Close up shadows on the tree. Notice that leaves (modeled by alpha-test texture) also cast correct shadows.
Water reflections by optimized GeneratedCubeMapTexture
Mirrors by RenderedTexture, by Victor Amat

Scene graph (X3D)

Contents:

1. What is X3D

X3D (and it's older version, VRML) is a file format for 3D models. Various 3D modeling applications can export to it, for example Blender includes an X3D exporter (see also our Blender exporting notes -- although now we advise using more feature-rich glTF exporter).

To try it out, just create some X3D models (or download them from the Internet, or grab our demo models) and open them with our view3dscene.

As a 3D file format, X3D is quite unique, as

Note that our engine supports many other 3D and 2D file formats too, like glTF, Collada, Wavefront OBJ, Spine JSON... They are all loaded into a graph of X3D nodes. So X3D is our scene graph, but it's absolutely not the only file format that we support.

Learning X3D: Use this part of the documentation to learn about the available X3D nodes and their fields.

2. X3D in Pascal

When we say that "X3D is our scene graph", what it means in practice is that you have a graph of X3D nodes inside the TCastleSceneCore.RootNode property of any TCastleScene instance. The TCastleScene is our central class to render and animate 3D and 2D content, see the manual about TCastleScene.

This way you can edit (or even build from scratch) a graph of X3D nodes using Pascal, to create any content you like. You are not limited to only loading the content from X3D files (or Collada, or Wavefront OBJ...).

For example, consider an X3D node Box(Pascal API: TBoxNode). This node in X3D has fields size (the X3D type is SFVec3f) and solid (the X3D type is SFBool). In Pascal, this node corresponds to the class TBoxNode, with properties Size: TVector3 and Solid: Boolean. You can create and edit instances of TBoxNode and use TCastleScene to render them.

This section of the Castle Game Engine documentation describes all X3D nodes (often by referring you to the X3D specification for details), and it shows various examples how to create and edit X3D graph using Pascal. See for example:

You can also pre-process the loaded scene (e.g. change the texture), by changing the X3D nodes graph (see this forum post for details).

2.1. X3D node instance lifetime in Pascal

The X3D nodes have a reference-counting mechanism. The node (TX3DNode or any descendant) is freed when the reference count of it changes from non-zero to zero.

So in the usual case, if you set the node as a value of some propery of the other node, then you no longer need to think about releasing this node — it will be released along with the parent. For example:

var
  Geometry: TBoxNode;
  Shape: TShapeNode;
  Root: TX3DRootNode;
begin
  Geometry := TBoxNode.Create;
 
  Shape := TShapeNode.Create;
  Shape.Geometry := Geometry;
 
  { Now you no longer need to worry about freeing Geometry.
    It will be freed along with Shape.
    Note that there's a shortcut for above 3 lines:
 
    Geometry := TBoxNode.CreateWithShape(Shape);
  }
 
  Root := TX3DRootNode.Create;
  Root.AddChildren(Shape);
 
  { Now you no longer need to worry about freeing Shape.
    It will be freed along with Root. }
 
  // to be continued ...

Moreover, when you load nodes into TCastleScene using Scene.Load(MyNodes, true) — the 2nd parameter means that Scene takes ownership of the whole nodes tree. So the whole TX3DRootNode instance will be freed along with Scene.

So we can continue the example above:

  Scene := TCastleScene.Create(Application);
  Scene.Load(Root, true);
 
  { Now you no longer need to worry about freeing Root.
    It will be freed along with Scene. }

And Scene is a regular Pascal TComponent, with usual component ownership. In the above example, it is owned by the Application singleton. In larger application, when using views, it will often be owned by TCastleView.FreeAtStop. And you can always free the scene explicitly too, like FreeAndNil(Scene);.

Notes:

  • If a node is not referenced from anywhere, you are responsible for freeing it manually as usual. For example, if we would not load Root into Scene (if we remove the line Scene.Load(Root, true) from above example) then at some point you should call FreeAndNil(Root);.

  • If for some reason you do want the node to not be automatically freed, you can use TX3DNode.KeepExistingBegin and TX3DNode.KeepExistingEnd.