Like many engines, Trial includes a "scene graph" – a tree of nodes in which the objects that are drawn or are interactable are organised. Each node is a
scene-node with a
container pointer to its parent node. Nodes that can contain other child nodes are
containers. Nodes that can be named are called
entitys. If an
entity is named, it is uniquely identified by its name within the
scene it is contained in. An entity can be obtained via its name with the
Operating on the graph is rather simple:
enter is used to introduce a node into a container, and
leave is used to remove it again. When a node is
entered, it is also
registered on the graph's root
scene. Similarly, when it is left, the node and any children it might have are first
deregistered. When the name of an
entity is changed via
setf, the object is also first
deregistered and then re-
registered after the name change.
container must be a
sequence and as such can be iterated over as one, using either
sequences:dosequence. All other standard sequence operations are naturally required to be supported as well.
You can also
clear a container fully, though note that this will not recursively clear if the container contained other containers. When a container is
finalized, all of its children are also finalized and the container is
cleared, though as with all finalization you should not rely on any particular state after finalization.
Finally, you can check whether a node is within a container with
contained-p, and retrieve the container it is in (if any) with the
container function. To retrieve the root scene, simply use the
By themselves nodes don't actually do anything. Often you'd like the nodes to carry some properties such as a location, orientation, etc. To this end, Trial offers a number of mixin classes:
An entity with a
An entity with a
bsize vec3 describing the half-size of the entity's bounding box.
An entity that is pointing in the
orientation direction according to the
An entity that is rotated by the
An entity that is rotated around the
An entity that is pivoted by
An entity that is scaled by
An entity with a
transform gizmo attached via
tf. It also supports convenience accessors for
This is what all nodes have in a typical game engine. The transform gizmo is a convenient way to encapsulate separated affine transformations.
Note that if you use multiple of these mixins that the order in which you specify them in the superclass list matters, as it determines the order in which their transformations apply to the
While you can create your own container types when necessary, Trial already provides a number of containers that should fit almost every need out there.
A container backed by a simple-vector. Insertion is O(1) and simply adds to the back, and removal is O(n). Iteration should be fast and memory-efficient.
A container where the order of elements is not guaranteed to be consistent. However, in return, insertion and removal are both O(1), and iteration is also fast and memory-efficient. This should be the default container type unless you expect a very small number of elements.
A container backed by a simple hash table. Iteration order is not guaranteed, but insertion and removal are both O(1). Additionally, setting elements via
elt does not "make sense" as there's no guarantee about the index of new elements.
A container that segregates objects into a multiple
bags depending on their
layer-index. The container needs to be initialised with the correct
layer-count. Any object with a layer-index beyond [0,layer-count] is clamped to within that range.
A container backed by a list. Insertion is O(1) and simple adds to the front, and removal is O(n). Iteration is not memory efficient due to the cons chain.
Defining your own container types is simple – all you need to do is subclass
container, and implement
leave, and the necessary sequence methods (most notably