|
1 | 1 | +++ |
2 | 2 | Name = "XYZ" |
3 | | -Categories = ["Widgets"] |
| 3 | +Categories = ["Architecture"] |
4 | 4 | +++ |
5 | 5 |
|
6 | | -**XYZ** is a [[widget]] for interactive 3D viewing and editing. 3D support is currently present on all platforms (with the exception of web browsers that do not support [WebGPU](https://caniuse.com/webgpu)), but the API is subject to change. More documentation will be written for xyz once the API is cleaned up. For now, you can see the API documentation for [[doc:xyz]], and the [xyz example](https://github.com/cogentcore/core/tree/main/examples/xyz). |
| 6 | +**xyz** is a package that supports interactive 3D viewing and editing, building on the [[GPU]] framework, which in turn is based on [WebGPU](https://www.w3.org/TR/webgpu/), which is available on all non-web platforms and is gaining wider browser support: [WebGPU](https://caniuse.com/webgpu). The full API documentation is at [[doc:xyz]], and the [xyz example](https://github.com/cogentcore/core/tree/main/examples/xyz) provides a good working example to start from. |
7 | 7 |
|
8 | 8 | Here is a screenshot of the xyz example: |
9 | 9 |
|
10 | 10 |  |
| 11 | + |
| 12 | +## Elements |
| 13 | + |
| 14 | +### Solid |
| 15 | + |
| 16 | +The [[doc:xyz.Solid]] type is the main 3D object that renders a visible shape. Its shape comes from a [[doc:xyz.Mesh]], which is a potentially shared element stored in a library and accessed by unique names. The other visible properties (e.g., `Color`, how `Shiny` or `Reflective` it is, etc) are defined by the [[doc:xyz.Material]] properties. It can also have a [[doc:xyz.Texture]] (also referenced by unique name from a shared library) that is an image providing a richer more realistic appearance. |
| 17 | + |
| 18 | +The [[doc:xyz.Pose]] values on a Solid (and all other `xyz` nodes) specify the 3D position and orientation of the object. |
| 19 | + |
| 20 | +### Scene |
| 21 | + |
| 22 | +The 3D scenegraph and the shared `Mesh` and `Texture` libraries are managed by the [[doc:xyz.Scene]] node, along with the `Lights`, `Camera` and a `Library` of loaded 3D objects that can be referenced by name. The scene of objects is rooted in the `Children` of the Scene. |
| 23 | + |
| 24 | +`Mesh` and `Texture` elements use the "Set / Reset" approach, where `Set` does add or update, based on unique name id, and if there are large changes and unused elements, a `Reset` can be used to start over. After the GPU is up and running (e.g., after the main app window is opened), changes take effect immediately, but everything can be configured prior to that, and they will all be applied when the GPU is activated. |
| 25 | + |
| 26 | +### Group |
| 27 | + |
| 28 | +The [[doc:xyz.Group]] is a container of other elements, and typically visible objects are actually Groups with multiple different subgroups and finally Solid elements. Groups are also used for optimizing the rendering, such that any Group that is fully out of view will be skipped over entirely: therefore, it is a good idea to create spatially-organized groups at different scales, depending on the overall scene size and complexity. |
| 29 | + |
| 30 | +### Resources |
| 31 | + |
| 32 | +Meshes are _exclusively_ defined by indexed triangles, and there are standard shapes such as `Box`, `Sphere`, `Cylinder`, `Capsule`, and `Lines` (rendered as thin boxes with end points specified), e.g.,: |
| 33 | + |
| 34 | +```go |
| 35 | + sphere := xyz.NewSphere(sc, "sphere", .75, 32) |
| 36 | + tree.AddChild(sc, func(n *xyz.Solid) { |
| 37 | + n.SetMesh(sphere).SetColor(colors.Orange).SetPos(0, -2, 0) |
| 38 | + }) |
| 39 | +``` |
| 40 | + |
| 41 | +`Texture`s are loaded from Go image files, e.g.,: |
| 42 | + |
| 43 | +```go |
| 44 | + grtx := xyz.NewTextureFileFS(assets.Content, sc, "ground", "ground.png") |
| 45 | + floorp := xyz.NewPlane(sc, "floor-plane", 100, 100) |
| 46 | + tree.AddChild(sc, func(n *xyz.Solid) { |
| 47 | + n.SetMesh(floorp).SetTexture(grtx).SetPos(0, -5, 0) |
| 48 | + n.Material.Tiling.Repeat.Set(40, 40) |
| 49 | + }) |
| 50 | +``` |
| 51 | + |
| 52 | +The Scene also contains a `Library` of uniquely named "objects" (Groups) which can be loaded from 3D object files in the [Wavefront .obj format](https://en.wikipedia.org/wiki/Wavefront_.obj_file), and then added into the scenegraph as needed, e.g.: |
| 53 | + |
| 54 | +```go |
| 55 | + lgo := errors.Log1(sc.OpenToLibraryFS(assets.Content, "gopher.obj", "")) |
| 56 | + tree.Add(p, func(n *xyz.Object) { |
| 57 | + n.SetObjectName("gopher").SetScale(.5, .5, .5).SetPos(1.4, -2.5, 0) |
| 58 | + n.SetAxisRotation(0, 1, 0, -60) |
| 59 | + }) |
| 60 | +``` |
| 61 | + |
| 62 | +The library objects are Cloned into the scenegraph so they can be independently configured and manipulated there. Because the Group and Solid nodes are lightweight, this is all very efficient. |
| 63 | + |
| 64 | +## Lights |
| 65 | + |
| 66 | +At least one light must be added to make everything in the scene visible. Four different types are supported. |
| 67 | + |
| 68 | +The lights use [standard light types](http://planetpixelemporium.com/tutorialpages/light.html) via the [[doc:xyz.LightColors]] to specify the light color, e.g., `xyz.DirectSun` is the brightest pure white color. |
| 69 | + |
| 70 | +The [[doc:xyz.Ambient]] light is the simplest, providing a diffuse uniform light that doesn't come from any given direction: |
| 71 | + |
| 72 | +```go |
| 73 | + xyz.NewAmbient(sc, "ambient", 0.3, xyz.DirectSun) |
| 74 | +``` |
| 75 | + |
| 76 | +The [[doc:xyz.Directional]] light shines toward the origin (position 0,0,0) from wherever it is placed, and only the vector from this position to the origin matters: distance is not a factor: |
| 77 | + |
| 78 | +```go |
| 79 | + xyz.NewDirectional(sc, "directional", 1, xyz.DirectSun).Pos.Set(0, 2, 1) |
| 80 | +``` |
| 81 | + |
| 82 | +The [[doc:xyz.Point]] light is like `Directional` except that it has decay factors as a function of distance: |
| 83 | + |
| 84 | +```go |
| 85 | + xyz.NewPoint(sc, "point", 1, xyz.DirectSun).Pos.Set(-5, 0, 2) |
| 86 | +``` |
| 87 | + |
| 88 | +The [[doc:xyz.Spot]] light is the most complex, with an angular decay and a angular cutoff, light a classic desk lamp. |
| 89 | + |
| 90 | +```go |
| 91 | + xyz.NewSpot(sc, "spot", 1, xyz.DirectSun).Pos.Set(-5, 0, 2) |
| 92 | +``` |
| 93 | + |
| 94 | +If you want a visible representation of the light in the Scene, you can add that using whatever Solid you want. |
| 95 | + |
| 96 | +## Camera |
| 97 | + |
| 98 | +The [[doc:xyz.Camera]] determines what view into the 3D scene is rendered, via its Pose parameters, e.g.,: |
| 99 | + |
| 100 | +```go |
| 101 | + sc.Camera.Pose.Pos.Set(0, 2, 10) |
| 102 | + sc.Camera.LookAt(math32.Vector3{}, math32.Vec3(0, 1, 0)) // look at origin |
| 103 | +``` |
| 104 | + |
| 105 | +## xyzcore |
| 106 | + |
| 107 | +The [[doc:xyz/xyzcore]] package provides two `core.Widget` wrappers around the `xyz.Scene`: |
| 108 | + |
| 109 | +* [[doc:xyz/xyzcore.SceneEditor]] provides full object selection and manipulation functionality, and a toolbar of controls. |
| 110 | + |
| 111 | +* [[doc:xyz/xyzcore.Scene]] just displays the scene, and supports mouse-based zooming and panning of the camera. |
| 112 | + |
| 113 | +### Events |
| 114 | + |
| 115 | +The GUI interactions are managed by functions such as [[doc:xyz.Scene.MouseScrollEvent]], [[doc:xyz.Scene.SlideMoveEvent]], and [[doc:xyz.Scene.NavKeyEvent]], which are connected to the core event management system in `xyzcore.Scene`. |
| 116 | + |
| 117 | +There are also other helper functions in `xyz/events.go`. |
| 118 | + |
| 119 | +## Updating, Making, Rendering, etc |
| 120 | + |
| 121 | +xyz is based on the [[doc:tree]] infrastructure, documented in [[Plan]], for flexible ways of building and updating 3D scenes. |
| 122 | + |
| 123 | +The `Update()` method on `Scene` (or any node) will update everything based on Maker and Updater functions that have been installed. |
| 124 | + |
| 125 | +The `Render()` method on the Scene will render to a [[doc:gpu.RenderTexture]] that is an offscreen rendering target. Use `RenderGrabImage()` to get the resulting image as a Go image. The `xyzcore.Scene` automatically manages the updating and rendering consistent with the [[core]] standard mechanisms, using an optimized direct rendering logic so the resulting rendered image stays on the GPU and is used directly. |
| 126 | + |
| 127 | +## Lines, arrows, etc |
| 128 | + |
| 129 | +There are handy functions in `xyz/lines.go` for creating complex line-based shapes, including those with arrow heads. These include `Mesh` functions for making the meshes, and `Init` functions that initialize a `Group` or `Solid` with line shapes. |
| 130 | + |
| 131 | +To make a set of lines: |
| 132 | + |
| 133 | +```go |
| 134 | + lines := NewLines(sc, "Lines", []math32.Vector3{{-3, -1, 0}, {-2, 1, 0}, {2, 1, 0}, {3, -1, 0}}, math32.Vec2(.2, .1), CloseLines) |
| 135 | + tree.AddChild(sc, func(n *Solid) { |
| 136 | + n.SetMesh(lines).SetColor(color.RGBA{255, 255, 0, 128}).SetPos(0, 0, 1) |
| 137 | + }) |
| 138 | +``` |
| 139 | + |
| 140 | +To make a line with arrow heads: |
| 141 | + |
| 142 | +```go |
| 143 | + tree.AddChild(sc, func(g *xyz.Group) { |
| 144 | + InitArrow(g, math32.Vec3(-1.5, -.5, .5), math32.Vec3(2, 1, 1), .05, colors.Cyan, StartArrow, EndArrow, 4, .5, 8) |
| 145 | + }) |
| 146 | +``` |
| 147 | + |
0 commit comments