0.1 – Early Steps

by Maniac on July 28, 2012

When I started developing Heka, I only had a rough idea of how it would look and work. I read several articles on component design and knew the Torque Engine quite well, but by and large, I was flying blind.

This was not so much because I didn’t know what I’m doing, but more so because I wanted to be as flexible and generic in my approach as possible. Most component designs use some sort of Game Object as a container and uses the components like plugins for that object, but since I didn’t yet know why that made so much sense, I built all Components to be equal and potential root nodes for the socalled “blobs”, collections of components.

To fully utilize scripting support, it was clear that the new component class hierarchy would not exist parallel to the vanilla class hierarchy, but completely replace it. Hence, the base component class was based on the SimGroup-Class. This class derives from SimObject, the base class to be accessible from and exposed to the scripting module, and featured the necessary functionality to add other SimObjects and deriving classes to it. This way, all components could be added to all other components, independent of their actual type.

The component base class, GameComponent, also implemented the necessary init/deinit methods, which first ran parellel to the vanilla onAdd/onRemove methods, later replacing them almost completely. These methods allow components to invoke necessary steps prior and after to picking up their actual purpose. E.g. the ShapeComponent, for the most part a replacement for the shape based functionality of the former ShapeBase-Class, has to actually load the necessary model data before it can be used and accordingly has to clean up after it was detached from a component blob.

Since in vanilla Torque the SceneObject class takes care of several distinct features deciding how and where to insert what type of components in it’s place was challenging. SceneObject took care of transform, scale, and volume, but also acted as the base class to be rendered, lighted, mounted, tracked and acted as an interface for gathering geometric collision data from an object. Building a component around spatial attributes made perfect sense, so the SpatialComponent was born. A simple refactoring from SceneObject to SpatialComponent obviously wasn’t the solution, so every occurance of SceneObject in the source had to be tracked down, examined and either replaced by the SpatialComponent or a corresponding search through the SpatialComponent to find a component type that could handle that specific task. This was implemented pretty straight forward at first. The SpatialComponent replaced the SceneObject everywhere where only the spatial attributes where affected, like building the SceneTree of positionable and potentially renderable objects. So the SceneTree would organize and track changes in the position of SpatialComponents and when it was time to render, iterate over them and ask them whether or not they wanted to be rendered. Whether or not something renders was depended on whethor or not a RenderComponent was reachable via the SpatialComponent, which meant that either the parent of or the SpatialComponent itself had a child that was a RenderComponent. A later article will go more in depth on this.

The SpatialComponent did not copy the mounting functionality of the SceneObject, but implement a different feature, which, combined with the PhysicsComponent, which will be covered in a later article, completely replaced the vanilla Torque mounting system. This feature was the ability of stacked SpatialComponents to assume the parents transform as it’s own relative position in space. So, if you positioned a SpatialComponent with no rotation in the game world at (x = 2, y = -3, z = 1) and added another one without rotation at the position (0, 2, 0), then the spatial child would have an effective position of (2, -1, 1). If you now moved the root spatial to (-5, 7, 5), the new position of the child spatial would be (-5, 9, 5). This of course worked for rotated transforms and rotating the root spatial, too, but the numbers wouldn’t be so obvious. The child spatial will, if not modified directly, always stay in it’s respective spatial relation to its parent.

This way, non-root SpatialComponents acted as socalled “subspaces” in an object, effectively acting like skeletal nodes. Such subspaces enable the definition of rotated or non rotated offsets, like specific nodes in a model, but without the need of a model to supply them. This is particularly useful for the placement, e.g. of a camera, lights, particle effects, simple hitboxes and mounted structures and objects, like the wheels, motor and doors of a vehicle.

This feature alone makes for a mighty tool in the hands of a developer, since it enables designers to define and manipulate spatial features and their relations via drag and drop.

As for the tracking of objects, the SpatialComponent simply replaced the SceneObject, since I couldn’t think of a more suitable type of component to fulfill this duty, just like the LightingComponent took care of all lighting duties. The LightingComponent will be, among others, be covered in the next article.

The handling of geometric collision data, of course, was not suited for the SpatialComponent, since it effectively only was the definition of space. Hence, the CollisionComponent and it’s various subtypes (terrain, static/dynamic water, shape, etc) were implemented. The collision geometry handling was not left in the specific components (TerrainComponent, ShapeComponent) to both keep the code in each component as focused and short as possible and to be able to only use the collision geometry – not to be confused with the physics collision handling – if actually necessary. Since rendering is not affected by a missing CollisionComponent, you can keep your blob smaller by excluding CollisionComponents for objects that don’t need to be hit by a raycast and are not connected to other components that rely on the collision geometry.

With those basic problems solved, the actual port of features to their respective components could commence.

{ 1 comment… read it below or add one }

Sonix October 8, 2012 at 6:38 am

Good read! 🙂

Leave a Comment