JRubyでODEをはじめてみる
物理計算エンジンOpen Dynamics Engine (ODE) をRubyでやりたい!という前々から願望があり、いろいろ調べているうちにJRubyならどうだろうということで試してみました。
ODEはC/C++のライブラリですが、探してみるとJava版も存在しているようです。
Java版ODE: ode4j
http://www.ode4j.org
ode4jはメンテナンスが行き届いているようでちゃんとこの時点で最新であるODE-0.12.0に対応しています。
また、APIもJava版にアレンジされていますがオリジナルよりももしかするときれいにまとまっていて扱いやすそうです。
で、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の組み合わせって興味あるひとどのくらいいるのだろうか。