Saturday, May 22, 2010

ResolutionNode

I'm back from a week long cruise to the Caribbean without cell phones, computers, or internet access. It was a great break from technology, but now I'm back, refreshed, and ready to crank out more functionality into Sgine.

What I have today is called ResolutionNode. If you look back at all the blogged examples below you'll notice all the 'z' translation stuff on the components. The reason for that is because Sgine (and OpenGL generally) is configured to have 'z' at 0.0 have a distance of -1.0 to 1.0 both vertically and horizontally. Further, anything between 0.0 and -1.0 (negative to back further into the screen) is clipped for mathematical precision reasons. So by pushing the the components to something like -1000.0 for 'z' causes it to appear much smaller because its depth is so far back into the projection.

This is fine for games and general 3D applications, but can be a pain in the neck when you want to have a normal graphics-based UI and you're expecting a specific resolution (e.g. 1024x768) to design graphics and layout for. To this end I have created a trait called ResolutionNode. It gives the ability to specify a resolution (e.g. 1024x768) and modifies the world matrix for that Node to properly display that resolution to fit to the screen. Not only does this give you the benefit of managing the resolution for the screen, but it also keeps you from having to worry about the *actual* resolution as this will scale-to-fit so even if you are running 1920x1200 resolution if you specify 1024x768 internally you'll end up with everything being stretched to fit.

This is actually part of a larger UI design concept I've been developing that I'll go into more on as the functionality is finalized in Sgine, but for now I give you my example.

The following code takes a 1024x768 image and a 640x480 image and displays them overlayed on top of each other. The cool thing here though is that you can see that using a ResolutionNode container for each I am able to see both of them at the exact same size.

package org.sgine.ui

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

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

import org.sgine.scene.GeneralNodeContainer
import org.sgine.scene.ext.ResolutionNode

object TestResolution {
 def main(args: Array[String]): Unit = {
  val r = Renderer.createFrame(1024, 768, "Test Resolution")
  r.verticalSync := false
  
  val scene = new GeneralNodeContainer()
  
  val r1024 = new GeneralNodeContainer() with ResolutionNode
  r1024.setResolution(1024.0, 768.0)
  scene += r1024
  
  val c1024 = new Image()
  c1024.source := Resource("1024.jpg")
  c1024.alpha := 0.5
  r1024 += c1024
  
  val r640 = new GeneralNodeContainer() with ResolutionNode
  r640.setResolution(640.0, 480.0)
  scene += r640
  
  val c640 = new Image()
  c640.source := Resource("640.jpg")
  c640.alpha := 0.5
  r640 += c640
  
  r.renderable := RenderableScene(scene)
 }
}

The two images I'm using are:



and:



The resulting screenshot is as follows:

Sunday, May 2, 2010

ColorNode and Render Sorting

I followed the same approach for ColorNode as I did for MatrixNode and now have the ability to define hierarchical color and alpha in Sgine. With this addition I started playing with TestCube to see how well it worked translucent. Unfortunately because I was not doing any sorting on the render list I end up with some undesirable effects:



With a simple modification to sort based on depth I now get this:



I'm doing the sort per render, which I'm sure is incredibly inefficient, but my framerate dropped from 3000fps to 2900fps in this example. I need to spend some time and make it only sort upon change, which in the test cube scenario would probably not make a lot of difference in performance since it's swapping positions every few frames, but will increase the efficiency in less frequently changing scenes.

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.
Scala Engine for high-performance interactive applications.