Fix for bugs: 1788 (forces) and 1799 (python delattr on game objects)
[blender.git] / source / gameengine / Physics / Sumo / Fuzzics / src / SM_Scene.cpp
1 /**
2  * $Id$
3  * Copyright (C) 2001 NaN Technologies B.V.
4  * The physics scene.
5  *
6  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version. The Blender
12  * Foundation also sells licenses for use in proprietary software under
13  * the Blender License.  See http://www.blender.org/BL/ for information
14  * about this.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  *
25  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
26  * All rights reserved.
27  *
28  * The Original Code is: all of this file.
29  *
30  * Contributor(s): none yet.
31  *
32  * ***** END GPL/BL DUAL LICENSE BLOCK *****
33  */
34
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 #ifdef WIN32
40 #pragma warning(disable : 4786)  // shut off 255 char limit debug template warning
41 #endif
42
43 #include "SM_Scene.h"
44 #include "SM_Object.h"
45 #include "SM_FhObject.h"
46
47 #include "SM_Debug.h"
48
49 #include <algorithm>
50
51 SM_Scene::SM_Scene() : 
52         m_scene(DT_CreateScene()),
53         m_respTable(DT_CreateRespTable()),
54         m_secondaryRespTable(DT_CreateRespTable()),
55         m_fixRespTable(DT_CreateRespTable()),
56         m_forceField(0.0, 0.0, 0.0),
57         m_lastTime(-1.0)
58 {
59         for (int i = 0 ; i < NUM_RESPONSE; i++)
60         {
61                 m_ResponseClass[i] = DT_GenResponseClass(m_respTable);
62                 m_secondaryResponseClass[i] = DT_GenResponseClass(m_secondaryRespTable);
63                 m_fixResponseClass[i] = DT_GenResponseClass(m_fixRespTable);
64         }
65         
66         /* Sensor */
67         DT_AddPairResponse(m_respTable, m_ResponseClass[SENSOR_RESPONSE], m_ResponseClass[SENSOR_RESPONSE], 0, DT_NO_RESPONSE, this);
68         DT_AddPairResponse(m_respTable, m_ResponseClass[SENSOR_RESPONSE], m_ResponseClass[STATIC_RESPONSE], SM_Scene::boing, DT_SIMPLE_RESPONSE, this);
69         DT_AddPairResponse(m_respTable, m_ResponseClass[SENSOR_RESPONSE], m_ResponseClass[OBJECT_RESPONSE], SM_Scene::boing, DT_SIMPLE_RESPONSE, this);
70         DT_AddPairResponse(m_respTable, m_ResponseClass[SENSOR_RESPONSE], m_ResponseClass[FH_RESPONSE], 0, DT_NO_RESPONSE, this);
71         
72         /* Static */
73         DT_AddPairResponse(m_respTable, m_ResponseClass[STATIC_RESPONSE], m_ResponseClass[SENSOR_RESPONSE], SM_Scene::boing, DT_SIMPLE_RESPONSE, this);
74         DT_AddPairResponse(m_respTable, m_ResponseClass[STATIC_RESPONSE], m_ResponseClass[STATIC_RESPONSE], 0, DT_NO_RESPONSE, this);
75         DT_AddPairResponse(m_respTable, m_ResponseClass[STATIC_RESPONSE], m_ResponseClass[OBJECT_RESPONSE], SM_Object::boing, DT_BROAD_RESPONSE, this);
76         DT_AddPairResponse(m_respTable, m_ResponseClass[STATIC_RESPONSE], m_ResponseClass[FH_RESPONSE], SM_FhObject::ray_hit, DT_SIMPLE_RESPONSE, this);
77         
78         /* Object */
79         DT_AddPairResponse(m_respTable, m_ResponseClass[OBJECT_RESPONSE], m_ResponseClass[SENSOR_RESPONSE], SM_Scene::boing, DT_SIMPLE_RESPONSE, this);
80         DT_AddPairResponse(m_respTable, m_ResponseClass[OBJECT_RESPONSE], m_ResponseClass[STATIC_RESPONSE], SM_Object::boing, DT_BROAD_RESPONSE, this);
81         DT_AddPairResponse(m_respTable, m_ResponseClass[OBJECT_RESPONSE], m_ResponseClass[OBJECT_RESPONSE], SM_Object::boing, DT_BROAD_RESPONSE, this);
82         DT_AddPairResponse(m_respTable, m_ResponseClass[OBJECT_RESPONSE], m_ResponseClass[FH_RESPONSE], SM_FhObject::ray_hit, DT_SIMPLE_RESPONSE, this);
83         
84         /* Fh Object */
85         DT_AddPairResponse(m_respTable, m_ResponseClass[FH_RESPONSE], m_ResponseClass[SENSOR_RESPONSE], 0, DT_NO_RESPONSE, this);
86         DT_AddPairResponse(m_respTable, m_ResponseClass[FH_RESPONSE], m_ResponseClass[STATIC_RESPONSE], SM_FhObject::ray_hit, DT_BROAD_RESPONSE, this);
87         DT_AddPairResponse(m_respTable, m_ResponseClass[FH_RESPONSE], m_ResponseClass[OBJECT_RESPONSE], SM_FhObject::ray_hit, DT_BROAD_RESPONSE, this);
88         DT_AddPairResponse(m_respTable, m_ResponseClass[FH_RESPONSE], m_ResponseClass[FH_RESPONSE], 0, DT_NO_RESPONSE, this);
89         
90         /* Object (Fix Pass) */
91         DT_AddPairResponse(m_fixRespTable, m_ResponseClass[OBJECT_RESPONSE], m_ResponseClass[SENSOR_RESPONSE], 0, DT_NO_RESPONSE, this);
92         DT_AddPairResponse(m_fixRespTable, m_ResponseClass[OBJECT_RESPONSE], m_ResponseClass[STATIC_RESPONSE], SM_Object::fix, DT_BROAD_RESPONSE, this);
93         DT_AddPairResponse(m_fixRespTable, m_ResponseClass[OBJECT_RESPONSE], m_ResponseClass[OBJECT_RESPONSE], SM_Object::fix, DT_BROAD_RESPONSE, this);
94         DT_AddPairResponse(m_fixRespTable, m_ResponseClass[OBJECT_RESPONSE], m_ResponseClass[FH_RESPONSE], 0, DT_NO_RESPONSE, this);
95 }
96
97 void SM_Scene::addTouchCallback(int response_class, DT_ResponseCallback callback, void *user)
98 {
99         DT_AddClassResponse(m_secondaryRespTable, m_secondaryResponseClass[response_class], callback, DT_BROAD_RESPONSE, user);
100 }
101
102 void SM_Scene::addSensor(SM_Object& object) 
103 {
104         object.calcXform();
105         m_objectList.push_back(&object);
106         DT_AddObject(m_scene, object.getObjectHandle());
107         DT_SetResponseClass(m_respTable, object.getObjectHandle(), m_ResponseClass[SENSOR_RESPONSE]);
108         DT_SetResponseClass(m_secondaryRespTable, object.getObjectHandle(), m_secondaryResponseClass[SENSOR_RESPONSE]);
109         DT_SetResponseClass(m_fixRespTable, object.getObjectHandle(), m_fixResponseClass[SENSOR_RESPONSE]);
110 }
111
112 void SM_Scene::add(SM_Object& object) {
113         object.calcXform();
114         m_objectList.push_back(&object);
115         DT_AddObject(m_scene, object.getObjectHandle());
116         if (object.isDynamic()) {
117                 DT_SetResponseClass(m_respTable, object.getObjectHandle(), m_ResponseClass[OBJECT_RESPONSE]);
118                 DT_SetResponseClass(m_secondaryRespTable, object.getObjectHandle(), m_secondaryResponseClass[OBJECT_RESPONSE]);
119                 DT_SetResponseClass(m_fixRespTable, object.getObjectHandle(), m_fixResponseClass[OBJECT_RESPONSE]);
120         } else {
121                 DT_SetResponseClass(m_respTable, object.getObjectHandle(), m_ResponseClass[STATIC_RESPONSE]);
122                 DT_SetResponseClass(m_secondaryRespTable, object.getObjectHandle(), m_secondaryResponseClass[STATIC_RESPONSE]);
123                 DT_SetResponseClass(m_fixRespTable, object.getObjectHandle(), m_fixResponseClass[STATIC_RESPONSE]);
124         }       
125
126         SM_FhObject *fh_object = object.getFhObject();
127
128         if (fh_object) {
129                 DT_AddObject(m_scene, fh_object->getObjectHandle());
130                 DT_SetResponseClass(m_respTable, fh_object->getObjectHandle(), m_ResponseClass[FH_RESPONSE]);
131                 DT_SetResponseClass(m_secondaryRespTable, fh_object->getObjectHandle(), m_secondaryResponseClass[FH_RESPONSE]);
132                 DT_SetResponseClass(m_fixRespTable, fh_object->getObjectHandle(), m_fixResponseClass[FH_RESPONSE]);
133         }
134 }       
135
136 void SM_Scene::requestCollisionCallback(SM_Object &object)
137 {
138         DT_SetResponseClass(m_respTable, object.getObjectHandle(), m_ResponseClass[OBJECT_RESPONSE]);
139         DT_SetResponseClass(m_secondaryRespTable, object.getObjectHandle(), m_secondaryResponseClass[OBJECT_RESPONSE]);
140 //      DT_SetResponseClass(m_fixRespTable, object.getObjectHandle(), m_fixResponseClass[OBJECT_RESPONSE]);
141 }
142
143 void SM_Scene::remove(SM_Object& object) {      
144         T_ObjectList::iterator i =
145                 std::find(m_objectList.begin(), m_objectList.end(), &object);
146         if (!(i == m_objectList.end()))
147         {
148                 std::swap(*i, m_objectList.back());
149                 m_objectList.pop_back();
150                 DT_RemoveObject(m_scene, object.getObjectHandle());
151
152                 SM_FhObject *fh_object = object.getFhObject();
153                 
154                 if (fh_object) {
155                         DT_RemoveObject(m_scene, fh_object->getObjectHandle());
156                 }
157         } 
158         else {
159                 // tried to remove an object that is not in the scene
160                 //assert(false);
161         }
162 }
163
164 void SM_Scene::beginFrame()
165 {
166         T_ObjectList::iterator i;
167         // Apply a forcefield (such as gravity)
168         for (i = m_objectList.begin(); i != m_objectList.end(); ++i)
169                 (*i)->applyForceField(m_forceField);
170
171 }
172
173 void SM_Scene::endFrame()
174 {
175         T_ObjectList::iterator i;
176         for (i = m_objectList.begin(); i != m_objectList.end(); ++i)
177                 (*i)->clearForce();
178 }
179
180 bool SM_Scene::proceed(MT_Scalar curtime, MT_Scalar ticrate) {
181         if (m_lastTime < 0.0)
182         {
183                 m_lastTime = curtime;
184                 return false;
185         }
186                 
187         // Divide the timeStep into a number of subsamples of size roughly 
188         // equal to subS (might be a little smaller).
189         MT_Scalar timeStep = curtime - m_lastTime;
190         MT_Scalar subStep;
191         int num_samples;
192         
193         if (ticrate > 0.0)
194         {
195                 subStep = 1.0/ticrate;
196                 num_samples = int(timeStep * ticrate);
197         
198                 if (num_samples > 4)
199                 {
200                         std::cout << "Dropping physics frames! step: " << timeStep << " frames:" << num_samples << std::endl;
201                         num_samples /= 4;
202                         subStep *= 4.0;
203                 }
204         } 
205         else
206         {
207                 // Variable time step. (old update)
208                 // Integrate at least 100 Hz
209                 subStep = timeStep > 0.01 ? 0.01 : timeStep;
210                 num_samples = int(timeStep * 0.01);
211                 if (num_samples < 1)
212                         num_samples = 1;
213         }
214         
215         T_ObjectList::iterator i;
216         
217         // No timestep! (should do a mini update)
218         if (num_samples <= 0)
219         {
220                 // Apply a forcefield (such as gravity)
221 #if 0
222                 for (i = m_objectList.begin(); i != m_objectList.end(); ++i) 
223                 {
224                         //(*i)->applyForceField(m_forceField);
225                         //(*i)->integrateForces(timeStep);
226                         // And second we update the object positions by performing
227                         // an integration step for each object
228                         (*i)->integrateMomentum(timeStep);
229                         //(*i)->clearForce();
230                 }
231 #endif
232                 return false;   
233         }
234         
235         m_lastTime += MT_Scalar(num_samples)*subStep;
236         
237         // Do the integration steps per object.
238         int step;
239         for (step = 0; step != num_samples; ++step) {
240
241                 for (i = m_objectList.begin(); i != m_objectList.end(); ++i) {
242                         // Apply a forcefield (such as gravity)
243                         //(*i)->applyForceField(m_forceField);
244                         //(*i)->setTimeStep(timeStep);
245                         (*i)->integrateForces(subStep);
246                         // And second we update the object positions by performing
247                         // an integration step for each object
248                         (*i)->integrateMomentum(subStep);
249                 }
250                 
251                 // I changed the order of the next 2 statements.
252                 // Originally objects were first integrated with a call
253                 // to proceed(). However if external objects were 
254                 // directly manipulating the velocities etc of physics 
255                 // objects then the physics environment would not be able 
256                 // to react before object positions were updated. --- Laurence.
257
258                 // So now first we let the physics scene respond to 
259                 // new forces, velocities set externally. 
260                 // The collsion and friction impulses are computed here. 
261                 DT_Test(m_scene, m_respTable);
262
263         // clear the user set velocities.
264 #if 0
265         clearObjectCombinedVelocities();
266 #endif
267                 DT_Test(m_scene, m_fixRespTable);
268                 
269                 // Finish this timestep by saving al state information for the next
270                 // timestep and clearing the accumulated forces. 
271                 for (i = m_objectList.begin(); i != m_objectList.end(); ++i) {
272                         (*i)->relax();
273                         (*i)->proceedKinematic(subStep);
274                         (*i)->saveReactionForce(subStep);
275                         //(*i)->clearForce();
276                 }
277         }
278         return true;
279 }
280
281 void SM_Scene::notifyCollision(SM_Object *obj1, SM_Object *obj2)
282 {
283         // For each pair of object that collided, call the corresponding callback.
284         if (m_secondaryRespTable)
285                 DT_CallResponse(m_secondaryRespTable, obj1->getObjectHandle(), obj2->getObjectHandle(), 0);
286 }
287
288
289 SM_Object *SM_Scene::rayTest(void *ignore_client, 
290                                                          const MT_Point3& from, const MT_Point3& to, 
291                                                          MT_Point3& result, MT_Vector3& normal) const {
292 #ifdef SM_DEBUG_RAYCAST
293         std::cout << "ray: { " << from << " } - { " << to << " }" << std::endl; 
294 #endif
295  
296         DT_Vector3 n, dfrom, dto;
297         DT_Scalar param;
298         from.getValue(dfrom);
299         to.getValue(dto);
300         SM_Object *hit_object = (SM_Object *) 
301                 DT_RayCast(m_scene, ignore_client, dfrom, dto, 1., &param, n);
302
303         if (hit_object) {
304                 //result = hit_object->getWorldCoord(from + (to - from)*param);
305                 result = from + (to - from) * param;
306                 normal.setValue(n);
307 #ifdef SM_DEBUG_RAYCAST
308                 std::cout << "ray: { " << from << " } -> { " << to << " }: { " << result 
309                   << " } (" << param << "), normal = { " << normal << " }" << std::endl;
310 #endif
311         }
312
313         return hit_object;
314 }
315
316 void SM_Scene::clearObjectCombinedVelocities() {
317
318         T_ObjectList::iterator i;
319
320         for (i = m_objectList.begin(); i != m_objectList.end(); ++i) {
321
322                 (*i)->clearCombinedVelocities();
323
324         }
325
326 }       
327
328
329 void SM_Scene::setSecondaryRespTable(DT_RespTableHandle secondaryRespTable) {
330         m_secondaryRespTable = secondaryRespTable;
331 }
332
333
334 DT_Bool SM_Scene::boing(
335         void *client_data,  
336         void *object1,
337         void *object2,
338         const DT_CollData *
339 ){
340         SM_Scene  *scene = (SM_Scene *)client_data; 
341         SM_Object *obj1  = (SM_Object *)object1;  
342         SM_Object *obj2  = (SM_Object *)object2;  
343         
344         scene->notifyCollision(obj1, obj2); // Record this collision for client callbacks
345
346 #ifdef SM_DEBUG_BOING   
347         printf("SM_Scene::boing\n");
348 #endif
349         
350         return DT_CONTINUE;
351 }
352
353 SM_Scene::~SM_Scene()
354
355 //      if (m_objectList.begin() != m_objectList.end()) 
356 //              std::cout << "SM_Scene::~SM_Scene: There are still objects in the Sumo scene!" << std::endl;
357         for (T_ObjectList::iterator it = m_objectList.begin() ; it != m_objectList.end() ; it++)
358                 delete *it;
359         
360         DT_DestroyRespTable(m_respTable);
361         DT_DestroyRespTable(m_secondaryRespTable);
362         DT_DestroyRespTable(m_fixRespTable);
363         DT_DestroyScene(m_scene);
364 }