Saturday, July 16, 2011

More Concise and More Powerful

Over a year ago I posted about the focus of Sgine to be extremely concise in creating 3d applications. I've always found it annoying that the barrier to entry writing 3d applications of any sort if overly complicated. I started Sgine with the belief that an engine could be created that abstracts without reducing the power of the system. Scala has been instrumental in making this possible.

Now, the point of this post is to announce that I'm finally back to the UI abstraction layer again. After nearly a year of re-architecting and adding new foundational concepts I'm finally back to the high-level abstraction that is the magic of Sgine.

In the post I mentioned above I was showing off just how concisely you can display an image in Sgine:

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
 }
}

My goal in this re-write was to keep at least as concise as 0.1 and hopefully achieve something even better. I do believe I have accomplished something even more concise in the current iteration of development with only a single line of logic to display an image on the screen:

package org.sgine.ui

object ImageTest extends UI {
  this += Image("sgine.png")
}

There may be some slight revisions to this (ex. I'm considering extracting the base container out into a variable name "container" instead of mixing it into UI), but I honestly don't see any way I can make it any more concise than that. :)

For those of you that love pictures, here's the screenshot of what's displayed:



There hasn't been a lot of chatter on here lately, but now that I'm back on the UI abstraction layer there will be a lot more to see, so check back often.

Sunday, June 12, 2011

Sgine + VLC = Awesomeness

I have long been interested in being able to play video in OpenGL and if you look back at previous posts I've made here, as well as posts at matthicks.com you'll see most of my previous work has been around the use of JMC (one of the only cool things to come out of JavaFX 1.x). However, it had significant problems playing specific kinds of videos and was just generally buggy. In this new remix of Sgine I'm adding a new project that provides complete support for VLC through the use of the VLCJ project.

Here are a couple screenshots taken in my testing using "Better Off Ted":





Still very preliminary, but both sound and video are displaying flawlessly on the screen. The great features of VLC include the ability to play practically any video file or stream, record from a webcam, watch DVDs, play audio, and even record the desktop. This will open the door for lots of useful non-game applications utilizing Sgine I hope.

It will probably be a while before I commit this new project as I still have a lot of tweaking and customization to do before it's ready. Additionally, this will likely rely on the still undefined scenegraph model for this iteration so check back frequently as there's much more to come.

Friday, June 3, 2011

NeHe Tutorials

In an effort to validate and test the functionality of the OpenGL Generator and derived implementations I have started porting the NeHe tutorials over to Sgine. If you aren't familiar with the NeHe tutorials they are a great resource to learn the fundamentals of OpenGL. I recently finished porting the first chunk to Sgine and wanted to post pictures and links to source. I start here with Lesson 2 since Lesson 1 is all setup and doesn't really apply to what I'm trying to show.



Lesson 02



Sgine Source code




Lesson 03



Sgine Source code




Lesson 04



Sgine Source code




Lesson 05



Sgine Source code



I'm not sure how many more of the NeHe tutorials I'll port as they require a lot of boiler plate to properly represent the OpenGL aspects of the code and my main goal with Sgine is to minimize boiler plate and complexity. However, this does serve as a good test base for OpenGL functionality on both desktop and Android device.

I am currently in the process of defining a good texturing / image abstraction layer for the rendering paradigm and will likely be posting about that in the near future.

Sunday, May 29, 2011

Reloaded

Yes, it has been quite a while since the last post, but Sgine is anything but inactive. Pretty soon after that last post a decision was made to add support for Android to the core architecture of Sgine. As you might imagine this was no small undertaking and required some massive changes. So here we are several months later and Sgine has been re-designed from the ground up and I wanted to take a minute to discuss some of those changes.

First, there is a new OpenGL abstraction layer. No, this isn't abstracting you from the details of OpenGL, but rather the OpenGL implementation. Obviously there's a great reason for this decision, and here's where we come back to our inclusion of Android. Android uses OpenGL ES and LWJGL (our desktop rendering platform) uses standard OpenGL. Now, in order to write applications that will run on the desktop and on Android we need a point of convergence to build from and that point is OpenGL. Unfortunately though they are incredibly similar, they are still very different in many ways. This required a unification layer and ultimately a code generator to dynamically link between Android GLES and LWJGL. I'd love to say at this point it's 100% finished, but honestly I expect there's still going to be modifications happening to this layer for a while.

The result of this work is the ability to import org.sgine.opengl.GL._ and get access to a consistent OpenGL layer that will work on desktop and handheld alike. What does this look like? Well, quite simple really. The following is a recreation of Lesson 2 of the NeHe tutorial in Sgine:
class Lesson02 extends GLDisplay {
  private val triangleVertices = Array(0.0f, 1.0f, 0.0f,
                                       -1.0f, -1.0f, 0.0f,
                                       1.0f, -1.0f, 0.0f)
  private var triangleVertexBuffer: FloatBuffer = _

  private val quadVertices = Array(-1.0f, -1.0f, 0.0f,
                                   1.0f, -1.0f, 0.0f,
                                   -1.0f, 1.0f, 0.0f,
                                   1.0f, 1.0f, 0.0f)
  private var quadVertexBuffer: FloatBuffer = _

