Saturday, May 1, 2010

MatrixNode Awesomeness!

There were several things I was hoping to get accomplished today that didn't get done because I got sidetracked on another task that ended up taking significantly longer than I had anticipated. My initial implementation of Node / Component support for Matrix4 was spotty and I wasn't very happy with it, so I took the time today to refactor it and make it into a much more powerful and simplified architecture.

For those of you that have actually used the scene support in Sgine will be aware, the scenegraph architecture is extremely abstract and is designed to avoid implications towards a specific use and allow multiple uses within the same scenegraph. For example, a NodeContainer could possibly contain physics information, sound/music information, visual rendering information, etc. while each part being completely separate and independent from the other. The level at which I'm able to accomplish this is almost entirely due to the awesomeness that is Scala, but to that end abstractions often make it difficult to architect advanced uses without making system extremely complicated. For example, if I want to have my root NodeContainer define matrix information to translate 'z' to -200.0, meaning that all children will receive that it can be problematic to support if nodes between don't also provide matrix support. However, that is the difficulty I solved today. :)

I've created a simple trait "MatrixNode" that contains a "localMatrix" and a "worldMatrix". The "localMatrix" can be manipulated to represent the local translation, rotation, scale, etc. and upon change will invalidate the "worldMatrix" causing it to get updated to the parent's "worldMatrix" (walking up the scenegraph as necessary) multiplied against the "localMatrix".

See the following example:
package org.sgine.ui

import org.sgine.core.Color
import org.sgine.core.Resource

import org.sgine.easing.Linear

import org.sgine.render.Renderer
import org.sgine.render.scene.RenderableScene

import org.sgine.scene.GeneralNodeContainer
import org.sgine.scene.MatrixNode

object TestScene {
 def main(args: Array[String]): Unit = {
  val r = Renderer.createFrame(1024, 768, "Test RenderScene")
  
  val scene = new GeneralNodeContainer()
  
  val container1 = new GeneralNodeContainer() with MatrixNode
  container1.localMatrix().translate(0.0, 0.0, -200.0)
  scene += container1
  
  val container2 = new GeneralNodeContainer() with MatrixNode
  container2.localMatrix().translate(0.0, 0.0, -500.0)
  container1 += container2
  
  val container3 = new GeneralNodeContainer()
  container2 += container3
  
  val component = new Image()
  component.source := Resource("puppies.jpg")
  component.color := Color(1.0, 1.0, 1.0, 0.5)
  component.location.z := 100.0
  container3 += component
  
  r.renderable := RenderableScene(scene)
 }
}

This is a pretty simple example. As you can see there are four containers (scene, container1, container2, and container3) and two of them mix-in "MatrixNode" in order to specify a translation (container1 and container2). Finally, there is a Image "component" that defines its own location.z. As a result "component" will render at a "z" translation of -600.0 (-200.0 + -500.0 + 100.0). Notice that although "container3" is between "container2" and "component" it is still applied. This allows extremely powerful control of the contents of the scenegraph while maintaining a very simple abstract feel.

Hopefully in the coming weeks you'll see much more functionality like this making its way into the system. In fact, I believe I'm going to have to write something along these lines for Color in order to properly mix colors hierarchically for OpenGL.

No comments:

Post a Comment

Scala Engine for high-performance interactive applications.