Wednesday, June 23, 2010

JLaunch + Sgine

As I mentioned in my previous post I've been working on a simplified deployment system for Java applications and I've made some pretty good strides. To that end I wanted to share my current progress by finally posting a working applet here to show:



Using a bit of JavaScript the applet will launch when you click on the static image. This is still a very early version, but my goal is to release JLaunch as an open-source project that provides a wizard to simplify creating deployments. The system currently supports two methods of launching. The first is a static void main(args) method that it invokes that gives over complete control to display a new Frame or whatever it is configured to do. The second is the referenced class providing a "createCanvas" method that returns a java.awt.Canvas instead that gets put into the applet for you upon instantiation. The latter instance is what is being used in the above example and is implemented by default in StandardDisplay, so any classes that extend that don't even have to think about it.

For example, see the code for the TestImage class being used:

package org.sgine.ui

import org.sgine.core.Resource

import org.sgine.render.Debug
import org.sgine.render.StandardDisplay

object TestImage extends StandardDisplay with Debug {
 def setup() = {
  val component = new Image(Resource("puppies.jpg"))
  scene += component
 }
}

I also inherit Debug which disables vertical-sync and shows the framerate in the top-left of the screen. To sum up, the above two lines of logic can be launched as an application (since StandardDisplay provides a main method that is inherited) or as an applet via JLaunch. Pretty awesome huh? :)

One last noteworthy item is that the jlaunch.jar is 14kb and the pack200 version is a total of 5kb. This, I believe, is a good representation of what the startup time of applets should be.

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].

Tuesday, June 15, 2010

Concise and Powerful

One of my biggest complaints and one of the things I've been criticized about most is my hatred for the complexities of using the majority of 3D engines today. To this end I've strived in Sgine to provide a much more simplistic and concise engine without sacrificing the power you would expect from a 3D engine.

When I was a developer on the jME (http://www.jmonkeyengine.com) project it is what led me to write "StandardGame" as far too commonly developers would end up with basically the same "Game" implementation for all their games, but it either consisted of a lot of copy and paste from other projects or a lot of re-writing the same code each time a new project was created.

If you look at previous posts the code has always been moderately simple and no more than a dozen or so lines of code to accomplish a basic task. However, I still had visions for improving further.

Perhaps I've gone a bit overboard in my agenda to simplify, but I've created a new trait in Sgine called "StandardDisplay" that is the spiritual successor to the ideas I put forth in jME years before. With Scala and Sgine a lot of possibilities for simplification have been opened up that were never possible before, such as putting the main method in a trait so it need not be explicitly defined. Further, one of the big problems with JRE applications today is deployment. Whether you want an Applet, a WebStart application, a desktop application, or all three it is a great deal of pain to get the end result you want. One of the goals of StandardDisplay is to be able to simply extend and execute in any display model you desire. Couple this with the idea of a deployment generation system I have yet to write and deployment of applications can be a simple afterthought instead of a major headache holding you back from getting your projects out.

Okay, I've droned on long enough without some actual code to show. If you've read this blog from the beginning you'll remember the very first post I made with an actual example with the following code to display an image of my dogs:

package org.sgine.render

import org.sgine.math.Matrix4

import javax.imageio._

import org.lwjgl.opengl.GL11._

object TestRenderer {
 def main(args: Array[String]): Unit = {
  val r = Renderer.createFrame(1024, 768, "Test Renderer")
  
  val t = new Texture(700, 366)
  TextureUtil(t, ImageIO.read(getClass.getClassLoader.getResource("resource/puppies.jpg")), 0, 0, 700, 366)
  
  val m = Matrix4().translate(z = -1000.0)
  val i = Image(t)
  val fps = FPS(1.0)
  
  r.renderable := RenderList(MatrixState(m) :: i :: fps :: Nil)
  
  println("Renderer started!")
 }
}

That concept has firmed up a lot since then with the scenegraph system and the UI package to more recently look like:

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
import org.sgine.scene.ext.ResolutionNode

object TestImage {
 def main(args: Array[String]): Unit = {
  val r = Renderer.createFrame(1024, 768, "Test RenderScene")
  
  val scene = new GeneralNodeContainer() with ResolutionNode
  scene.setResolution(1024, 768)
  
  val component = new Image()
  component.source := Resource("puppies.jpg")
  scene += component
  
  r.renderable := RenderableScene(scene)
 }
}

Though much more concise and simplified I still was not satisfied. After all, I just wanted to display a picture of my puppies on the screen. Enter StandardDisplay:

package org.sgine.ui

import org.sgine.core.Resource

import org.sgine.render.StandardDisplay

object TestImage extends StandardDisplay {
 def setup() = {
  val component = new Image(Resource("puppies.jpg"))
  scene += component
 }
}

That's right, two lines of actual logic code to display an Image with puppies. Yes, this technically could have been reduced to a single line, but the goal was concise, not showing how I used to write code in Perl. ;)

This can be executed as an application since StandardDisplay trait contains a "main" method that calls "start" and the only requirement is that a "setup" method be implemented. You can go much further than this if desired to customize and configure how and what you want to display but this is all that is required to get going.

There is still a lot of work to be done to support Applets, WebStart, etc. but the core is functional and pretty impressive if I do say so myself. :)

Sunday, June 13, 2010

MediaPlayer Component

Getting JMC from JavaFX 1.3 to work within Sgine took a bit longer than I anticipated. In JavaFX 1.2 it was a simple matter of calling paintVideoFrame and using a BufferedImage to draw onto and then convert to a texture. Fortunately for performance in JavaFX 1.3 they changed to working with NIO buffers instead, but unfortunately that created a much bigger headache trying to figure out how to access and properly use it since there is absolutely no documentation and the source code is not freely available. However, after several days of poking around it has finally paid off. There are still some optimizations that should be done but on my machine I'm able to watch H.264 video at 4,000fps in synchronous mode (direct rendering in OpenGL) and 5,200fps in asynchronous mode (pushing data to the buffer in another thread via PBO).

Here's a screenshot of the working example:


Here's the source required to run the video:
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
import org.sgine.scene.ext.ResolutionNode

object TestMediaPlayer {
 def main(args: Array[String]): Unit = {
  val r = Renderer.createFrame(640, 480, "Test MediaPlayer")
  
  val scene = new GeneralNodeContainer() with ResolutionNode
  scene.setResolution(640, 480)
  
  val component = new MediaPlayer()
  val url = new java.net.URL("http://sun.edgeboss.net/download/sun/media/1460825906/1460825906_2956241001_big-buck-bunny-640x360.flv")
  component.source := Resource(url)
  scene += component
  
  r.renderable := RenderableScene(scene)
  
  component.play()
 }
}

As always the goal is to keep the API as simple as possible and hopefully 27 total lines of code is simplistic enough. :)

There's still much left to do as far as introspection and control of video playing as well as controls to allow user interaction with the player, but we've finally got a fast and reliable means of playing all popular audio and video formats and I will probably divert back to more important work for a while. :)

Wednesday, June 9, 2010

Video Player

I've been pretty busy lately and haven't had time to work on Sgine as much as I'd liked, so I figured I needed to have something pretty awesome to show for my next post. Hopefully video playing is still considered awesome as that's the best I could do.

I'm leveraging the JMC system that JavaFX uses for video playing and integrating it to render directly to LWJGL.

It's not really ready for prime-time as I've got a lot of things still to do to increase the framerate and integrate it as an actual component, but for now I have a screenshot with the promise of more to come soon:

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