  def create() = {
    glShadeModel(GL_SMOOTH)
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f)
    glClearDepth(1.0f)
    glEnable(GL_DEPTH_TEST)
    glDepthFunc(GL_LEQUAL)

    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()

    // Create Triangle
    var bb = ByteBuffer.allocateDirect(triangleVertices.length * 4)
    bb.order(ByteOrder.nativeOrder())
    triangleVertexBuffer = bb.asFloatBuffer()
    triangleVertexBuffer.put(triangleVertices)
    triangleVertexBuffer.position(0)

    // Create Quad
    bb = ByteBuffer.allocateDirect(quadVertices.length * 4)
    bb.order(ByteOrder.nativeOrder())
    quadVertexBuffer = bb.asFloatBuffer()
    quadVertexBuffer.put(quadVertices)
    quadVertexBuffer.position(0)
  }

  def resize(width: Int, height: Int) = {
    glViewport(0, 0, width, height)

    gluPerspective(45.0f, width.toFloat / height.toFloat, 0.1f, 100.0f)

    glMatrixMode(GL_MODELVIEW)
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
  }

  def render() = {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()
    glTranslatef(-1.5f, 0.0f, -6.0f)

    glVertexPointer(3, GL_FLOAT, 0, triangleVertexBuffer)
    glEnableClientState(GL_VERTEX_ARRAY)
    glDrawArrays(GL_TRIANGLE_STRIP, 0, triangleVertices.length / 3)
    glDisableClientState(GL_VERTEX_ARRAY)

    glTranslatef(3.0f, 0.0f, 0.0f)
    glVertexPointer(3, GL_FLOAT, 0, quadVertexBuffer)
    glEnableClientState(GL_VERTEX_ARRAY)
    glDrawArrays(GL_TRIANGLE_STRIP, 0, quadVertices.length / 3)
    glDisableClientState(GL_VERTEX_ARRAY)
  }
}

object Lesson02 {
  def main(args: Array[String]): Unit = {
    val lesson02 = new Lesson02()
    val controller = LWJGLController(lesson02, 1024, 768, "NeHe Lesson 02")
  }
}

As you can see from the original tutorial the OpenGL code is surprisingly similar. It would have been almost identical if GLES supported fixed function pipeline (old-school GL).

Hopefully in the near future many more of the NeHe tutorials will be ported to Sgine as examples of how to do OpenGL coding using Sgine, but this is a good start. If you want to see the current status check out the org.sgine.opengl.nehe project.

This leads to the next major change in Sgine. The framework / engine was becoming too large for a single project and thanks to the awesomeness that is SBT (Simple Build Tool) the project has now been split into a multi-project that is built and published individually and with better dependency separation. This will allow projects to take advantages of parts of Sgine without having to utilize the entire framework.

Now, as might be obvious from previous posts I've made, I'm a big fan of simplicity and smaller more concise blocks of code. The above snippet for Lesson 2 had two major flaws:
  1. The number of lines of code is incredibly high to draw a triangle and quad to the screen.
  2. Having to directly use OpenGL seems incredibly tedious when you need to do even something as simple as drawing a triangle and quad to the screen.

This leads to the next layer of abstraction in Sgine. We now have a render abstraction that hides the details of the rendering platform to the developer. This means we can both plug in a different rendering platform later (can you say ray-tracing?) as well as reduce the complexity of standard rendering tasks in a well defined object oriented structure.

The following code is Lesson 2 using the new rendering framework:
object RendererTest extends RenderApplication {
  private val triangleMatrix = Matrix4.Identity.translate(x = -1.5, z = -6.0)
  private val triangleShape = Shape(Vertices.triangle())
  private val quadMatrix = Matrix4.Identity.translate(x = 1.5, z = -6.0)
  private val quadShape = Shape(Vertices.quad())

  def update() = {
  }

  def render() = {
    renderer.loadMatrix(triangleMatrix)
    triangleShape.render()
    renderer.loadMatrix(quadMatrix)
    quadShape.render()
  }

  def dispose() = {
    triangleShape.dispose()
    quadShape.dispose()
  }
}

I presume it's pretty obvious what this code is doing apart from the call to the Vertices methods. The Vertices triangle and quad methods have default args that define the size of the shape to be -1 to 1 both vertically and horizontally and generates the the triangle vertices to display.

For those of you who like pictures, it's not much, but here's a screenshot:


One final thing to note is that Sgine is going back to providing its own math library. Simplex3d has been great, but the needs of Sgine are moderately different from the goals of Simplex3d and for both performance and simplicity we are rolling our own.

The new rendering framework and math library are still in early development, but more is coming fast. Once the rendering framework is complete there will be one last layer of abstraction above that: the scenegraph. The scenegraph envisioned will be extremely similar to the scenegraph that Sgine 0.1 provided (pre-refactor), the major difference being performance improvements and relying the rendering framework relying on the OpenGL abstraction. The end goal is still to provide an extremely performant, yet easy to use 3d engine in Scala.

Stay tuned, more will be coming soon.
Scala Engine for high-performance interactive applications.