Showing posts with label bounding. Show all posts
Showing posts with label bounding. Show all posts

Wednesday, June 16, 2010

Layout Management

For any UI application layout management is an absolute essential. In 3D game development I have yet to see any sort of layout management and though I can think of a few uses they are pretty few and far between. However, with the goals of Sgine to provide both a 3D UI as well as a full scenegraph infrastructure layout management is a very important thing.

What's particularly cool about layout management in Sgine is that it's not just for UI elements and it's not just for vertical and horizontal. That's right, you can use layout managers to lay out 3D objects in 3D space.

The layout management infrastructure is now in place, but still has a long way to go to provide a full set of advanced layout techniques. For now, here's a simple example laying out two Buttons:

package org.sgine.ui

import org.sgine.core.Direction

import org.sgine.render.StandardDisplay

import org.sgine.ui.layout.BoxLayout

object TestLayout extends StandardDisplay {
 def setup() = {
  val container = new LayoutContainer()
  container.layout := BoxLayout(Direction.Vertical, 10)
  container += new Button("Test Button 1")
  container += new Button("Test Button 2")
  scene += container
 }
}

What do you get for those five lines of logic? This:



You are not required to specify the layout as it will default to BoxLayout(Direction.Vertical, 0), but in order to show how easy configuring the layout function I decided to show a custom example. Custom layout functions can be provided that simply follow: Function1[NodeContainer, Unit].

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, April 24, 2010

Label, Scale9, FontManager, and Resource

Yes, this is my third post today, but it's Saturday and I'm cranking through code now that I've gotten over the mouse-picking hurdle!

I have created a new component "Label" that is moderately self-explanatory, but represents a single-line of text. With this Resource comes to fruition as a simple lookup call "Resource(resourceName)" that will find a resource in the defined resource paths (defaults to look in "resource" path). Additionally I have created a FontManager that simplifies the management of fonts in the system. I have plans in the long-run to create a bitmap-font generator from AWT fonts, but for now getting a font is as easy as "FontManager("Arial")" presuming there's an AngelCode font definition in the Resource lookup path by the name of "Arial.fnt".

See the following test of Label:

package org.sgine.ui

import scala.io.Source

import org.sgine.input.event.MousePressEvent

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

import org.sgine.scene.GeneralNodeContainer

object TestLabel {
 def main(args: Array[String]): Unit = {
  val r = Renderer.createFrame(1024, 768, "Test RenderScene")
  
  val scene = new GeneralNodeContainer()
  val component = new Label()
  component.location.z := -500.0
  component.font := FontManager("Franklin")
  component.text := "Hello World!"
  scene += component
  
  r.renderable := RenderableScene(scene)
 }
}

Even more exciting is Scale-9 (a.k.a nine-slice) support that opens up more advanced skinning functionality and in the short-run allows creation of pretty buttons; bet you can guess what's up next. ;)

Anyway, here's a quick test using the new Scale9 component:

package org.sgine.ui

import org.sgine.core.Resource

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

import org.sgine.scene.GeneralNodeContainer

object TestScale9 {
 def main(args: Array[String]): Unit = {
  val r = Renderer.createFrame(1024, 768, "Test Scale-9")
  
  val scene = new GeneralNodeContainer()
  
  val component = new Scale9()
  component(Resource("scale9/windows/button/hover.png"), 3.0, 3.0, 4.0, 5.0)
  component.width := 200.0
  component.height := 50.0
  component.location.z := -500.0
  scene += component
  
  r.renderable := RenderableScene(scene)
 }
}

Here's a screenshot of the application:



That's using a pretty Windows 7 skinned button in a hover state.

Component Mouse Events

Now that I've got mouse-picking all figured out things are moving swiftly forward. I have created a new "bounding" package in sgine to support hit testing for mouse events and collision detection in the future. One of the awesome things about Scala's mix-ins is that I can modularize functionality down to a few traits that make up specific functionality to allow users of the engine to take on as much or as little of the system as they want. To this end I've created two classes: MatrixPropertyContainer (name to change to MatrixObject) and BoundingObject. MatrixPropertyContainer represents an immutable property "matrix" that returns a mutable Matrix4 and BoundingObject similarly contains an immutable property "bounding" that returns a Bounding instance.

In RenderableScene I listen for events on Mouse, walk through the NodeView for my scene matching Nodes that have both of these traits and then do a hit-test with them. The result is shown in the test below:

package org.sgine.ui

import org.sgine.core.Resource

import org.sgine.event.EventHandler

import org.sgine.input.event.MouseEvent

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

import org.sgine.scene.GeneralNodeContainer

object TestMousePicking {
 def main(args: Array[String]): Unit = {
  // Create the Renderer
  val r = Renderer.createFrame(1024, 768, "Test Mouse Picking")
  
  // Create a mutable scene
  val scene = new GeneralNodeContainer()
  
  // Create an image to show the puppies
  val component = new Image()
  component.location.x := -200.0
  component.location.z := -500.0
  component.scale.x := 1.5
  component.rotation.y := Math.Pi / -4.0
  component.source := Resource("resource/puppies.jpg")   // 700x366
  scene += component
  
  // Add our scene to the renderer
  r.renderable := RenderableScene(scene, false)
  
  // Add a listener to listen to all mouse events to the picture
  component.listeners += EventHandler(mouseEvent)
 }
 
 // The method that is invoked when a mouse event occurs on the picture
 private def mouseEvent(evt: MouseEvent) = {
  println("MouseEvent: " + evt)
 }
}

Though adding a listener to mouse events is a one-line call the underlying functionality remains modular and optional to rendering functionality in the application giving the benefits of simple functionality in classes that make use of it, and trimmed down explicit functionality in those classes that do not. The mouse event system dispatches "press", "release", "move", and "wheel" events. I have on my todo to incorporate "over" and "out" events as well.

Mouse Picking

The reason for the delay since the last post has been the fact that I've been bogged down with mouse-picking for a while. Getting the math correct to properly handle ray-casting to 3D object space in a fast and efficient way took a while to wrap my head around, but finally, with quite a bit of help from my math guru of a wife I have a nice and efficient "translateLocal" method in Renderer:

def translateLocal(x: Double, y: Double, m: Matrix4, store: MutableVector3) = {
  synchronized {
   storeRay.origin.set(0.0, 0.0, 0.0)
   storeRay.direction.set(x, y, -nearDistance)
   
   storeMatrix.set(m)
   storeMatrix.invert()
   storeRay.transform(storeMatrix)
   storeRay.translateLocal(store)
   
   store
  }
 }

Today I'm working on integration of this within the Component architecture to support determination of hits within bounding regions. This does not create any additional garbage and I can run this on my machine about a million times in 700ms. Ideally I'd like the performance to be better than that, but since this does not absolutely have to be done within the rendering thread and with optimizations to limit the checks in the first place the performance should be fine.

Upgrading to LWJGL 2.4.2 from 2.2.2 gave me a framerate improvement of about 40% in my examples which is nice. :)

Next steps are to finish bounding support for BoundingQuad (what all my 2D UI components will use) and then start working on Scale-9 images, Label, TextInput, and multi-line text support with selection ability along with focus management.
Scala Engine for high-performance interactive applications.