Transform, Components and MeshRenderer (Metal Part 6)

z4gon
z4gon

Implementing the Transform objects which will form a tree structure to build the Scene Graph. Adding Components to the Game Object, so that different functionalities and behaviors can be composed inside a Game Object as a list of children components. Finally implementing a MeshRenderer component which will be in charge of rendering the 3D mesh associated with the Game Object.

Cover Image for Transform, Components and MeshRenderer (Metal Part 6)

Source Code

See Project in GitHub 👩‍💻

References


Table of Content


Transform

Picture

The Transform class will have information about position, rotation and scale.

It will also have a list of children Transform objects, to makeup the Scene Graph in the Scene.

Everytime render() and update() are invoked on a Transform, it will iterate the children elements and invoke render() and update() on them.

Then it will check if itself implements the Renderable or Updatable protocols, if yes, then it will proceed to invoke doRender() and doUpdate() on itself.

1class Transform { 2 public var children: [Transform]! = [] 3 4 public func addChildren(transform: Transform){ 5 children.append(transform) 6 } 7 8 public func update(deltaTime: Float){ 9 for child in children { 10 child.update(deltaTime: deltaTime) 11 } 12 13 if let updatableSelf = self as? Updatable { 14 updatableSelf.doUpdate(deltaTime: deltaTime) 15 } 16 } 17 18 public func render(renderCommandEncoder: MTLRenderCommandEncoder){ 19 for child in children { 20 child.render(renderCommandEncoder: renderCommandEncoder) 21 } 22 23 if let renderableSelf = self as? Renderable { 24 renderableSelf.doRender(renderCommandEncoder: renderCommandEncoder) 25 } 26 } 27}

Component

GameObject will have a list of components that may or may not be Renderable or Updatable.

On doRender() and doUpdate(), the GameObject will iterate its components and invoke doRender() and doUpdate() accordingly.

1class GameObject : Transform { 2 3 public var components: [Component]! = [] 4 5 public func addComponent(component: Component){ 6 components.append(component) 7 } 8} 9 10extension GameObject : Updatable { 11 public func doUpdate(deltaTime: Float){ 12 for component in components { 13 if let updatableComponent = component as? Updatable { 14 updatableComponent.doUpdate(deltaTime: deltaTime) 15 } 16 } 17 } 18} 19 20extension GameObject : Renderable { 21 public func doRender(renderCommandEncoder: MTLRenderCommandEncoder){ 22 for component in components { 23 if let renderableComponent = component as? Renderable { 24 renderableComponent.doRender(renderCommandEncoder: renderCommandEncoder) 25 } 26 } 27 } 28}

MeshRenderer

The MeshRenderer component will be in charge of drawing the mesh using the Metal graphics API elements, like the render command encoder.

It will have a reference to a Mesh class.

1class MeshRenderer : Component, Renderable { 2 private var _vertexBuffer: MTLBuffer! 3 private var _mesh: Mesh! 4 5 init(mesh: Mesh) { 6 _mesh = mesh 7 _vertexBuffer = Engine.device.makeBuffer(bytes: _mesh.vertices, length: Vertex.stride * _mesh.vertices.count, options: []) 8 } 9 10 func doRender(renderCommandEncoder: MTLRenderCommandEncoder) { 11 renderCommandEncoder.setRenderPipelineState(RenderPipelineStateCache.getPipelineState(.Basic)) 12 renderCommandEncoder.setVertexBuffer(_vertexBuffer, offset: 0, index: 0) 13 renderCommandEncoder.drawPrimitives(type: MTLPrimitiveType.triangle, vertexStart: 0, vertexCount: _mesh.vertices.count) 14 } 15}

The Mesh class will have the array of vertices that will later be used by the MeshRenderer to create the Vertex Buffer off of.

1class Mesh { 2 public var vertices: [Vertex]! 3 4 init(){ 5 createVertices() 6 } 7 8 func createVertices() {} 9} 10 11class TriangleMesh : Mesh{ 12 override func createVertices() { 13 vertices = [ 14 Vertex(position: float3( 0, 1,0), color: float4(1,0,0,1)), 15 Vertex(position: float3(-1,-1,0), color: float4(0,1,0,1)), 16 Vertex(position: float3( 1,-1,0), color: float4(0,0,1,1)) 17 ] 18 } 19}