Ruby-FFIでODEを動かす実験2

ODE付属のdrawstuffもFFIで動かしてみました。

試した環境は、

linuxでのコンパイルの場合、drawstuffの動的ライブラリは通常は生成されないので、工夫が必要です。


require 'ffi'

module DrawStuff
  extend FFI::Library
  ffi_lib '/usr/local/lib/libdrawstuff.so'
#  ffi_lib 'C:/opt/ode-1.12/lib/ReleaseDoubleDLL/drawstuff.dll'

  callback :start_callback, [], :void
  callback :step_callback, [:int], :void
  callback :command_callback, [:int], :void
  callback :stop_callback, [], :void

  class DsFunctions < FFI::Struct
    layout :version, :int,
           :start, :start_callback,
           :step, :step_callback,
           :command, :command_callback,
           :stop, :stop_callback,
           :path_to_textures, :string
  end

  attach_function :dsSimulationLoop, [:int, :pointer, :int, :int, :pointer], :void
  attach_function :dsPrint, [:string], :void
  attach_function :dsSetViewpoint, [:pointer, :pointer], :void
  attach_function :dsSetColor, [:float, :float, :float], :void

  attach_function :dsDrawSphereD, [:pointer, :pointer, :float], :void
#  attach_function :dsDrawSphere, [:pointer, :pointer, :float], :void

end
$:.unshift File.dirname(__FILE__)
require 'ode'
require 'draw_stuff'


@start = Proc.new do
  puts "start"
#  DrawStuff.dsSetViewpoint(xyz, hpr)
end

@step = Proc.new do |pause|
  break unless pause == 0

  ODE.dSpaceCollide(@space, nil, @nearCallback)
  ODE.dWorldStep(@world, 0.01)
  ODE.dJointGroupEmpty(@contactgroup)

  DrawStuff.dsSetColor(1.0, 0.0, 0.0)
  pos = ODE.dBodyGetPosition(@sphere_body)
  rot = ODE.dBodyGetRotation(@sphere_body)
  DrawStuff.dsDrawSphereD( pos, rot, 1.0 )

  pos_p = ODE.dBodyGetPosition(@sphere_body)
  pos = ODE::DVector3.new(pos_p)
  puts "pos x=#{pos[:x]}, y=#{pos[:y]}, z=#{pos[:z]}"
end

@command = Proc.new do |cmd|
  puts "command #{cmd}"
end

@stop = Proc.new do
  puts "stop"
end


ODE.dInitODE
@world = ODE.dWorldCreate
@space = ODE.dSimpleSpaceCreate(nil)
@contactgroup = ODE.dJointGroupCreate(nil)
ODE.dCreatePlane(@space, 0.0, 0.0, 1.0, 0)
ODE.dWorldSetGravity(@world, 0.0, 0.0, -9.8)

@sphere_body = ODE.dBodyCreate(@world)
ODE.dBodySetPosition(@sphere_body, 0.0, 0.0, 10.0)

@sphere_geom = ODE.dCreateSphere(@space, 1.0)
ODE.dGeomSetBody(@sphere_geom, @sphere_body)

@nearCallback = Proc.new do |data_p, g1, g2|
  b1 = ODE.dGeomGetBody( g1 )
  b2 = ODE.dGeomGetBody( g2 )
#  break if b1 and b2 and ODE.dAreConnectedExcluding(b1, b2, 4) # 誤り
  return if !b1.null? and !b2.null? and ODE.dAreConnectedExcluding(b1, b2, 4) == 1
  contacts = []
  4.times do 
    c = ODE::DContact.new
    c[:surface][:mode] = 0x004 | 0x010
    c[:surface][:mu] = 10000.0
    c[:surface][:bounce] = 0.1
    c[:surface][:soft_cfm] = 0.01
    contacts << c
  end
  numc = ODE.dCollide(g1, g2, 4, contacts[0][:geom], ODE::DContact.size)
  if numc > 0
    numc.times do |i|
      c = ODE.dJointCreateContact(@world, @contactgroup, contacts[i])
      ODE.dJointAttach(c, b1, b2)
    end
  end
end


# drawstuff

@args = FFI::MemoryPointer.new(:pointer, 0) #[$0]+ARGV
@width = 320
@height = 240

fn = DrawStuff::DsFunctions.new
fn[:version]  = 2
fn[:start]    = @start
fn[:step]     = @step
fn[:command]  = @command
fn[:stop]     = @stop
fn[:path_to_textures] = "./textures"  #CRuby@linuxだとうまく読めない。。。
DrawStuff.dsSimulationLoop(@args.size, @args, @width, @height, fn)

動いてくれると、感動しますネ。

はまったこと

  • :doubleと:floatを間違えると値が渡らなくてSphereradius=0.0になってうまく表示されなかった
  • fn[:path_to_textures]、なぜかlinuxでエラーが起こる(解決していない)
  • linuxで.soファイル生成時、-lGL,-lGLUを付け忘れて実行時エラーになった

メモ

配列をCのfloat[]に変換するにはどうすればよいでしょうか。
dsSetViewpointで試して動いた方法をメモします。

  xyz = [   3.0, 5.0, 1.0].pack('f*')
  hpr = [-120.0, 0.0, 0.0].pack('f*')
  DrawStuff.dsSetViewpoint(xyz, hpr)