JRubyでODEをはじめてみる

物理計算エンジンOpen Dynamics Engine (ODE) をRubyでやりたい!という前々から願望があり、いろいろ調べているうちにJRubyならどうだろうということで試してみました。

ODEはC/C++のライブラリですが、探してみるとJava版も存在しているようです。
Java版ODE: ode4j
http://www.ode4j.org
ode4jはメンテナンスが行き届いているようでちゃんとこの時点で最新であるODE-0.12.0に対応しています。
また、APIJava版にアレンジされていますがオリジナルよりももしかするときれいにまとまっていて扱いやすそうです。
で、JavaのライブラリならばJRubyでも使えるぞ、ということでチャレンジしてみました。

(CUI)自由落下、衝突なし

まずは、GUIなしで試してみます。

require 'core-0.2.8.jar'

OdeHelper = org.ode4j.ode.OdeHelper


OdeHelper.initODE

world = OdeHelper.createWorld
world.setGravity(0.0, 0.0, -9.8)

body = OdeHelper.createBody(world)
body.setPosition(0.0, 0.0, 10.0)

1000.times do
  world.step(0.01)

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

  break if pos[2] < 0.0 
end

サイトからcore-0.2.8.jarをダウンロードしてきたら、rubyスクリプトと同じ場所に置きます。
実行すると、

 $ jruby-1.7.9 hoge.rb

(CUI)自由落下、衝突あり

次に、衝突ありを試してみます。

require 'core-0.2.8.jar'

OdeHelper = org.ode4j.ode.OdeHelper
DContactJoint = org.ode4j.ode.DContactJoint
DContactBuffer = org.ode4j.ode.DContactBuffer
OdeConstants = org.ode4j.ode.OdeConstants

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

#OdeHelper.initODE
OdeHelper.initODE2(0)

@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)

# sim-loop
500.times do |i|

  @space.collide(nil, Proc.new{|data,o1,o2|
      nearCallBack(data,o1,o2)
  })
#  @world.step(0.02)
  @world.quickStep(0.02)
  @cgroup.empty

  pos = ball_body.getPosition.toFloatArray
  puts "#{'%4d'%i}: pos - #{pos[0]} #{pos[1]} #{pos[2]}"

end

collideのnearCallbackの部分はdemoを参考に作成してみました。
いろいろとハマりましたがもとのODEがわかっていればそれほど難しくはなかったです。

JRubyとODEの組み合わせって興味あるひとどのくらいいるのだろうか。

続く