[If you’re new to this series, you may wish to skip ahead to part-7, where I’ve done a partial ‘start-over’]

This is the second part in my short series of posts on building a sprite engine with Delphi. In this part we’re going to resolve goal number 4: Hierarchical scene composition.

Hierarchical Scene Composition

Hierarchical scene composition means that we build up the scene that we’re rendering from components which are arranged in a tree-like structure, a hierarchy. Objects in the tree are drawn in the order they appear as we recursively render the tree. Lets take a look at an example tree for further clarification. In this scene we’re going to render a bird flying over a city-scape…

In this scene, we start by rendering the “Scene Root” node, which doesn’t render anything it’s self, but will call on all of it’s children to render their content. In this case, the root node has only one child “Clear backdrop” which is a component that will erase all of the content of our scene, and paint a backdrop for our scene. Suppose the backdrop is a block of sky blue to represent some sky.

Next up, the “Clear Backdrop” component renders each of it’s children in order. Starting with “Clouds” which renders some sprites of clouds in the upper part of the screen, then rendering “Buildings” which puts the silhouette of some buildings along the bottom of the screen, and finishing with “Bird” which renders a bird in the sky.

This scene composition technique is used to ensure that components are rendered in the order they need to be, in order to build the scene up from back to front. Otherwise known as ‘painters algorithm’ as this is much the same way a painter constructs a painting, starting at the back and working forwards.

What the hierarchy also does for us, is it offers the opportunity for each component to alter the way it’s children are drawn. For example, if we made the ‘Clear Backdrop’ component in the above scene invisible, it could choose not to render it’s children, making them invisible also. Perhaps the position or rotational properties of the child components could be adjusted also.

In order to create this hierarchical structure, we’re going to start with three classes.

TSceneComponent – This represents any scene component which needs to be rendered. It will have the Render() method which will be called as the scene is being composed, and it’ll be available for owning other scene components, making the hierarchy possible. TSceneRoot – Derrived from the TSceneComponent, this class will represent the root node of the hierarchy, into which we’ll place the other components which make up the scene. TSpriteScene – This is a component which we’ll install into the IDE, which has a Root property (TSceneRoot). It therefore hosts the scene we’ll be rendering.

Before we get on with writing these three classes however, there is something else we’ll need. The TSceneComponent is responsible for rendering objects to our screen, and so it’ll need some connection to the context that we’re going to render onto.

Now, I’ve read ahead some and I know that we’ll need the ability to pass more information to the TSceneComponent than just the rendering context. Lets create a context class of our own, which will be responsible for passing all of the required information through our scene components. Create a new unit inside your sprite engine package and add this content…

unit unitViewportContext ; interface uses FMX . Types3D ; type TViewportContext = class private fContext3D : TContext3D ; public property Context3D : TContext3D read fContext3D write fContext3D ; end ; implementation end .

This unit provides a wrapper class ‘TViewportContext’ which at present has only one property ‘Context3D’ which is a TContext3D class. This is the very same TContext3D class which we rendered to in the previous article. We’ll see how this is used later, for now, lets create those scene hierarchy classes…

