root/lang/ruby/pitagora/trunk/ode_ruby/demo/test2.rb @ 14282

Revision 14282, 17.9 kB (checked in by takeru, 5 years ago)

demo version

Line 
1require "pp"
2require "ode_cpp"
3require "thread"
4require 'monitor'
5require 'yaml'
6
7def log(s)
8  puts s
9end
10
11# TODO
12#   視点を複数切り替え
13#   ポインタを拡充
14#   colors
15#   blog / coderepos / 動画
16#   ppmを動画にする方法
17
18
19#--------------------------------------------------------------
20# init
21Thread.abort_on_exception = true
22include Ode_cpp
23
24#--------------------------------------------------------------
25# config
26window_width  = 352*2
27window_height = 228*2
28
29#--------------------------------------------------------------
30# globals, world/space
31$monitor = Monitor.new
32$world = DWorld.new
33$world.setGravity(0.0, 0.0, -9.81)
34$world.setCFM(0.00001)
35$space = DHashSpace.new(nil) #DSimpleSpace.new(nil)
36$contactgroup = DJointGroup.new
37ground = DPlane.new($space.id, 0.0, 0.0, 1.0, 0.0) # ax+by+cz=d
38$objects = []
39$save_num = 0
40
41#p Ode_cpp::dWorldGetContactMaxCorrectingVel($world.id)
42#p Ode_cpp::dWorldGetContactSurfaceLayer($world.id)
43Ode_cpp::dWorldSetContactMaxCorrectingVel($world.id, 1000.0)
44Ode_cpp::dWorldSetContactSurfaceLayer($world.id, 0.001)
45Ode_cpp::dWorldSetAutoDisableAverageSamplesCount($world.id, 10)
46
47# Base/Box/Sphere/Capsule/Cylinder
48require "./demo/objects.rb"
49$pointer_sphere = Sphere.new(0.05, 1)
50
51#----------------------------------------
52$sim_running = false
53def sim_start
54  $sim_running = true
55end
56
57def sim_stop
58  $sim_running = false
59end
60
61def sim_toggle_running
62  if $sim_running
63    sim_stop
64  else
65    sim_start
66  end
67end
68
69def sim_init(fn=nil)
70  sim_stop
71  $objects.each do |o|
72    o.destroy
73  end
74  $objects.clear
75  # $objects << $pointer_sphere
76  srand(1)
77  $sim_step_count = 0
78
79  if fn
80    load_from_yaml(fn)
81  else
82    create_walls
83    create_slope1
84    create_capsule_cylinder
85    create_pins
86    #ray = Ray.new(1)
87    #ray.position = [0, -4, 5]
88
89    # a box
90    box = Box.new([2,2,2], 1);
91    box.position = [-20.0, 0.0, 1.0]
92    box.color    = [1.0, 0.0, 1.0]
93
94    #sphere = Sphere.new(3.0, 1.0)
95    #sphere.position = [5.0, 5.0, 30.0]
96    #sphere.color    = [1.0, 1.0, 0.0]
97  end
98end
99
100
101def save_to_yaml
102  yaml_obj = $objects.collect do |obj|
103    { :class=>obj.class.name,
104      :pars=>obj.pars,
105      :color=>obj.color,
106      :position=>obj.position,
107      :rotation=>obj.rotation }
108  end
109  yaml_str = YAML.dump(yaml_obj)
110  # puts yaml_str
111  loop{
112    $save_num += 1
113    fn = "save/state_#{$save_num}.yaml"
114    next if File.exists?(fn)
115    open(fn, 'w'){|f| f.write(yaml_str) }
116    puts "SAVED to #{fn}"
117    break
118  }
119end
120
121def load_from_yaml(fn)
122  yaml_obj = YAML.load_file(fn)
123  yaml_obj.each do |hash|
124    klass = eval(hash[:class])
125    obj = klass.new(*hash[:pars])
126    obj.color = hash[:color]
127    obj.position = hash[:position]
128    # obj.rotation = hash[:rotation] # TODO!!
129  end
130end
131
132#---------------------------------------
133def create_walls
134  w = 40.0
135  h =  4.0
136  d =  5.0
137
138  # red
139  wall = Box.new([w, d, h], 0.1)
140  wall.position = [0.0, w*0.6, h/2]
141  wall.color = [1.7, 0.0, 0.0]
142
143  # green
144  wall = Box.new([w, d, h], 0.1)
145  wall.position = [0.0, -w*0.6, h/2]
146  wall.color = [0.0, 1.7, 0.0]
147
148  # blue
149  wall = Box.new([d, w, h], 0.1)
150  wall.position = [w*0.6, 0.0, h/2]
151  wall.color = [0.0, 0.0, 1.7]
152
153  # yellow
154  wall = Box.new([d, w, h], 0.1)
155  wall.position = [-w*0.6, 0.0, h/2]
156  wall.color = [1.0, 1.0, 0.0]
157end
158create_walls
159
160# 坂道
161def create_slope1
162  b = Box.new([2,10,3], 1);
163  b.position = [15, 0, 1]
164  b.color = [0.7, 0.5, 0]
165  slope = Box.new([17, 10, 0.2], 0.1);
166  slope.position = [7, 0, 3.2]
167  slope.color = [1.0, 0.7, 0]
168end
169
170def create_capsule_cylinder
171  c = Capsule.new(1, 3, 1) # (radius, length, density, direction[1,2,3])
172  c.position = [10, -2-10, 5]
173  c.color = [1.0, 0.5, 0.5]
174
175  c = Cylinder.new(0.5, 3, 1) # (radius, length, density, direction[1,2,3])
176  c.position = [10, 2-10, 5]
177  c.color = [0.0, 0.7, 1.0]
178end
179
180def create_pins
181  (1..4).each do |n|
182    (1..n).each do |m|
183      x = n*1.5
184      y = (n/2.0-m)*1.5
185      #c = Cylinder.new(0.2, 4, 0.05) # (radius, length, density, direction[1,2,3])
186      c = Box.new([0.5,0.5,4], 0.01)
187      c.position = [-3-x, y, 2]
188      c.color = [0.0, 0.7, 1.0]
189    end
190  end
191end
192
193def rand_within(from, to)
194  rand*(to-from)+from
195end
196
197def create_many_objects
198  50.times{
199    sp = Sphere.new(rand_within(1,5), 1)
200    sp.position = [rand_within(-40,40), rand_within(-40,40), rand_within(100,150)]
201    sp.color = [rand(2).to_f, rand(2).to_f, rand(2).to_f]
202  }
203  #10.times{
204  #  bx = Box.new([rand_within(1,5), rand_within(1,5), rand_within(1,5)], 1)
205  #  bx.position = [rand_within(-40,40), rand_within(-40,40), rand_within(15,100)]
206  #  bx.color = [rand(2).to_f, rand(2).to_f, rand(2).to_f]
207  #}
208end
209
210#box = Box.new([10.0, 50.0, 0.1], 1.0)
211#box.position = [0.0, 0.0, 0.0]
212#box.color    = [1.5, 1.5, 0.0]
213
214
215require "draw_stuff"
216path_to_textures = File.join(File.dirname(__FILE__), "../../ode-0.9_fix/drawstuff/textures")
217ds = DrawStuff.new(:world=>$world,
218                   :width  =>window_width,
219                   :height =>window_height,
220                   :path_to_textures=>path_to_textures)
221
222ds.instance_eval do
223  def start
224    log "start"
225    @cmd_proc = nil
226    @draw_count = 0
227    @normal_steps_per_sec = 200
228    @steps_per_sec = @normal_steps_per_sec
229    @normal_draws_per_sec = 60
230    @draws_per_sec = @normal_draws_per_sec
231    @process_wii_remo_mode = :move
232
233    #log "init viewpoint=%s" % self.viewpoint.inspect
234    #self.set_viewpoint([2,0,1], [180,0,0])
235    # init viewpoint=[[2.0, 0.0, 1.0], [180.0, 0.0, 0.0]]
236
237    #xyz = [  50.0,   0.0,   1.0] # 視点の位置
238    #hpr = [-180.0,   0.0,   0.0] # 視線の方向
239    xyz = [18.7891311645508, 10.3102083206177, 4.46602010726929]
240    hpr = [-134.198913574219, -13.0999450683594, 0.0]
241    self.set_viewpoint(xyz, hpr)
242  end
243 
244  def _collide(o1, o2)
245    # $spaceに属する物体で衝突する可能性のある組それぞれで呼ばれる
246    # log "cb #{o1._ptr} #{o2._ptr} #{$ground._ptr} #{$ground._ptr==o1._ptr} #{$ground._ptr==o2._ptr}"
247
248    ps_ptr = $pointer_sphere.geom.id._ptr
249    if o1._ptr==ps_ptr || o2._ptr==ps_ptr
250      return
251    end
252    #if $objects.last.geom.id._ptr == o1._ptr
253    #  log "geom_id = %d" % $objects.last.geom.id._ptr
254    #  log "o1        %d" % o1._ptr
255    #end
256    #if $objects.last.geom.id._ptr == o2._ptr
257    #  log "geom_id = %d" % $objects.last.geom.id._ptr
258    #  log "o2        %d" % o2._ptr
259    #end
260
261    b1 = dGeomGetBody(o1)
262    b2 = dGeomGetBody(o2)
263    if b1 && b2 && 0<Ode_cpp::dAreConnectedExcluding(b1, b2, Ode_cpp::DJointTypeContact)
264      return
265    end
266
267    # 衝突検出
268    contact_geoms = Ode_cpp::dCollide(o1, o2, 5) # 衝突情報の生成
269    contact_geoms.each do |cg|
270      # pp cg
271      contact = DContact.new
272      contact.geom = cg
273      contact.surface.mode       = DContactBounce|DContactSoftCFM
274      contact.surface.mu         = Float::MAX
275      contact.surface.mu2        = 0.0
276      contact.surface.bounce     = 0.1
277      contact.surface.bounce_vel = 0.1
278      contact.surface.soft_cfm   = 0.01
279      # 接触ジョイントの生成
280      #joint = Ode_c.dJointCreateContact($world._id, $contactgroup, contact)
281      joint = DContactJoint.new($world.id, $contactgroup.id, contact)
282      # 接触している2つの剛体を接触ジョイントにより拘束
283      #Ode_c.dJointAttach(joint, Ode_c.dGeomGetBody(cg.g1), Ode_c.dGeomGetBody(cg.g2))
284      joint.attach(Ode_cpp::dGeomGetBody(cg.g1), Ode_cpp::dGeomGetBody(cg.g2))
285    end
286  end
287
288  def step(pause)
289    $monitor.synchronize{
290      self._step_without_lock(pause)
291    }
292  end
293
294  def _step_without_lock(pause)
295    3.times do
296      break unless $sim_running
297      $space.collide(Proc.new{|o1,o2|
298        _collide(o1, o2)
299      });
300      # @world.step(0.01)
301      @world.stepFast1(0.01, 1)
302      # @world.stepFast1(0.01, 1)
303      #Ode_cpp::dJointGroupEmpty($contactgroup) # ジョイントグループを空にする
304      $contactgroup.empty
305     
306      # @steps_per_sec
307      _step = 100
308      if $sim_step_count%_step==0
309        if @step_per_sec_start_time
310          sec = Time.now-@step_per_sec_start_time
311          @steps_per_sec = _step/sec
312          log "%.2f step/sec." % @steps_per_sec
313        end
314        @step_per_sec_start_time = Time.now
315      end
316      $sim_step_count += 1
317    end
318   
319    # @steps_per_sec
320    _step = 100
321    if @draw_count%_step==0
322      if @draws_per_sec_start_time
323        sec = Time.now-@draws_per_sec_start_time
324        @draws_per_sec = _step/sec
325        log "%.2f draw/sec." % @draws_per_sec
326      end
327      @draws_per_sec_start_time = Time.now
328    end
329    @draw_count += 1
330   
331    # 描画
332    $objects.each do |obj|
333      draw = true
334      if obj==@selected_object && (@draw_count/20)%3==0
335        draw = false
336      end
337      if draw
338        obj.draw
339      end
340    end
341
342    # ポインタ位置計算
343    # @pointer = [0.0,0.0,3.0]
344    @pointer, hpr = self.viewpoint
345    fwd = 8
346    @pointer[0] += Math.cos(hpr[0]*DEG_TO_RAD)*fwd
347    @pointer[1] += Math.sin(hpr[0]*DEG_TO_RAD)*fwd
348    $pointer_sphere.position = @pointer
349   
350    # ポインタ描画
351    [0,1,2].each do |n|
352      if @process_wii_remo_mode==:move
353        if @draw_count/10%3!=n
354          next
355        end
356      end
357      len = 0.5
358      p_start = @pointer.dup
359      p_start[n] -= len
360      p_end   = @pointer.dup
361      p_end[n]   += len
362      color = [0.0, 0.0, 0.0]
363      color[n] = 1.3
364      DrawStuff::DSLib.dsSetColor(*color)
365      DrawStuff::DSLib.dsDrawLineD(p_start, p_end)
366    end
367   
368    # cmd_proc
369    if @cmd_proc
370      @cmd_proc.call
371      @cmd_proc = nil
372    end
373   
374    process_wii_remo
375  end
376
377  def command(cmd)
378    cmd = cmd.chr if cmd.kind_of?(Fixnum)
379    log "[command #{cmd}]"
380    case cmd
381    when 'v'
382      @cmd_proc = proc{
383        xyz, hpr = self.viewpoint
384        log "xyz=%s hpr=%s" % [xyz.inspect, hpr.inspect]
385      }
386    when 'o'
387      @cmd_proc = proc{
388        obj_types = Hash.new(0)
389        ObjectSpace.each_object do |obj|
390          obj_types[obj.class] += 1
391        end
392        obj_types.to_a.sort{|a,b| b[1]-a[1] }.each do |pair|
393          log "%30s : %6d" % pair #if 10<=pair[1]
394        end
395      }
396    when '\\' # start/stop
397      sim_toggle_running
398    when '^' # init
399      sim_init
400    when '_' # save
401      save_to_yaml
402    end
403   
404    # viatual Wii remo
405    if @process_wii_remo_mode==:move
406      case cmd
407      when 'a','s','w','z','q','e','d','x'
408        mode = nil
409        dx,dy = 0,0
410        case cmd
411        when 'w','d' : dy =  20
412        when 'z','x' : dy = -20
413        when 'a','q' : dx =   6
414        when 's','e' : dx =  -6
415        end
416        case cmd
417        when 'q','e','d','x'
418          mode = :z
419        end
420        self.motion(mode, dx, dy)
421        # todo
422        #   accXY
423        #   modeでわける
424      end
425    end
426
427    # mode command
428    case @process_wii_remo_mode
429    when :move
430      obj = nil
431      case cmd
432      when 'b': obj = Box.new([1,1,1], 1)
433      when 'p': obj = Sphere.new(0.5, 1)
434      when 'c': obj = Capsule.new(0.5, 1, 1)
435      when 'y': obj = Cylinder.new(0.5, 1, 1)
436      when ' '
437        @selected_object = _select_object
438        if @selected_object
439          _change_process_wii_remo_mode(:edit)
440        end
441      end
442      if obj
443        obj.position = @pointer
444        unless $sim_running
445          @selected_object = obj
446          _change_process_wii_remo_mode(:edit)
447        end
448      end
449    when :edit
450      case cmd
451      when 'r' # remove object
452        $objects.delete(@selected_object)
453        _change_process_wii_remo_mode(:move)
454      when ' '
455        _change_process_wii_remo_mode(:move)
456      when 'm','n' # increace/reduce  density
457        log "TODO no impl"
458      when '1','2','3','4','5','6','7','8','9','0' # color
459        log "TODO no impl"
460      end
461    end
462  end
463
464  def process_wii_remo
465    return unless @wii_remote_client
466    case @process_wii_remo_mode
467    when :move
468      process_wii_remo__move
469    when :edit
470      process_wii_remo__edit
471    end
472  end
473
474  def process_wii_remo__move
475    buttons = @wii_remote_client.buttons_by_chars
476
477    # ヌンチャク
478    if @wii_remote_client.nuncyaku_joystick
479      x, y = @wii_remote_client.nuncyaku_joystick
480      dx = -((x-128)/16.0).truncate
481      dy =  ((y-128)/16.0).truncate
482      mode = nil
483      mode = :z if buttons.include?('z')
484      mode = :c if buttons.include?('c')
485      self.motion(mode, dx, dy)
486    end
487
488    # ボタン(エッジ)
489    pushed = _wii_button_pushed
490    unless pushed.empty?
491      if pushed.include?('A')
492        command(' ')
493        return
494      end
495      if pushed.include?('H')
496        if buttons.include?('B')
497          sim_init
498        else
499          sim_toggle_running
500        end
501        return
502      end
503      if pushed.include?('u'); command('b') end
504      if pushed.include?('d'); command('p') end
505      if pushed.include?('l'); command('c') end
506      if pushed.include?('r'); command('y') end
507    end
508  end
509
510  # pointerにぶつかっているobjectを返す
511  def _select_object
512    selected_object = nil
513    $objects.each do |obj|
514      contact_geoms = Ode_cpp::dCollide($pointer_sphere.geom.id, obj.geom.id, 1)
515      unless contact_geoms.empty?
516        return obj
517      end
518    end
519    return nil
520  end
521
522  def process_wii_remo__edit
523    buttons = @wii_remote_client.buttons_by_chars
524
525    # ボタン(エッジ)
526    pushed = _wii_button_pushed
527    unless pushed.empty?
528      if pushed.include?('A')
529        command(' ')
530        return
531      end
532    end
533
534    # ヌンチャク
535    dx,dy = 0,0
536    if @wii_remote_client.nuncyaku_joystick
537      x, y = @wii_remote_client.nuncyaku_joystick
538      dx = -((x-128)/16.0).truncate
539      dy =  ((y-128)/16.0).truncate
540
541#      mode = nil
542#      mode = :z if buttons.include?('z')
543#      mode = :c if buttons.include?('c')
544#      self.motion(mode, dx, dy)
545    end
546
547    # ボタン(連続)
548    if !buttons.empty? || dx!=0 || dy!=0
549      obj = @selected_object
550      delta = 0.01
551     
552      # 移動
553      m = false
554      position = obj.position
555      if dy!=0;                 position[0]-= delta*dy/2; m=true; end
556      if dx!=0;                 position[1]-= delta*dx/2; m=true; end # TODO方向を視点からの方向で
557      if buttons.include?('c'); position[2]+= delta;      m=true; end
558      if buttons.include?('z'); position[2]-= delta;      m=true; end
559      position[2] = 0 if position[2]<0
560      obj.position = position if m
561
562      m = false
563      case obj
564      when Box
565        lengths, density = obj.pars
566        if buttons.include?('u'); lengths[0] += delta; m=true; end
567        if buttons.include?('d'); lengths[0] -= delta; m=true; end
568        if buttons.include?('r'); lengths[1] += delta; m=true; end
569        if buttons.include?('l'); lengths[1] -= delta; m=true; end
570        if buttons.include?('1'); lengths[2] += delta; m=true; end
571        if buttons.include?('2'); lengths[2] -= delta; m=true; end
572        [0,1,2].each{|n| lengths[n] = 0.1 if lengths[n]<0.1 }
573        obj.pars = [lengths, density] if m
574      when Sphere
575        radius, density = obj.pars
576        if buttons.include?('r'); radius += delta; m=true; end
577        if buttons.include?('l'); radius -= delta; m=true; end
578        radius = 0.1 if radius<0.1
579        obj.pars = [radius, density] if m
580      when Capsule,Cylinder
581        radius, length, density = obj.pars
582        if buttons.include?('r'); radius += delta; m=true; end
583        if buttons.include?('l'); radius -= delta; m=true; end
584        if buttons.include?('u'); length += delta; m=true; end
585        if buttons.include?('d'); length -= delta; m=true; end
586        radius = 0.1 if radius<0.1
587        length = 0.1 if length<0.1
588        obj.pars = [radius, length, density] if m
589      end
590    end
591  end
592
593  def _wii_button_pushed
594    buttons = @wii_remote_client.buttons_by_chars
595    @pre_buttons ||= []
596    pushed = buttons-@pre_buttons
597    @pre_buttons = buttons
598    return pushed
599  end
600
601  def _change_process_wii_remo_mode(mode)
602    case mode
603    when :move
604      @selected_object = nil
605      @process_wii_remo_mode = :move
606      log "MOVE mode!!"
607    when :edit
608      @process_wii_remo_mode = :edit
609      log "EDIT mode!!"
610    end
611  end
612 
613  def stop
614    log "stop"
615  end
616
617  def wii_remote_client=(c) @wii_remote_client = c; end
618  DEG_TO_RAD = Math::PI/180.0
619  #// call this to update the current camera position. the bits in `mode' say
620  #// if the left (1), middle (2) or right (4) mouse button is pressed, and
621  #// (deltax,deltay) is the amount by which the mouse pointer has moved.
622  def motion__0(mode, dx, dy)
623    return if dx==0 && dy==0
624    xyz, hpr = self.viewpoint
625
626    side = 0.01*dx
627    fwd = (mode==4) ? (0.01*dy) : 0.0
628    s = Math.sin(hpr[0]*DEG_TO_RAD)
629    c = Math.cos(hpr[0]*DEG_TO_RAD)
630    if mode==1
631      hpr[0] += dx*0.5
632      hpr[1] += dy*0.5
633    else
634      xyz[0] += -s*side + c*fwd
635      xyz[1] +=  c*side + s*fwd
636      if mode==2 || mode==5
637        xyz[2] += 0.01*dy
638      end
639    end
640    self.set_viewpoint(xyz, hpr)
641  end
642
643  def motion(mode, dx, dy)
644    return if dx==0 && dy==0
645    rate = 1.0*@normal_draws_per_sec/@draws_per_sec
646    dx *= rate
647    dy *= rate
648
649    xyz, hpr = self.viewpoint
650    s = Math.sin(hpr[0]*DEG_TO_RAD)
651    c = Math.cos(hpr[0]*DEG_TO_RAD)
652    if mode==nil
653      # 前後移動/左右回転
654      fwd  = 0.01*dy
655      #dx *= -1 if dy<0
656      xyz[0] += c*fwd
657      xyz[1] += s*fwd
658      hpr[0] += dx*0.5
659    elsif mode==:z
660      # 左右/高さ移動
661      side = 0.01*dx
662      xyz[0] += -s*side
663      xyz[1] +=  c*side
664      xyz[2] += 0.01*dy
665      xyz[2] = 0.0 if xyz[2]<0
666    elsif mode==:c
667      # 視線方向
668      hpr[0] += dx*0.5
669      hpr[1] += dy*0.5
670    else
671    end
672    self.set_viewpoint(xyz, hpr)
673  end
674end # ds.instance_eval
675
676opts = {:irb=>true, :wii=>true}
677
678if opts[:wii]
679  require "wii_remo"
680  wii_remo = WiiRemo.instance
681  wii_remo.callback = proc{|pars|
682    case pars[:type]
683    when :discovered
684      r = pars[:remote]
685      client = WiiRemoSimpleClient.new
686      r.bind(client)
687      ds.wii_remote_client = client
688      log client.inspect
689      wii_remo.stop_search
690    when :error
691      code = pars[:code]
692      log "ERROR %x" % code
693    else
694      log "ELSE #{pars.inspect}"
695    end
696  }
697  wii_remo.start_search
698  log "Start WiiRemote SEARCH...."
699end
700
701fn = ARGV.shift
702log "ARGV=#{ARGV}"
703
704threads = []
705if opts[:irb]
706  threads << Thread.start{
707    require 'irb'
708    IRB.start
709    log "irb EXIT."
710  }
711end
712
713sim_init(fn)
714#sim_start
715ds.run
716threads.each{|t| t.join }
717
Note: See TracBrowser for help on using the browser.