Showing posts with label picking. Show all posts
Showing posts with label picking. Show all posts

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.