Here's something I've been hacking on for the past two months, an OpenGL scenegraph engine written mostly in Ruby with some critical sections inlined in C. It's been a educational experience :) Librend is a simple scenegraph engine on top of OpenGL, SDL, Imlib2, GLEW and Cairo that aims to make it quick and easy to write apps that mix 2D, 3D and vector graphics. I'm dubious about its suitability for public consumption at this stage but I'll release it anyhow :I Comments and questions welcome! Homepage: http://librend.rubyforge.org/ Screenshots: http://librend.rubyforge.org/screenshots/ API docs: http://librend.rubyforge.org/rdoc/ Downloads: http://rubyforge.org/frs/?group_id=716 >From the Rend module docs: Librend implements a scenegraph 3D engine. The graph is a tree, and is read from the root upwards. You create Rend::Models and attach children to them. The models are then drawn from root upwards, with the children using the properties of their parents as the default. Transforms cascade, so an object 4 levels down the tree has its base position and rotation determined by applying all the positions and rotations of its parents. The scene root is given to the renderer, which draws the scenegraph to the screen. There are no (well, shouldn be) rendering API specific parts in the scene graph, so it quite API-independent. (Note: This is not entirely true, there are some places which use API-specific functions, e.g. the classes Sound and Shader and the current unabstracted event model.) Here an irb session showcasing setting up a scene and editing it on the fly. require 'rend' # First, create the scene and some models. scene = Rend::Model.new ball = Rend::Sphere.new cube = Rend::Cube.new # Add the ball and the cube to the scene. scene.attach ball, cube # Set up the renderer and start running. renderer = Rend::Renderer.new renderer.scene = scene # Let's move the ball up... ball.position = [0, 2, 0] # ...and tweak the camera a bit. renderer.camera.fov = 60 renderer.camera.looking_at[1] = 1 renderer.camera.position[2] += 2 # Wouldn't it be nice if the cube rotated? # Let's add a time handler to do just that. cube.add_time_handler{|o,t| o.rotation = [(t*100)%360, 1, 0, 1]} # Some light would be nice aswell. scene.lights = [Rend::Light.new(:position => [2, 5, 3])] # The camera should orbit our scene, right? renderer.camera.add_time_handler{|o,t| o.position = [4.0*Math.sin(t/2), 2.0, 4.0*Math.cos(t/2)] } # The cube rotation makes the orbiting look all weird, # let's take it out. cube.time_handlers.clear # The ball looks a bit crude, let's add some detail. ball.geometry.radial_detail = 150 ball.geometry.axial_detail = 150 # And make it shiny red! ball.material = Rend::Material.new ball.material.diffuse = [1, 0, 0, 1] # red diffuse ball.material.specular = [1, 1, 1, 1] # white specular ball.material.shininess = 150 renderer.thread.kill # Oops, there goes our eventloop renderer.run # but we can restart it renderer.thread.join # and wait until someone exits the program