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 container
s. Nodes that can be named are called entity
s. 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 node
function.
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 enter
ed, it is also register
ed on the graph's root scene
. Similarly, when it is left, the node and any children it might have are first deregister
ed. When the name of an entity
is changed via setf
, the object is also first deregister
ed and then re-register
ed after the name change.
Each container
must be a sequence
and as such can be iterated over as one, using either for:for
or 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 finalize
d, all of its children are also finalized and the container is clear
ed, 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 scene
function.
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:
located-entity
An entity with a location
vec3.
sized-entity
An entity with a bsize
vec3 describing the half-size of the entity's bounding box.
oriented-entity
An entity that is pointing in the orientation
direction according to the up
vector.
rotated-entity
An entity that is rotated by the rotation
quaternion.
axis-rotated-entity
An entity that is rotated around the axis
by angle
.
pivoted-entity
An entity that is pivoted by pivot
.
scaled-entity
An entity that is scaled by scaling
.
transformed-entity
An entity with a transform
gizmo attached via tf
. It also supports convenience accessors for location
, scaling
, rotation
, axis
, angle
.
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 model-matrix
via apply-transforms
.
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.
array-container
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.
bag
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.
hash-table-container
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.
layered-container
A container that segregates objects into a multiple bag
s 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.
list-container
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 clear
, enter
, leave
, and the necessary sequence methods (most notably sequences:make-sequence-iterator
).