root / trunk / pysoy / src / scenes / Scene.pym

Revision 1279, 10.7 kB (checked in by ArcRiley, 7 weeks ago)

Ticket #956 :

  • basic move finished with Mesh and Shape
  • _render removed from Body, Scene now calls _body._model._render()
  • Property svn:keywords set to Id
Line 
1# PySoy's scenes.Scene class
2#
3# Copyright (C) 2006,2007,2008 PySoy Group
4#
5#  This program is free software; you can redistribute it and/or modify
6#  it under the terms of the GNU Affero General Public License as published
7#  by the Free Software Foundation, either version 3 of the License, or
8#  (at your option) any later version.
9#
10#  This program is distributed in the hope that it will be useful,
11#  but WITHOUT ANY WARRANTY; without even the implied warranty of
12#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13#  GNU Affero General Public License for more details.
14#
15#  You should have received a copy of the GNU Affero General Public License
16#  along with this program; if not, see http://www.gnu.org/licenses
17#
18# $Id$
19DEF GeomScene = 1
20DEF GeomBody  = 2
21DEF GeomField = 4
22DEF GeomLight = 8
23DEF GeomGhost = 16
24cdef class Scene (soy._internals.Loopable) :
25  '''PySoy Scene
26
27    Scenes are containers in physics space for bodies, joints, and shapes.
28    Objects in different worlds cannot interact, for example, bodies in two
29    different worlds cannot collide.  Worlds also apply gravity to bodies.
30  '''
31
32  ############################################################################
33  #
34  # Python functions
35  #
36 
37  def __cinit__(self, *args, **kw) :
38    from soy.colors import gray
39    self._worldID      = ode.dWorldCreate()
40    self._spaceID      = ode.dSimpleSpaceCreate(NULL)
41    self._contactGroup = ode.dJointGroupCreate(0)
42    self._bodies       = soy._internals.Children()
43    self._fields       = soy._internals.Children()
44    self._joints       = soy._internals.Children()
45    self._lights       = soy._internals.Children()
46    self._ambient      = gray
47    self._stepSize     = 0.01
48    self._friction     = 0
49    self._prevTime     = _time()
50    self._stepMutex    = py.PyThread_allocate_lock()
51    # a list cor keeping track of the fields in the scene.
52    self._giveFields   = soy._internals.PointerSet()
53    #
54    self._callFields   = soy._internals.PointerSet()
55    _scenes._append(<void*> self)
56
57
58  def __dealloc__(self) :
59    _scenes._remove(<void*> self)
60    if self._worldID != NULL :
61      ode.dWorldDestroy(self._worldID)
62    if self._spaceID != NULL :
63      ode.dSpaceDestroy(self._spaceID)
64
65
66  def __repr__(self) :
67    report = []
68
69    if self._bodies._current == 1 :
70      report.append('1 body')
71    else:
72      report.append('%d bodies' % self._bodies._current)
73
74    if self._lights._current == 1 :
75      report.append('1 light')
76    else :
77      report.append('%d lights' % self._lights._current)
78
79    return '<Scene with %s>' % ', '.join(report)
80
81  ############################################################################
82  #
83  # C functions
84  #
85 
86  cdef int _loop(self) nogil :
87    cdef int _i, _steps
88    self._time = _time()
89    self._callFields._empty()
90    self._giveFields._empty()
91    #
92    self._fields._iterStart()
93    for _i from 0 <= _i < self._fields._current :
94      # Make sure every field is in givefields & _give each one
95      if not self._giveFields._has_key(self._fields._list[_i]) :
96        (<soy.fields.Field> self._fields._list[_i])._give(0)
97        self._giveFields._insert(self._fields._list[_i])
98    #
99    for _i from 0 <= _i < self._fields._current :
100      # Apply fields; add incompletly applied fields to the list
101      if not (<soy.fields.Field> self._fields._list[_i])._apply() :
102        pass
103        #self._callFields._insert(self._fields.list[_i])
104    #
105    # Apply any outstanding fields
106    self._callFields._foreach(_runField, NULL)
107    self._fields._iterDone()
108    #
109    self._stepLock()
110    _steps = self._steps()
111    for _i from 0 <= _i < _steps :
112      self._time = _time()
113      self._giveFields._foreach(_prerunField, NULL)
114      self._callFields._empty()
115      ode.dSpaceCollide(self._spaceID, <void*> self,
116                        <void(*)(void*, ode.dGeomID, ode.dGeomID) nogil>
117                        Scene._callback)
118      ode.dWorldQuickStep(self._worldID, self._stepSize)
119      ode.dJointGroupEmpty(self._contactGroup)
120      if _i != 0 :
121        self._callFields._foreach(_runField, NULL)
122    self._stepUnLock()
123    return 1
124
125
126  cdef void _render(self) :
127    cdef int _i
128    #
129    # Setup scene-level rendering
130    gl.glClear(gl.GL_DEPTH_BUFFER_BIT)
131    gl.glEnable(gl.GL_DEPTH_TEST)
132    gl.glEnable(gl.GL_LIGHTING)
133    gl.glLightModelfv(gl.GL_LIGHT_MODEL_AMBIENT, self._ambient._rgba)
134    #
135    # Turn on each light, keep it's iteration locked against mod until done
136    self._lights._iterStart()
137    for _i from 0 <= _i < self._lights._current :
138      # This is a quick hack (gl.GL_LIGHT0 + _i)
139      (<soy.bodies.Light> self._lights._list[_i])._on(gl.GL_LIGHT0 + _i)
140    #
141    # Iterate over bodies
142    self._bodies._iterStart()
143    for _i from 0 <= _i < self._bodies._current :
144      if (<soy.bodies.Body> self._bodies._list[_i])._model is not None :
145        (<soy.models.Model>
146         (<soy.bodies.Body> self._bodies._list[_i])._model)._render(
147         (<soy.bodies.Body> self._bodies._list[_i]))
148    self._bodies._iterDone()
149    #
150    # Turn off all lights and finish iteration loop
151    for _i from 0 <= _i < self._lights._current :
152      # This is a quick hack (gl.GL_LIGHT0 + _i)
153      (<soy.bodies.Light> self._lights._list[_i])._off(gl.GL_LIGHT0 + _i)
154    self._lights._iterDone()
155    gl.glDisable(gl.GL_LIGHTING)
156    gl.glDisable(gl.GL_DEPTH_TEST)
157
158
159  cdef int _steps(self) nogil :
160    cdef int     _step
161    cdef double  _lapsTime
162    _lapsTime = _time() - self._prevTime
163    if _lapsTime < ode.dEpsilon :
164      _lapsTime = ode.dEpsilon
165    _step = math.lround(_lapsTime / self._stepSize)
166    if _step > 12 :
167      _step = 12
168    self._prevTime = self._prevTime + (_step * self._stepSize)
169    return _step
170   
171
172  cdef void _stepLock(self) nogil :
173    py.PyThread_acquire_lock(self._stepMutex,1)
174
175
176  cdef int _stepTryLock(self) nogil :
177    return py.PyThread_acquire_lock(self._stepMutex,0)
178
179
180  cdef void _stepUnLock(self) nogil :
181    py.PyThread_release_lock(self._stepMutex)
182
183
184  cdef void _callback(self, ode.dGeomID _geomA, ode.dGeomID _geomB) nogil :
185    cdef int                _contactGeoms, _f, _i
186    cdef ode.dJointID       _joint
187    cdef ode.dContact       _contact
188    cdef ode.dContactGeom   _contactGeom[2]
189    cdef ode.dGeomID        _geomIDs[2]
190    cdef ode.dBodyID        _bodyIDs[2]
191
192    #
193    # Start by generating up to four contacts between 2 bodies
194    _contactGeoms = ode.dCollide(_geomA, _geomB, 2,
195                                 _contactGeom, sizeof(_contactGeom[0]))
196    #
197    # Don't waste time when there's no collision
198    if _contactGeoms == 0 :
199      return
200    #
201    # This makes case-testing much simpler in the code
202    _geomIDs[0] = _geomA
203    _geomIDs[1] = _geomB
204    #
205    # Scene Check
206    for _i from 0 <= _i < 2 :
207      if ode.dGeomGetCategoryBits(_geomIDs[_i]) == GeomScene :
208        _bodyIDs[_i] = NULL
209        #stdio.printf('Scene Collision %d\n', _i)
210      else :
211        _bodyIDs[_i] = ode.dGeomGetBody(_geomIDs[_i])
212        #stdio.printf('Body Collision %d %d\n', _i, _bodyIDs[_i])
213    #
214    # Ghost Check
215    for _i from 0 <= _i < 2 :
216      if ode.dGeomGetCategoryBits(_geomIDs[_i]) == GeomGhost :
217        #stdio.printf('Ghost Collision\n')
218        _bodyIDs[(_i + 1) % 2] = NULL
219    #
220    # If we ended up with a null-null collision, just return now
221    if _bodyIDs[0] == NULL and _bodyIDs[1] == NULL :
222      #stdio.printf('Both bodies null, aborting\n')
223      return
224    #
225    # Add each joint to the bodies we just determined
226    for _i from 0 <= _i < _contactGeoms :
227      _contact.geom = _contactGeom[_i]
228      _contact.surface.mode = ode.dContactBounce
229      _contact.surface.mu = self._friction
230      _contact.surface.bounce = 0.8
231      _contact.surface.bounce_vel = 0
232      #stdio.printf('Creating joint\n')
233      _joint = ode.dJointCreateContact(self._worldID, self._contactGroup,
234                                       &_contact)
235      #stdio.printf('Attaching joint\n')
236      ode.dJointAttach(_joint, _bodyIDs[0], _bodyIDs[1])
237      #stdio.printf('Processed contact joint %d\n', _i)
238    return
239
240    '''
241    # This is the old code, mostly for fields.  It needs to be merged into
242    # the above but for now we're just testing Scene and Ghosts
243    if _c > 0 :
244      # Get the Body objects
245      _bo1 = <soy.bodies.Body> ode.dBodyGetData(_b1)
246      _bo2 = <soy.bodies.Body> ode.dBodyGetData(_b2)
247      _f = 0
248      # activate any fields present...
249      if isinstance(_bo1, soy.fields.Field) :
250        _f = 1
251        if not (<soy.fields.Field> _bo1)._exert(_bo2) :
252          self._callFields._insert(<void*> _bo1)
253      if isinstance(_bo2, soy.fields.Field) :
254        _f = 1
255        if not (<soy.fields.Field> _bo2)._exert(_bo1) :
256          self._callFields._insert(<void*> _bo2)
257      # if neither shape is a field
258      if _f == 0 :
259        _ct.geom = _cg
260        # surface parameters should have an exposed interface on soy.shapes.Shape
261        # this code should pull parameters from there.
262        _ct.surface.mode = ode.dContactBounce
263        _ct.surface.bounce = 0.8
264        _ct.surface.bounce_vel = 0
265        _j = ode.dJointCreateContact(self._worldID, self._contactGroup, &_ct)
266        ode.dJointAttach(_j, _b1, _b2)
267    '''
268
269  ############################################################################
270  #
271  # Properties
272  #
273 
274  property gravity :
275    '''Scene gravity
276
277    This is a scene-wide pseudo-force drawing all bodies in a single
278    direction.  This should not be confused with a monopole force or other
279    forces which are often used for gravity in larger scenes.
280
281    Takes a (x,y,z) tuple of numbers, defaults to (0.0, 0.0, 0.0)
282    '''
283    def __get__(self) :
284      cdef ode.dVector3 grav
285      ode.dWorldGetGravity(self._worldID, grav)
286      return (grav[0], grav[1], grav[2])
287    def __set__(self, value) :
288      ode.dWorldSetGravity(self._worldID, value[0], value[1], value[2])
289
290
291  property stepsize :
292    '''Step Size
293
294    This is the fraction of time each "step" calculates for.  Larger steps
295    trade accuracy for speed.
296
297    Takes a float, defaults to .01
298    '''
299    def __get__(self) :
300      return self._stepSize
301    def __set__(self, value) :
302      self._stepLock()
303      self._stepSize = value
304      self._stepUnLock()
305
306
307  property ambient :
308    '''Scene's ambient light
309
310    This is the ambient light for the scene.  When no light is used this is
311    the only light available in the scene - use it well.
312
313    Defaults to soy.colors.gray
314    '''
315    def __get__(self) :
316      return self._ambient
317    def __set__(self, soy.colors.Color value) :
318      self._ambient = value
319
320
321  property friction :
322    '''Scene's Friction
323   
324    This is the amount of friction given to ode's contact joint.
325   
326    Defaults to 0 ( Really Slippery )
327    '''
328    def __get__(self) :
329      return self._friction
330    def __set__(self, value) :
331      if isinstance(value, int):
332        self._friction = value
333      else:
334        raise TypeError("Friction must be an int of some sort")
Note: See TracBrowser for help on using the browser.