unit unitSpriteScene ; interface uses classes , unitViewportContext ; type {# TSceneComponent represents any component of the scene to be rendered. } TSceneComponent = class ( TComponent ) protected procedure Render ( Context : TViewportContext ) ; virtual ; public constructor Create ( aOwner : TSceneComponent ) ; reintroduce; virtual ; end ; {# Root node of the scene } TSceneRoot = class ( TSceneComponent ) ; {# TSpriteScene Represents the entire sprite scene to be rendered } TSpriteScene = class ( TComponent ) private fRoot : TSceneRoot ; public constructor Create ( aOwner : TComponent ) ; override ; destructor Destroy ; override ; procedure Render ( Context : TViewportContext ) ; public property Root : TSceneRoot read fRoot ; end ; procedure Register ; implementation procedure Register ; begin RegisterComponents ( 'SpriteEngine' , [ TSpriteScene ] ) ; end ; { TSpriteScene } constructor TSpriteScene . Create ( aOwner : TComponent ) ; begin inherited Create ( aOwner ) ; fRoot : = TSceneRoot . Create ( nil ) ; end ; destructor TSpriteScene . Destroy ; begin fRoot . Free ; inherited Destroy ; end ; procedure TSpriteScene . Render ( Context : TViewportContext ) ; begin fRoot . Render ( Context ) ; end ; { TSceneComponent } constructor TSceneComponent . Create ( aOwner : TSceneComponent ) ; begin inherited Create ( aOwner ) ; end ; procedure TSceneComponent . Render ( Context : TViewportContext ) ; var idx : int32 ; Ref : TComponent ; begin for idx : = 0 to pred ( ComponentCount ) do begin Ref : = Components [ idx ] ; if ( Ref is TSceneComponent ) then begin TSceneComponent ( Ref ) . Render ( Context ) ; end ; end ; end ; end .

A few things to notice about the unitSpriteScene unit.

The TSceneComponent reintroduces the standard TComponent constructor to ensure that a scene component may be owned by another TSceneComponent only.

The TSceneComponent.Render() method does nothing other than looping the child components seeking child TSceneComponent classes, and calling their Render() method.

This means that when we derive from TSceneComponent we can call the inherited Render() method to draw all children.

This means that when we derive from TSceneComponent we can call the inherited Render() method to draw all children. The Render() method accepts our TViewportContext class as a parameter. This means that in order to render our scene, the TViewport component that we developed in the previous post will need to create a TViewportContext and pass it into the Render() method of TSpriteScene.

The TSpriteScene component is the one which is registered and placed into the Tool Palette. This will allow us to create a scene which may be attached to the TViewport component by a property reference. We’ll see how this is important later.

Now that we have this hierarchical scene, we need to alter our TViewport to be able to render a scene. At the same time as making this change, I’m going to remove the temporary ‘RenderSomething()’ method from the viewport unit, as well as removing the texture material property. Lets see the new version of unitViewport.

unit unitViewport ; interface uses unitSpriteScene , // for TSpriteScene unitViewportContext , // for TViewportContext System . Classes , // for TComponent FMX . Forms3D ; // for TForm3D type TViewport = class ( TComponent ) private fForm : TForm3D ; fSpriteScene : TSpriteScene ; fContext : TViewportContext ; private procedure SetForm ( const Value : TForm3D ) ; public constructor Create ( aOwner : TComponent ) ; override ; destructor Destroy ; override ; procedure Render ; published property Form : TForm3D read fForm write SetForm ; property Scene : TSpriteScene read fSpriteScene write fSpriteScene ; end ; procedure Register ; implementation uses FMX . Types3D ; // for TVertexBuffer procedure Register ; begin RegisterComponents ( 'SpriteEngine' , [ TViewport ] ) ; end ; { TViewport } constructor TViewport . Create ( aOwner : TComponent ) ; begin inherited Create ( aOwner ) ; // Auto assign the form if it is TForm3D, calls SetForm if assigned ( aOwner ) and ( aOwner is TForm3D ) then begin Form : = TForm3D ( aOwner ) ; end ; fSpriteScene : = nil ; fContext : = TViewportContext . Create ; fContext . Context3D : = nil ; end ; destructor TViewport . Destroy ; begin Form : = nil ; // causes dispose of camera and dummy. fSpriteScene : = nil ; fContext . Free ; inherited Destroy ; end ; procedure TViewport . Render ; begin if assigned ( Form ) then begin // Render something as a test. if Form . Context . BeginScene then begin try Form . Context . SetContextState ( TContextState . cs2DScene ) ; if assigned ( Scene ) then begin fContext . Context3D : = Form . Context ; Scene . Render ( fContext ) ; end ; finally Form . Context . EndScene ; end ; end ; end ; end ; procedure TViewport . SetForm ( const Value : TForm3D ) ; begin if Value < > Form then begin fForm : = Value ; end ; end ; end .

In this version of the viewport unit, we’ve added the Scene property, which is a reference to a TSpriteScene. The idea here being, when you drop a TViewport onto your form, you also drop a TSpriteScene component and set the Scene property to point to it. In this way, you could drop several TSpriteScene’s onto your form and switch between them by altering the Scene property on the viewport.

We’ve also added a private member named fContext, which is the new TViewportContext that we created earlier. In the constructor we create this context and initialize it, and then dispose of it in the destructor.

The Render method for the viewport has been altered also. We no longer call ‘RenderSomething()’ but instead we ensure that the ‘Context3D’ property of fContext is correctly set, and then call the Render() method of the Scene, passing the context as a parameter.

If you were to drop a TViewport component and a TSpriteScene component onto your main form now, and wire them together. When you call Render, nothing will happen. Why? Well because your scene is empty!

In order to put something into the scene, we’ll need to create a new TSceneComponent descendant class to render something.

This post is already getting a little long, so we’ll look at creating our actual sprite class (first draft) in the next post. For right now, lets create a class to clear the backdrop of our scene to a specified color.

unit unitBackdrop ; interface uses classes , System . UITypes , // for TAlphaColor unitSpriteScene , // for TSceneComponent unitViewportContext ; // for TViewportContext type TBackdrop = class ( TSceneComponent ) private fColor : TAlphaColor ; protected procedure Render ( Context : TViewportContext ) ; override ; public constructor Create ( aOwner : TSceneComponent ) ; override ; published property Color : TAlphaColor read fColor write fColor ; end ; implementation { TBackdrop } constructor TBackdrop . Create ( aOwner : TSceneComponent ) ; begin inherited Create ( aOwner ) ; Color : = TAlphaColorRec . Blue ; end ; procedure TBackdrop . Render ( Context : TViewportContext ) ; begin Context . Context3D . Clear ( Color ) ; inherited Render ( Context ) ; end ; end .

There, we’ve created a scene component which has a color property. In the constructor we initialize the color property, and in the Render() method, we simply clear the context using the preset color. We then call the inherited Render() method to ensure any children will be rendered.

Having added all of the above units to your package, rebuild and re-install the package.

Lets build a simple scene…

Start a new Multidevice Delphi application (Firemonkey/FMX) Select a 3D application from the wizard. Drop a TViewport component onto your form. Drop a TSpriteScene component onto your form. Drop a TTimer component onto your form. Ensure Viewport1.Form is set to point at your main form. Set Viewport1.Scene to point at SpriteScene1 Set the timer interval to 100 In the OnTimer event add “Viewport1.Render;” On the forms OnCreate add “TBackdrop.Create(SpriteScene1.Root);”

*Note* You could add a custom component editor to the package for the TSpriteScene component, so that you can easily add TBackdrop as a child component in the designer. – That’s an exercise for the reader, I’m still trying to get us to a working sprite engine, far less one that is so convenient to use yet.

Now run the application, you should see the same thing I did….

In this post we set out to create a hierarchical scene composition system, which we have done. At this point however, our application isn’t really doing anything interesting.

In the next post we’ll see the spite engine finally begin to take shape when we add the sprite class. Until then…

Thanks for reading!