Android OpenGLES Water Caustics

I like water caustics – those sinuous reflections you get from the surface of water. I’ve always wanted a good “water pool” type animated background. The apps on the store don’t quite do what I’ve been looking for, so I wrote one.

Initially, I dipped back into 3D programming and tried to see if they could be done in real-time on the device. Water Caustics are notorious for being computationally intensive to get looking right, so I was sceptical it could be done on a mobile device, and I was right to be! Even using native c++ and the android NDK, Fast fourier transforms for smoothing the wave surface and OpenGL for sunlight ray rendering (using the fastest caustics mathematical algorithm I could find), the result is just too disappointing at 25 fps.

caustics_test

Mobile processors are good, but they are not that good yet. To get a real-time effect without jitters doesn’t give good enough detail on my HTC Desire – it just looks like a smoke filter effect. Not to mention the drain on the battery and other processes. Still, it was fun to get back to OpenGL 3D rendering again.

In the end, the best solution was to let a desktop app do the hardwork of rendering a set of tileable, loopable animation frames. I still use OpenGL and the NDK for the final live wallpaper app, but this time just to bitblit the frames onto the surface. It’s just about fast enough on my device and OS and looks pretty cool in motion. You’ll just have to believe me! (or else download it from my apps page).

caustics_wallpaper

Loading EJBs in JRuby on Torquebox

Here’s one of those obscure little problems you come across in Torquebox that might help someone wrestling with the same problem; it sure would have saved me some time!

One of the things I like about using the Java flavour of Ruby (JRuby) is that it’s pretty easy
to invoke and access EJBs. Here’s an example:

require 'java'
require '~/Applications/torquebox-1.0.1/jboss/lib/jboss-common-core.jar'
require '~/Applications/torquebox-1.0.1/jboss/client/jbossall-client.jar'
require '~/ExampleEAR/ExampleEAR-ejb/dist/ExampleEAR-ejb.jar'
require '~/Applications/netbeans/enterprise/modules/ext/javaee-api-6.0.jar'

include_class 'java.util.Properties'
include_class 'javax.naming.InitialContext'
include_class 'com.hypermatix.session.ExampleSessionBean';
include_class 'com.hypermatix.session.ExampleSessionBeanRemote';

class EJBExampleController < ApplicationController
  
  def index
    properties = Properties.new
    properties.put(Context::INITIAL_CONTEXT_FACTORY,"org.jnp.interfaces.NamingContextFactory")
    properties.put(Context::PROVIDER_URL, "jnp://localhost:1099")
    properties.put(Context::URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");

    context = InitialContext.new(properties)

    sessionBean = context.lookup("ExampleEAR/ExampleSessionBean/remote")

    result = sessionBean.someBusinessMethod(parameters)
  end
end

Torquebox is a platform that combines JBoss AS and JRuby into one server, and adds a lot of very powerful integration additions too. However, when I deployed this JRuby test app and the EJB on the same Torquebox JBoss instance, I got a "name not bound" message with the JNDI context.lookup for the EJB. Looking further down the stack trace (getting a stack trace from a JRuby NativeException needs a helper!):

Can not find interface declared by Proxy in our CL + org.jboss.web.tomcat.serice.WebCtxLoader

Further again down the stack trace, it turned out that a ClassNotFoundException was causing this. My own ExampleSessionBean EJB class was not being located by the Torquebox JBoss instance. When I tried to invoke it in a standalone Java app, it worked fine. I couldn't find any similar problem reported for JRuby, however a similar error can occur in Apache web apps. It turns out to be the way the JBoss class loading mechanism works. The solution is to ensure that the web app uses the same class loading domain as the EJB. To do this, just add a jboss-classloading.xml file into the config directory of the JRuby app and into the EAR you use to deploy the EJBs:

<?xml version="1.0" encoding="UTF-8"?>
<classloading xmlns="urn:jboss:classloading:1.0"
              domain="DefaultDomain"
              parent-domain="Ignored"  
              export-all="NON_EMPTY"   
              import-all="true">
</classloading>