Skip to content

Instantly share code, notes, and snippets.

@rscottm
Created March 22, 2012 22:35
Show Gist options
  • Select an option

  • Save rscottm/2165163 to your computer and use it in GitHub Desktop.

Select an option

Save rscottm/2165163 to your computer and use it in GitHub Desktop.
Initial pass at using the dexmaker project to generate Ruboto callbacks
######################################################
#
# ruboto_generate.rb (by Scott Moyer)
#
# This is an initial pass at using the dexmaker
# project (http://code.google.com/p/dexmaker/) to
# generate Ruboto callbacks. The first half of this
# script generates the callback. The second half is
# the standard demo-opengl.rb script with minor
# modifications to allow it to use the generated
# RubotoGLSurfaceViewRenderer instead of the class
# compiled into Ruboto IRB. To run this you'll need
# to compile a version of Ruboto IRB with the
# dexmaker.jar.
#
######################################################
######################################################
#
# Expand the functionality of TypeId
#
java_import 'com.google.dexmaker.TypeId'
class TypeId
@@convert_hash = {
nil => VOID,
"java.lang.String" => STRING,
"java.lang.Object" => OBJECT
}
@@corresponding_class = {
INT => get("Ljava/lang/Integer;"),
FLOAT => get("Ljava/lang/Float;"),
DOUBLE => get("Ljava/lang/Double;"),
BYTE => get("Ljava/lang/Byte;"),
BOOLEAN => get("Ljava/lang/Boolean;"),
CHAR => get("Ljava/lang/Char;"),
SHORT => get("Ljava/lang/Short;"),
LONG => get("Ljava/lang/Long;")
}
@@primitive_types = []
%w(int float double byte boolean char short long).each do |i|
@@convert_hash[i] = const_get(i.upcase)
@@primitive_types << const_get(i.upcase)
end
def self.convert_type(type)
# TODO: Handling arrays
@@convert_hash[type] || get("L#{type.gsub('.', '/')};")
end
def corresponding_class
@@corresponding_class[self]
end
def primitive?
@@primitive_types.include?(self)
end
end
######################################################
#
# Generate a new class for an interface
#
def ruboto_generate(name, interface_name)
# TODO: Already generated?
java_import 'java.lang.reflect.Modifier'
java_import 'com.google.dexmaker.Label'
java_import "com.google.dexmaker.Comparison"
dex_maker = com.google.dexmaker.DexMaker.new
interface = eval("Java::#{interface_name.gsub('$', '::')}")
class_type_id = TypeId.convert_type(name)
dex_maker.declare(class_type_id,
"#{name.split('.')[-1]}.generated",
Modifier::PUBLIC,
TypeId::OBJECT,
TypeId.convert_type(interface_name))
#
# Create callbacks field
#
callbackProcs_field = class_type_id.getField(TypeId.get("[Ljava/lang/Object;"), "callbackProcs")
dex_maker.declare(callbackProcs_field, Modifier::PRIVATE, nil)
#
# Build constructor and create callbacks array
#
constructor_id = class_type_id.getConstructor
dex_maker.declare(constructor_id, Modifier::PUBLIC).instance_eval do
array = newLocal(TypeId.get("[Ljava/lang/Object;"))
size = newLocal(TypeId::INT)
invokeDirect(TypeId::OBJECT.getConstructor, nil, getThis(class_type_id))
loadConstant(size, interface.java_class.declared_instance_methods.length)
newArray(array, size)
iput(callbackProcs_field, getThis(class_type_id), array)
returnVoid
end
#
# Build setCallbackProc method
#
method_id = class_type_id.getMethod(TypeId::VOID, "setCallbackProc", TypeId::INT, TypeId::OBJECT)
dex_maker.declare(method_id, Modifier::PUBLIC).instance_eval do
index = getParameter(0, TypeId::INT)
block = getParameter(1, TypeId::OBJECT)
array = newLocal(TypeId.get("[Ljava/lang/Object;"))
iget(callbackProcs_field, array, getThis(class_type_id))
aput(array, index, block)
returnVoid
end
#
# Loop through and build a constant and method for each method in the interface
#
interface.java_class.declared_instance_methods.each_with_index do |m,i|
# Define the constant
constant_name = "CB_" + m.name.gsub(/[A-Z]/, '_\0').upcase.gsub(/^ON_/, "")
const = class_type_id.getField(TypeId::INT, constant_name)
dex_maker.declare(const, Modifier::PUBLIC | Modifier::STATIC | Modifier::FINAL, i.to_java(:int))
# Build the method
parameter_type_array = m.parameter_types.map{|j| TypeId.convert_type(j.name)}
method_id = class_type_id.getMethod(TypeId.convert_type(m.return_type), m.name, *parameter_type_array)
dex_maker.declare(method_id, Modifier::PUBLIC).instance_eval do
parameter_array = []
parameter_type_array.each_with_index{|j, k| parameter_array << getParameter(k, j)}
# Callback procs array
index = newLocal(TypeId::INT)
array = newLocal(TypeId.get("[Ljava/lang/Object;"))
block = newLocal(TypeId::OBJECT)
# Call arguments array
p_arr = newLocal(TypeId.get("[Ljava/lang/Object;"))
p_size = newLocal(TypeId::INT)
p_index = newLocal(TypeId::INT)
# Holds nil for comparison
null = newLocal(TypeId::OBJECT)
# Holds method name
call_string = newLocal(TypeId::STRING)
# Create a local to help convert primitives
tmp_locals = {}
parameter_type_array.each do |p|
tmp_locals[p] = newLocal(p.corresponding_class) if p.primitive?
end
# Local for posible return
ret = m.return_type ? newLocal(TypeId.convert_type(m.return_type)) : nil
no_block = Label.new
iget(callbackProcs_field, array, getThis(class_type_id))
loadConstant(index, i)
aget(block, array, index)
loadConstant(null, nil)
compare(Comparison::EQ, no_block, block, null)
# TODO: Handle multiple parameters differently
script_class_type_id = TypeId.convert_type("org.ruboto.Script")
# Create and populate an array for method parameters
loadConstant(p_size, parameter_type_array.length)
newArray(p_arr, p_size)
parameter_array.each_with_index do |k, j|
loadConstant(p_index, j)
# Need to convert primitives to add to array
if k.type.primitive?
newInstance(tmp_locals[k.type], k.type.corresponding_class.getConstructor(k.type), k)
aput(p_arr, p_index, tmp_locals[k.type])
else
aput(p_arr, p_index, k)
end
end
# Call Script to call the method
# TODO: Test non void returns
method_parameters = [TypeId::VOID, "callMethod", TypeId::OBJECT, TypeId::STRING, TypeId.get("[Ljava/lang/Object;")]
method_parameters << TypeId.convert_type("java.lang.Class") if ret
script_call_method_id = script_class_type_id.getMethod(*method_parameters)
loadConstant(call_string, "call")
method_parameters = [script_call_method_id, ret, block, call_string, p_arr]
method_parameters << m.return_type if ret # Might need .java_class
invokeStatic(*method_parameters)
mark(no_block)
ret ? returnValue(ret) : returnVoid
end
end
# TODO: Consider using the byte array instead of writing to a file
# TODO: Use default directory
loader = dex_maker.generateAndLoad(
com.google.dexmaker.DexMaker.java_class.class_loader,
java.io.File.new("/sdcard/jruby"))
runtime = org.jruby.Ruby.getGlobalRuntime
rv = org.jruby.javasupport.Java.getProxyClass(
runtime,
org.jruby.javasupport.JavaClass.get(runtime, loader.loadClass(name)))
Dir.glob("/sdcard/jruby/Generated*"){|f| File.delete f}
return rv
end
######################################################
#
# Generate RubotoGLSurfaceViewRenderer
#
RubotoGLSurfaceViewRenderer = ruboto_generate(
"org.rubyandroid.RubotoGLSurfaceViewRenderer",
"android.opengl.GLSurfaceView$Renderer")
require 'ruboto/base'
RubotoGLSurfaceViewRenderer.class_eval do
extend Ruboto::CallbackClass
include Ruboto::Callbacks
end
#######################################################
#
# glsurfaceview.rb (by Scott Moyer)
#
# This demo ports the GLSurfaceView demo from the Android
# API Demos to Ruboto.
#
#######################################################
require 'ruboto/activity'
confirm_ruboto_version(10, false)
java_import "android.opengl.GLSurfaceView"
java_import "javax.microedition.khronos.egl.EGL10"
java_import "javax.microedition.khronos.egl.EGLConfig"
java_import "javax.microedition.khronos.opengles.GL10"
java_import "java.nio.ByteBuffer"
java_import "java.nio.ByteOrder"
java_import "java.nio.IntBuffer"
# A class implementing GLSurfaceView.Renderer
#ruboto_import "org.ruboto.callbacks.RubotoGLSurfaceViewRenderer"
#######################################################
#
# Cube Class
#
# Needs to get set up before the Activity tries to use it
#
class Cube
def initialize
one = 0x10000
vertices = [
-one, -one, -one,
one, -one, -one,
one, one, -one,
-one, one, -one,
-one, -one, one,
one, -one, one,
one, one, one,
-one, one, one,
]
colors = [
0, 0, 0, one,
one, 0, 0, one,
one, one, 0, one,
0, one, 0, one,
0, 0, one, one,
one, 0, one, one,
one, one, one, one,
0, one, one, one,
]
indices = [
0, 4, 5, 0, 5, 1,
1, 5, 6, 1, 6, 2,
2, 6, 7, 2, 7, 3,
3, 7, 4, 3, 4, 0,
4, 7, 6, 4, 6, 5,
3, 0, 1, 3, 1, 2
]
vbb = ByteBuffer.allocateDirect(vertices.length*4)
vbb.order(ByteOrder.nativeOrder)
@vertex_buffer = vbb.asIntBuffer
@vertex_buffer.put(vertices.to_java(:int))
@vertex_buffer.position(0)
cbb = ByteBuffer.allocateDirect(colors.length*4)
cbb.order(ByteOrder.nativeOrder)
@color_buffer = cbb.asIntBuffer
@color_buffer.put(colors.to_java(:int))
@color_buffer.position(0)
@index_buffer = ByteBuffer.allocateDirect(indices.length)
@index_buffer.put(indices.to_java(:byte))
@index_buffer.position(0)
end
def draw(gl)
gl.glFrontFace(GL10::GL_CW)
gl.glVertexPointer(3, GL10::GL_FIXED, 0, @vertex_buffer)
gl.glColorPointer(4, GL10::GL_FIXED, 0, @color_buffer)
gl.glDrawElements(GL10::GL_TRIANGLES, 36, GL10::GL_UNSIGNED_BYTE, @index_buffer)
end
end
#######################################################
#
# Activity
#
# Start a new activity or connect to $activity
#
$activity.start_ruboto_activity "$glsurface" do
setTitle "GLSurfaceView"
def on_create(bundle)
@surface_view = GLSurfaceView.new(self)
@surface_view.renderer = @renderer
self.content_view = @surface_view
end
def on_resume
@surface_view.on_resume
end
def on_pause
@surface_view.on_pause
end
@renderer = RubotoGLSurfaceViewRenderer.new_with_callbacks do
@translucent_background = false
@cube = Cube.new
@angle = 0.0
def on_draw_frame(gl)
gl.glClear(GL10::GL_COLOR_BUFFER_BIT | GL10::GL_DEPTH_BUFFER_BIT)
gl.glMatrixMode(GL10::GL_MODELVIEW)
gl.glLoadIdentity
gl.glTranslatef(0, 0, -3.0)
gl.glRotatef(@angle, 0, 1, 0)
gl.glRotatef(@angle*0.25, 1, 0, 0)
gl.glEnableClientState(GL10::GL_VERTEX_ARRAY)
gl.glEnableClientState(GL10::GL_COLOR_ARRAY)
@cube.draw(gl)
gl.glRotatef(@angle*2.0, 0, 1, 1)
gl.glTranslatef(0.5, 0.5, 0.5)
@cube.draw(gl)
@angle += 1.2
end
def on_surface_changed(gl, width, height)
gl.glViewport(0, 0, width, height)
ratio = width.to_f / height.to_f
gl.glMatrixMode(GL10::GL_PROJECTION)
gl.glLoadIdentity
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10)
end
def on_surface_created(gl, config)
gl.glDisable(GL10::GL_DITHER)
gl.glHint(GL10::GL_PERSPECTIVE_CORRECTION_HINT, GL10::GL_FASTEST)
if (@translucent_background)
gl.glClearColor(0,0,0,0)
else
gl.glClearColor(1,1,1,1)
end
gl.glEnable(GL10::GL_CULL_FACE)
gl.glShadeModel(GL10::GL_SMOOTH)
gl.glEnable(GL10::GL_DEPTH_TEST)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment