xyz
is a 3D graphics framework written in Go. It is a separate standalone package that renders to an offscreen Vulkan framebuffer, which can easily be converted into a Go image.RGBA
. The xyzcore package provides an integration of xyz in Cogent Core, for dynamic and efficient 3D rendering within 2D GUI windows.
xyz
is built on the gpu WebGPU framework (specifically the phong rendering system, and uses the tree tree structure for the scenegraph. It currently supports standard Phong-based rendering with different types of lights and materials. It is designed for scientific and other non-game 3D applications, and lacks almost all of the advanced features one would expect in a modern 3D graphics framework. Thus, its primary advantage is its simplicity and support for directly programming 3D visualizations in Go, its broad cross-platform support across all major desktop and mobile platforms, and the use of WebGPU which is highly efficient.
- The physics sub-package provides a physics engine for simulating 3D virtual worlds, using xyz.
-
You must call
SetMesh
orSetTexture
to update or add a new Mesh or Texture element. Just changing an existing mesh by itself does not trigger the proper updates. -
The
xyz.Scene.Update()
method updates the scenegraph object matricies and triggers re-render..Render()
orNeedsRender()
just drives a new render if only the camera parameters have changed, but this will not reflect changes in object poses. -
xyzcore.Scene.UpdateWidget()
is the cleanest way to trigger theUpdate
on the underlying xyx.Scene and also a re-render of thecore
widget viewing that scene. If using anxyzcore.SceneEditor
then useEditor.SceneWidget().UpdateWidget()
.
The 3D scenegraph is rooted at a xyz.Scene
node, which contains all of the shared Mesh
and Texture
elements, along with Lights
, Camera
and a Library
of loaded 3D objects that can be referenced by name.
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 in xyzcore
), changes take effect immediately, but everything can be configured prior to that, and they will all be applied when the GPU is activated.
Children of the Scene are Node
nodes, with Group
and Solid
as the main subtypes. NodeBase
is the base implementation, which has a Pose
for the full matrix transform of relative position, scale, rotation, and bounding boxes at multiple levels.
-
Group
is a container -- most discrete objects should be organized into a Group, with Groups of Solids underneath. For maximum efficiency it is important to organize large scenegraphs into hierarchical groups by location, so that regions can be pruned for rendering. The Pose on the Group is inherited by everything under it, so things can be transformed at different levels as well. -
Solid
has aMaterial
to define the color / texture of the solid, and the name of aMesh
that defines the shape. Thus, the scenegraph elements are very lightweight and basically point (by name) to shared larger resources on the Scene, where they can be efficiently shared among any number of Solid elements.
Objects that have uniform Material color properties on all surfaces can be a single Solid, but if you need e.g., different textures for each side of a box then that must be represented as a Group of Solids using Plane Meshes, each of which can then bind to a different Texture via their Material settings.
Node bounding boxes are in both local and World reference frames, and are used for visibility (render pruning) and event selection. Very large scenegraphs can be efficiently rendered by organizing elements into larger and larger Group collections: pruning automatically happens at the highest level where everything below is invisible. This same principle is used in eve
for efficient collision detection.
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).
Texture
s are loaded from Go image files, stored by unique names on the Scene, and the Node-specific Material can optionally refer to a texture -- likewise allowing efficient re-use across different Solids.
The Scene also contains a Library
of uniquely named "objects" (Groups) which can be loaded from 3D object files, and then added into the scenegraph as needed. Thus, a typical, efficient workflow is to initialize a Library of such objects, and then configure the specific scene from these objects. 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.
The Scene also holds the Camera and Lights for rendering -- there is no point in putting these out in the scenegraph -- if you want to add a Solid representing one of these elements, you can easily do so.
The Scene is fully in charge of the rendering process by iterating over the scene elements and culling out-of-view elements, ordering opaque then transparent elements, etc.
There are standard Render types that manage the relevant GPU programs / Pipelines to do the actual rendering, depending on Material and Mesh properties (e.g., uniform vs per-vertex color vs. texture).
-
Scene
is the root node of the 3D scenegraph.-
Camera
is a field on the Scene that has all the current camera settings. By default the camera does a naturalistic Perspective projection, but you can enable Orthographic by ticking theOrtho
button -- you will generally need to reduce the Far plane value to be able to see anything -- the Ortho projection shows you the entire scene within those two planes, and it scales accordingly to be able to fit everything. -
Lights
contain the lighting parameters for the scene -- if you don't have any lights, everything will be dark!Ambient
lights contribute generic lighting to every surface uniformly -- usually have this at a low level to represent scattered light that has bounced around everywhere.Directional
lights represent a distant light-source like the sun, with effectively parallel rays -- the position of the light determines its direction by pointing back from there to the origin -- think of it as the location of the sun. Only the normal direction value is used so the magnitude of the values doesn't matter.Point
lights have a specific position and radiate light uniformly in all directions from that point, with both a linear and quadratic decay term.Spot
lights are the most sophisticated lights, with both a position and direction, and an angular cutoff so light only spreads out in a cone, with appropriate decay factors.
-
Meshes
are the library ofMesh
shapes that can be used in the scene. These provide the triangle-based surfaces used to define shapes. Theshape.go
code provides the basic geometric primitives such asBox
,Sphere
,Cylinder
, etc, and you can load mesh shapes from standard.obj
files as exported by almost all 3D rendering programs. You can also write code to generate your own custom / dynamic shapes, as we do with theNetView
in the emergent neural network simulation system. -
Textures
are the library ofTexture
files that define more complex colored surfaces for objects. These can be loaded from standard image files. -
Solid
s are the Children of the Scene, and actually determine the content of the 3D scene. Each Solid has aMesh
field with the name of the mesh that defines its shape, and aMat
field that determines its material properties (Color, Texture, etc). In addition, each Solid has its ownPose
field that determines its position, scale and orientation within the scene. Because eachSolid
is atree.Node
tree node, it can contain other scene elements as its Children -- they will inherit thePose
settings of the parent (and so-on up the tree -- all poses are cumulative) but not automatically any material settings. You can callCopyMatToChildren
if you want to apply the current materials to the children nodes. And use Style parameters to set these according to node name or Class name. -
Group
s can be used to applyPose
settings to a set of Children that are all grouped together (e.g., a multi-part complex object can be moved together etc by putting a set ofSolid
s into a Group)
-
Mouse events are handled by the standard Cogent Core Window event dispatching methods, based on bounding boxes which are always updated -- this greatly simplifies gui interactions. There is default support for selection and Pose
manipulation handling -- see manip.go
code and NodeBase
's ConnectEvents3D
which responds to mouse clicks.
A full 2D Cogent Core GUI can be embedded within a 3D scene using the xyzcore.Embed2D
Node type, which renders a core.Scene
onto a Texture projected onto a Plane. It captures events within its own bounding box, and translates them into coordinates for the 2D embedded gui. This allows full 2D interactive control within whatever perspective is present in the 3D scene. However, things like cursors and popups render in the flat 2D screen and are only approximately located.
In addition to interactive guis, the embedded 2D node can be used for rendering full SVG graphics to a texture.