JRubyでODEをはじめてみる2

前回に引き続き
今度はGUIで試してみます。

オリジナルのODEにはdrawstuffというデモ用のGUIキット?が含まれています。
ode4jにもありがたいことにdemoのjarの中にdrawstuffが隠れています。
今回はdemoに含まれているdrawstuffを利用します。

ode4jのdrawstuffはLWJGLライブラリが使用されているので準備します。
http://lwjgl.org/download.php
lwjgl-2.9.1.zip

まずはdrawstuffの動作確認

require 'ode4j/core-0.2.8.jar'
require 'ode4j/demo-0.2.8.jar'
require 'lwjgl/jar/lwjgl.jar'
require 'lwjgl/jar/lwjgl_util.jar'

DrawStuff = org.ode4j.drawstuff.DrawStuff
DsFunctions = org.ode4j.drawstuff.DrawStuff.dsFunctions

class TestSim < DsFunctions

  def start
  end

  def command(cmd)
  end

  def step(pause)
  end

  def stop
  end
end

DrawStuff::dsSimulationLoop(ARGV, 400,300, TestSim.new)

実行は次のようにします。
lwjglはネイティブライブラリが必要なためパス指定を付けてあげます。
たとえば以下はMacOS の場合です。

  • 実行
 $ ls lwjgl/macosx
libjinput-osx.jnilib
liblwjgl.jnilib
openal.dylib

 $ jruby-1.7.9 -J-Djava.library.path=lwjgl/macosx hoge2.rb


(GUI)ボールの落下

以下のソースでひとまず格好がつきました。

require 'ode4j/core-0.2.8.jar'
require 'ode4j/demo-0.2.8.jar'
require 'lwjgl/jar/lwjgl.jar'
require 'lwjgl/jar/lwjgl_util.jar'

#for ODE
OdeHelper = org.ode4j.ode.OdeHelper
DContactJoint = org.ode4j.ode.DContactJoint
DContactBuffer = org.ode4j.ode.DContactBuffer
OdeConstants = org.ode4j.ode.OdeConstants
#for Drawstuff
DrawStuff = org.ode4j.drawstuff.DrawStuff
DsFunctions = org.ode4j.drawstuff.DrawStuff.dsFunctions

class TestSim < DsFunctions

  MAX_CONTACTS = 4 

  def nearCallback(data,o1,o2)
    b1 = o1.getBody
    b2 = o2.getBody

    until b1 or b2 or !OdeHelper.areaConnectedExcluding(b1,b2,DContactJoint.class)
      return
    end 

    contacts = DContactBuffer.new(MAX_CONTACTS)
    
    MAX_CONTACTS.times do |n| 
      contact = contacts.get(n)
      contact.surface.mode = OdeConstants.dContactBounce |
                            OdeConstants.dContactSoftCFM
      contact.surface.mu = OdeConstants.dInfinity
      contact.surface.bounce = 0.1
      contact.surface.bounce_vel = 0.1
      contact.surface.soft_cfm = 0.01
    end

    numc = OdeHelper.collide(o1,o2,MAX_CONTACTS,contacts.getGeomBuffer)
    if numc != 0
      numc.times do |n|
        c = OdeHelper.createContactJoint(@world,@cgroup,contacts.get(n))
        c.attach(b1,b2)
      end
    end
  end

  def init
    @world = OdeHelper.createWorld
    @world.setGravity(0.0, 0.0, -9.8)
    @world.setCFM(0.001)
    @space = OdeHelper.createHashSpace
    #@space = OdeHelper.createSimpleSpace(nil)
    @cgroup = OdeHelper.createJointGroup

    floor = OdeHelper.createPlane(@space, 0.0, 0.0, 1.0, 0)

    ball_body = OdeHelper.createBody(@world)
    ball_body.setPosition(0.0, 0.0, 10.0)
    ball_mass = OdeHelper.createMass
    ball_mass.setSphereTotal(1.0, 0.5)
    ball_body.setMass(ball_mass)
    @ball = OdeHelper.createSphere(@space, 0.5)
    @ball.setBody(ball_body)
  end

  def start
    xyz = [-6.0,0,3.0]
    hpr = [0.0,0.0,0.0]
    DrawStuff::dsSetViewpoint(xyz,hpr)

    init
  end

  def command(cmd)
  end

  def simLoop(pause)
    @space.collide(nil, Proc.new{|data,o1,o2|
        nearCallback(data,o1,o2)
    })
    if !pause
#      @world.step(0.02)
      @world.quickStep(0.02)
    end
    @cgroup.empty

    pos = @ball.getPosition.toFloatArray
    puts "pos - #{pos[0]} #{pos[1]} #{pos[2]}"

    DrawStuff::dsSetColor(1,0,0)
    DrawStuff::dsDrawSphere(
        @ball.getPosition,
        @ball.getRotation,
        0.5)
  end

  def step(pause)
    simLoop(pause)
  end

  def stop
  end
end

#OdeHelper.initODE
OdeHelper.initODE2(0)

DrawStuff::dsSimulationLoop(ARGV, 400,300, TestSim.new)


作りはかなり雑です。
全くといっていいほどRubyらしく書けてません。
つっこむところはいろいろありそうですが、とりあえず、やりたいことの第一歩としてはよいのかなと。

JRuby+ODE という組み合わせは如何なものかと思いますが、利点をあげるとするならば

  • Java環境が入っていればどこでも動く
  • 環境構築からコード実行まで一切コンパイルを必要としない
  • ODEでRubyの楽しさを味わえる

という感じでしょうか。

ただし、おそらくオリジナルODEよりは動作はおそくなるだろうなーと。
その辺はいろいろ試す中でみていこうと思います。