Rigs of Rods 2026.01
Soft-body Physics Simulation
Loading...
Searching...
No Matches
Collisions.cpp
Go to the documentation of this file.
1/*
2 This source file is part of Rigs of Rods
3 Copyright 2005-2012 Pierre-Michel Ricordel
4 Copyright 2007-2012 Thomas Fischer
5 Copyright 2013-2020 Petr Ohlidal
6
7 For more information, see http://www.rigsofrods.org/
8
9 Rigs of Rods is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License version 3, as
11 published by the Free Software Foundation.
12
13 Rigs of Rods is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "Collisions.h"
23
24#include "Application.h"
25#include "ApproxMath.h"
26#include "Actor.h"
27#include "ActorManager.h"
28#include "ErrorUtils.h"
29#include "GameContext.h"
30#include "GfxScene.h"
31#include "Landusemap.h"
32#include "Language.h"
33#include "MovableText.h"
34#include "PlatformUtils.h"
35#include "ScriptEngine.h"
36#include "Terrain.h"
37
38using namespace RoR;
39
40// some gcc fixes
41#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX
42#pragma GCC diagnostic ignored "-Wfloat-equal"
43#endif //OGRE_PLATFORM_LINUX
44
45//hash function SBOX
46//from http://home.comcast.net/~bretm/hash/10.html
47unsigned int sbox[] =
48{
49 0xF53E1837, 0x5F14C86B, 0x9EE3964C, 0xFA796D53,
50 0x32223FC3, 0x4D82BC98, 0xA0C7FA62, 0x63E2C982,
51 0x24994A5B, 0x1ECE7BEE, 0x292B38EF, 0xD5CD4E56,
52 0x514F4303, 0x7BE12B83, 0x7192F195, 0x82DC7300,
53 0x084380B4, 0x480B55D3, 0x5F430471, 0x13F75991,
54 0x3F9CF22C, 0x2FE0907A, 0xFD8E1E69, 0x7B1D5DE8,
55 0xD575A85C, 0xAD01C50A, 0x7EE00737, 0x3CE981E8,
56 0x0E447EFA, 0x23089DD6, 0xB59F149F, 0x13600EC7,
57 0xE802C8E6, 0x670921E4, 0x7207EFF0, 0xE74761B0,
58 0x69035234, 0xBFA40F19, 0xF63651A0, 0x29E64C26,
59 0x1F98CCA7, 0xD957007E, 0xE71DDC75, 0x3E729595,
60 0x7580B7CC, 0xD7FAF60B, 0x92484323, 0xA44113EB,
61 0xE4CBDE08, 0x346827C9, 0x3CF32AFA, 0x0B29BCF1,
62 0x6E29F7DF, 0xB01E71CB, 0x3BFBC0D1, 0x62EDC5B8,
63 0xB7DE789A, 0xA4748EC9, 0xE17A4C4F, 0x67E5BD03,
64 0xF3B33D1A, 0x97D8D3E9, 0x09121BC0, 0x347B2D2C,
65 0x79A1913C, 0x504172DE, 0x7F1F8483, 0x13AC3CF6,
66 0x7A2094DB, 0xC778FA12, 0xADF7469F, 0x21786B7B,
67 0x71A445D0, 0xA8896C1B, 0x656F62FB, 0x83A059B3,
68 0x972DFE6E, 0x4122000C, 0x97D9DA19, 0x17D5947B,
69 0xB1AFFD0C, 0x6EF83B97, 0xAF7F780B, 0x4613138A,
70 0x7C3E73A6, 0xCF15E03D, 0x41576322, 0x672DF292,
71 0xB658588D, 0x33EBEFA9, 0x938CBF06, 0x06B67381,
72 0x07F192C6, 0x2BDA5855, 0x348EE0E8, 0x19DBB6E3,
73 0x3222184B, 0xB69D5DBA, 0x7E760B88, 0xAF4D8154,
74 0x007A51AD, 0x35112500, 0xC9CD2D7D, 0x4F4FB761,
75 0x694772E3, 0x694C8351, 0x4A7E3AF5, 0x67D65CE1,
76 0x9287DE92, 0x2518DB3C, 0x8CB4EC06, 0xD154D38F,
77 0xE19A26BB, 0x295EE439, 0xC50A1104, 0x2153C6A7,
78 0x82366656, 0x0713BC2F, 0x6462215A, 0x21D9BFCE,
79 0xBA8EACE6, 0xAE2DF4C1, 0x2A8D5E80, 0x3F7E52D1,
80 0x29359399, 0xFEA1D19C, 0x18879313, 0x455AFA81,
81 0xFADFE838, 0x62609838, 0xD1028839, 0x0736E92F,
82 0x3BCA22A3, 0x1485B08A, 0x2DA7900B, 0x852C156D,
83 0xE8F24803, 0x00078472, 0x13F0D332, 0x2ACFD0CF,
84 0x5F747F5C, 0x87BB1E2F, 0xA7EFCB63, 0x23F432F0,
85 0xE6CE7C5C, 0x1F954EF6, 0xB609C91B, 0x3B4571BF,
86 0xEED17DC0, 0xE556CDA0, 0xA7846A8D, 0xFF105F94,
87 0x52B7CCDE, 0x0E33E801, 0x664455EA, 0xF2C70414,
88 0x73E7B486, 0x8F830661, 0x8B59E826, 0xBB8AEDCA,
89 0xF3D70AB9, 0xD739F2B9, 0x4A04C34A, 0x88D0F089,
90 0xE02191A2, 0xD89D9C78, 0x192C2749, 0xFC43A78F,
91 0x0AAC88CB, 0x9438D42D, 0x9E280F7A, 0x36063802,
92 0x38E8D018, 0x1C42A9CB, 0x92AAFF6C, 0xA24820C5,
93 0x007F077F, 0xCE5BC543, 0x69668D58, 0x10D6FF74,
94 0xBE00F621, 0x21300BBE, 0x2E9E8F46, 0x5ACEA629,
95 0xFA1F86C7, 0x52F206B8, 0x3EDF1A75, 0x6DA8D843,
96 0xCF719928, 0x73E3891F, 0xB4B95DD6, 0xB2A42D27,
97 0xEDA20BBF, 0x1A58DBDF, 0xA449AD03, 0x6DDEF22B,
98 0x900531E6, 0x3D3BFF35, 0x5B24ABA2, 0x472B3E4C,
99 0x387F2D75, 0x4D8DBA36, 0x71CB5641, 0xE3473F3F,
100 0xF6CD4B7F, 0xBF7D1428, 0x344B64D0, 0xC5CDFCB6,
101 0xFE2E0182, 0x2C37A673, 0xDE4EB7A3, 0x63FDC933,
102 0x01DC4063, 0x611F3571, 0xD167BFAF, 0x4496596F,
103 0x3DEE0689, 0xD8704910, 0x7052A114, 0x068C9EC5,
104 0x75D0E766, 0x4D54CC20, 0xB44ECDE2, 0x4ABC653E,
105 0x2C550A21, 0x1A52C0DB, 0xCFED03D0, 0x119BAFE2,
106 0x876A6133, 0xBC232088, 0x435BA1B2, 0xAE99BBFA,
107 0xBB4F08E4, 0xA62B5F49, 0x1DA4B695, 0x336B84DE,
108 0xDC813D31, 0x00C134FB, 0x397A98E6, 0x151F0E64,
109 0xD9EB3E69, 0xD3C7DF60, 0xD2F2C336, 0x2DDD067B,
110 0xBD122835, 0xB0B3BD3A, 0xB0D54E46, 0x8641F1E4,
111 0xA0B38F96, 0x51D39199, 0x37A6AD75, 0xDF84EE41,
112 0x3C034CBA, 0xACDA62FC, 0x11923B8B, 0x45EF170A,
113};
114
115using namespace Ogre;
116using namespace RoR;
117
118Collisions::Collisions(Ogre::Vector3 terrn_size):
119 forcecam(false)
120 , free_eventsource(0)
121 , hashmask(0)
122 , landuse(0)
123 , m_terrain_size(terrn_size)
124 , collision_version(0)
125 , forcecampos(Ogre::Vector3::ZERO)
126{
127 for (int i=0; i < HASH_POWER; i++)
128 {
129 hashmask = hashmask << 1;
130 hashmask++;
131 }
132
134 defaultgm = getGroundModelByString("concrete");
136
137 hashtable_height.fill(std::numeric_limits<float>::min());
138}
139
141{
142 if (landuse) delete landuse;
143}
144
146{
147 return loadGroundModelsConfigFile(PathCombine(App::sys_config_dir->getStr(), "ground_models.cfg"));
148}
149
151{
152 String group = "";
153 try
154 {
155 group = ResourceGroupManager::getSingleton().findGroupContainingResource(filename);
156 }
157 catch (...)
158 {
159 // we wont catch anything, since the path could be absolute as well, then the group is not found
160 }
161
162 Ogre::ConfigFile cfg;
163 try
164 {
165 // try to load directly otherwise via resource group
166 if (group == "")
167 cfg.loadDirect(filename);
168 else
169 cfg.loadFromResourceSystem(filename, group, "\x09:=", true);
170 }
171 catch (Ogre::Exception& e)
172 {
173 ErrorUtils::ShowError("Error while loading ground model", e.getFullDescription());
174 return 1;
175 }
176
177 // parse the whole config
178 parseGroundConfig(&cfg);
179
180 // after it was parsed, resolve the dependencies
181 std::map<Ogre::String, ground_model_t>::iterator it;
182 for (it=ground_models.begin(); it!=ground_models.end(); it++)
183 {
184 if (!strlen(it->second.basename)) continue; // no base, normal material
185 String bname = String(it->second.basename);
186 if (ground_models.find(bname) == ground_models.end()) continue; // base not found!
187 // copy the values from the base if not set otherwise
188 ground_model_t *thisgm = &(it->second);
189 ground_model_t *basegm = &ground_models[bname];
190 memcpy(thisgm, basegm, sizeof(ground_model_t));
191 // re-set the name
192 strncpy(thisgm->name, it->first.c_str(), 255);
193 // after that we need to reload the config to overwrite settings of the base
194 parseGroundConfig(&cfg, it->first);
195 }
196 // check the version
198 {
199 ErrorUtils::ShowError(_L("Configuration error"), _L("Your ground configuration is too old, please copy skeleton/config/ground_models.cfg to My Documents/Rigs of Rods/config"));
200 exit(124);
201 }
202 return 0;
203}
204
205
206void Collisions::parseGroundConfig(Ogre::ConfigFile *cfg, String groundModel)
207{
208 Ogre::ConfigFile::SectionIterator seci = cfg->getSectionIterator();
209
210 Ogre::String secName, kname, kvalue;
211 while (seci.hasMoreElements())
212 {
213 secName = seci.peekNextKey();
214 Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
215
216 if (!groundModel.empty() && secName != groundModel) continue;
217
218 Ogre::ConfigFile::SettingsMultiMap::iterator i;
219 for (i = settings->begin(); i != settings->end(); ++i)
220 {
221 kname = i->first;
222 kvalue = i->second;
223 // we got all the data available now, processing now
224 if (secName == "general" || secName == "config")
225 {
226 // set some class properties accoring to the information in this section
227 if (kname == "version") this->collision_version = StringConverter::parseInt(kvalue);
228 } else
229 {
230 // we assume that all other sections are separate ground types!
231 if (ground_models.find(secName) == ground_models.end())
232 {
233 // ground models not known yet, init it!
234 ground_models[secName] = ground_model_t();
235 // clear it
236 memset(&ground_models[secName], 0, sizeof(ground_model_t));
237 // set some default values
238 ground_models[secName].alpha = 2.0f;
239 ground_models[secName].strength = 1.0f;
240 // some fx defaults
241 ground_models[secName].fx_particle_amount = 20;
242 ground_models[secName].fx_particle_min_velo = 5;
243 ground_models[secName].fx_particle_max_velo = 99999;
244 ground_models[secName].fx_particle_velo_factor = 0.7f;
245 ground_models[secName].fx_particle_fade = -1;
246 ground_models[secName].fx_particle_timedelta = 1;
247 ground_models[secName].fx_particle_ttl = 2;
248 strncpy(ground_models[secName].name, secName.c_str(), 255);
249
250 }
251
252 if (kname == "adhesion velocity") ground_models[secName].va = StringConverter::parseReal(kvalue);
253 else if (kname == "static friction coefficient") ground_models[secName].ms = StringConverter::parseReal(kvalue);
254 else if (kname == "sliding friction coefficient") ground_models[secName].mc = StringConverter::parseReal(kvalue);
255 else if (kname == "hydrodynamic friction") ground_models[secName].t2 = StringConverter::parseReal(kvalue);
256 else if (kname == "stribeck velocity") ground_models[secName].vs = StringConverter::parseReal(kvalue);
257 else if (kname == "alpha") ground_models[secName].alpha = StringConverter::parseReal(kvalue);
258 else if (kname == "strength") ground_models[secName].strength = StringConverter::parseReal(kvalue);
259 else if (kname == "base") strncpy(ground_models[secName].basename, kvalue.c_str(), 255);
260 else if (kname == "fx_type")
261 {
262 if (kvalue == "PARTICLE")
263 ground_models[secName].fx_type = FX_PARTICLE;
264 else if (kvalue == "HARD")
265 ground_models[secName].fx_type = FX_HARD;
266 else if (kvalue == "DUSTY")
267 ground_models[secName].fx_type = FX_DUSTY;
268 else if (kvalue == "CLUMPY")
269 ground_models[secName].fx_type = FX_CLUMPY;
270 }
271 else if (kname == "fx_particle_name") strncpy(ground_models[secName].particle_name, kvalue.c_str(), 255);
272 else if (kname == "fx_colour") ground_models[secName].fx_colour = StringConverter::parseColourValue(kvalue);
273 else if (kname == "fx_particle_amount") ground_models[secName].fx_particle_amount = StringConverter::parseInt(kvalue);
274 else if (kname == "fx_particle_min_velo") ground_models[secName].fx_particle_min_velo = StringConverter::parseReal(kvalue);
275 else if (kname == "fx_particle_max_velo") ground_models[secName].fx_particle_max_velo = StringConverter::parseReal(kvalue);
276 else if (kname == "fx_particle_fade") ground_models[secName].fx_particle_fade = StringConverter::parseReal(kvalue);
277 else if (kname == "fx_particle_timedelta") ground_models[secName].fx_particle_timedelta = StringConverter::parseReal(kvalue);
278 else if (kname == "fx_particle_velo_factor") ground_models[secName].fx_particle_velo_factor = StringConverter::parseReal(kvalue);
279 else if (kname == "fx_particle_ttl") ground_models[secName].fx_particle_ttl = StringConverter::parseReal(kvalue);
280
281
282 else if (kname == "fluid density") ground_models[secName].fluid_density = StringConverter::parseReal(kvalue);
283 else if (kname == "flow consistency index") ground_models[secName].flow_consistency_index = StringConverter::parseReal(kvalue);
284 else if (kname == "flow behavior index") ground_models[secName].flow_behavior_index = StringConverter::parseReal(kvalue);
285 else if (kname == "solid ground level") ground_models[secName].solid_ground_level = StringConverter::parseReal(kvalue);
286 else if (kname == "drag anisotropy") ground_models[secName].drag_anisotropy = StringConverter::parseReal(kvalue);
287
288 }
289 }
290
291 if (!groundModel.empty()) break; // we dont need to go through the other sections
292 }
293}
294
295Ogre::Vector3 Collisions::calcCollidedSide(const Ogre::Vector3& pos, const Ogre::Vector3& lo, const Ogre::Vector3& hi)
296{
297 Ogre::Real min = pos.x - lo.x;
298 Ogre::Vector3 newPos = Ogre::Vector3(lo.x, pos.y, pos.z);
299
300 Ogre::Real t = pos.y - lo.y;
301 if (t < min) {
302 min=t;
303 newPos = Ogre::Vector3(pos.x, lo.y, pos.z);
304 }
305
306 t = pos.z - lo.z;
307 if (t < min) {
308 min=t;
309 newPos = Ogre::Vector3(pos.x, pos.y, lo.z);
310 }
311
312 t = hi.x - pos.x;
313 if (t < min) {
314 min=t;
315 newPos = Ogre::Vector3(hi.x, pos.y, pos.z);
316 }
317
318 t = hi.y - pos.y;
319 if (t < min) {
320 min=t;
321 newPos = Ogre::Vector3(pos.x, hi.y, pos.z);
322 }
323
324 t = hi.z - pos.z;
325 if (t < min) {
326 min=t;
327 newPos = Ogre::Vector3(pos.x, pos.y, hi.z);
328 }
329
330 return newPos;
331}
332
333void Collisions::setupLandUse(const char *configfile)
334{
335 if (landuse) return;
336 landuse = new Landusemap(configfile);
337}
338
340{
341 if (number > -1 && number < m_collision_boxes.size())
342 {
343 m_collision_boxes[number].enabled = false;
344 if (m_collision_boxes[number].eventsourcenum >= 0 && m_collision_boxes[number].eventsourcenum < free_eventsource)
345 {
346 eventsources[m_collision_boxes[number].eventsourcenum].es_enabled = false;
347 }
348 // Is it worth to update the hashmap? ~ ulteq 01/19
349 }
350}
351
353{
354 if (number > -1 && number < m_collision_tris.size())
355 {
356 m_collision_tris[number].enabled = false;
357 // Is it worth to update the hashmap? ~ ulteq 01/19
358 }
359}
360
362{
363 if (!ground_models.size() || ground_models.find(name) == ground_models.end())
364 return 0;
365
366 return &ground_models[name];
367}
368
369unsigned int Collisions::hashfunc(unsigned int cellid)
370{
371 unsigned int hash = 0;
372 for (int i=0; i < 4; i++)
373 {
374 hash ^= sbox[((unsigned char*)&cellid)[i]];
375 hash *= 3;
376 }
377 return hash&hashmask;
378}
379
380void Collisions::hash_add(int cell_x, int cell_z, int value, float h)
381{
382 unsigned int cell_id = (cell_x << 16) + cell_z;
383 unsigned int pos = hashfunc(cell_id);
384
385 hashtable[pos].emplace_back(cell_id, value);
386 hashtable_height[pos] = std::max(hashtable_height[pos], h);
387}
388
389int Collisions::hash_find(int cell_x, int cell_z)
390{
391 unsigned int cellid = (cell_x << 16) + cell_z;
392 unsigned int pos = hashfunc(cellid);
393
394 return static_cast<int>(pos);
395}
396
397int Collisions::addCollisionBox(bool rotating, bool virt, Vector3 pos, Ogre::Vector3 rot, Ogre::Vector3 l, Ogre::Vector3 h, Ogre::Vector3 sr, const Ogre::String &eventname, const Ogre::String &instancename, const Ogre::String& reverb_preset_name, bool forcecam, Ogre::Vector3 campos, Ogre::Vector3 sc /* = Vector3::UNIT_SCALE */, Ogre::Vector3 dr /* = Vector3::ZERO */, CollisionEventFilter event_filter /* = EVENT_ALL */, int scripthandler /* = -1 */)
398{
399 Quaternion rotation = Quaternion(Degree(rot.x), Vector3::UNIT_X) * Quaternion(Degree(rot.y), Vector3::UNIT_Y) * Quaternion(Degree(rot.z), Vector3::UNIT_Z);
400 Quaternion direction = Quaternion(Degree(dr.x), Vector3::UNIT_X) * Quaternion(Degree(dr.y), Vector3::UNIT_Y) * Quaternion(Degree(dr.z), Vector3::UNIT_Z);
401 int coll_box_index = (int)m_collision_boxes.size();
402 collision_box_t coll_box;
403
404 coll_box.enabled = true;
405
406 // set refined box anyway
407 coll_box.relo = l*sc;
408 coll_box.rehi = h*sc;
409
410 // calculate selfcenter anyway
411 coll_box.selfcenter = coll_box.relo;
412 coll_box.selfcenter += coll_box.rehi;
413 coll_box.selfcenter *= 0.5;
414
415 // and center too (we need it)
416 coll_box.center = pos;
417 coll_box.virt = virt;
418 coll_box.event_filter = event_filter;
419
420 // camera stuff
421 coll_box.camforced = forcecam;
422 if (forcecam)
423 {
424 coll_box.campos = coll_box.center + rotation * campos;
425 }
426
427 // audio stuff
428 if (!reverb_preset_name.empty())
429 {
430 coll_box.reverb_preset_name = reverb_preset_name;
431 }
432
433 // first, self-rotate
434 if (rotating)
435 {
436 // we have a self-rotated block
437 coll_box.selfrotated = true;
438 coll_box.selfrot = Quaternion(Degree(sr.x), Vector3::UNIT_X) * Quaternion(Degree(sr.y), Vector3::UNIT_Y) * Quaternion(Degree(sr.z), Vector3::UNIT_Z);
439 coll_box.selfunrot = coll_box.selfrot.Inverse();
440 } else
441 {
442 coll_box.selfrotated = false;
443 }
444
445 coll_box.eventsourcenum = -1;
446
447 if (!eventname.empty())
448 {
449 //LOG("COLL: adding "+TOSTRING(free_eventsource)+" "+String(instancename)+" "+String(eventname));
450 // this is event-generating
454 eventsources[free_eventsource].es_cbox = coll_box_index;
459 }
460
461 // next, global rotate
462 if (fabs(rot.x) < 0.0001f && fabs(rot.y) < 0.0001f && fabs(rot.z) < 0.0001f)
463 {
464 // unrefined box
465 coll_box.refined = false;
466 } else
467 {
468 // refined box
469 coll_box.refined = true;
470 // build rotation
471 coll_box.rot = rotation;
472 coll_box.unrot = rotation.Inverse();
473 }
474
475 // set raw box
476 // 8 points of a cube
477 if (coll_box.selfrotated || coll_box.refined)
478 {
479 coll_box.debug_verts[0] = Ogre::Vector3(l.x, l.y, l.z) * sc;
480 coll_box.debug_verts[1] = Ogre::Vector3(h.x, l.y, l.z) * sc;
481 coll_box.debug_verts[2] = Ogre::Vector3(l.x, h.y, l.z) * sc;
482 coll_box.debug_verts[3] = Ogre::Vector3(h.x, h.y, l.z) * sc;
483 coll_box.debug_verts[4] = Ogre::Vector3(l.x, l.y, h.z) * sc;
484 coll_box.debug_verts[5] = Ogre::Vector3(h.x, l.y, h.z) * sc;
485 coll_box.debug_verts[6] = Ogre::Vector3(l.x, h.y, h.z) * sc;
486 coll_box.debug_verts[7] = Ogre::Vector3(h.x, h.y, h.z) * sc;
487
488 // rotate box
489 if (coll_box.selfrotated)
490 for (int i=0; i < 8; i++)
491 {
492 coll_box.debug_verts[i]=coll_box.debug_verts[i]-coll_box.selfcenter;
493 coll_box.debug_verts[i]=coll_box.selfrot*coll_box.debug_verts[i];
494 coll_box.debug_verts[i]=coll_box.debug_verts[i]+coll_box.selfcenter;
495 }
496 if (coll_box.refined)
497 {
498 for (int i=0; i < 8; i++)
499 {
500 coll_box.debug_verts[i] = coll_box.rot * coll_box.debug_verts[i];
501 }
502 }
503 // find min/max
504 coll_box.lo = coll_box.debug_verts[0];
505 coll_box.hi = coll_box.debug_verts[0];
506 for (int i=1; i < 8; i++)
507 {
508 coll_box.lo.makeFloor(coll_box.debug_verts[i]);
509 coll_box.hi.makeCeil(coll_box.debug_verts[i]);
510 }
511 // set absolute coords
512 coll_box.lo += pos;
513 coll_box.hi += pos;
514 } else
515 {
516 // unrefined box
517 coll_box.lo = pos + coll_box.relo;
518 coll_box.hi = pos + coll_box.rehi;
519 Vector3 d = (coll_box.rehi - coll_box.relo);
520 coll_box.debug_verts[0] = coll_box.relo;
521 coll_box.debug_verts[1] = coll_box.relo; coll_box.debug_verts[1].x += d.x;
522 coll_box.debug_verts[2] = coll_box.relo; coll_box.debug_verts[2].y += d.y;
523 coll_box.debug_verts[3] = coll_box.relo; coll_box.debug_verts[3].x += d.x; coll_box.debug_verts[3].y += d.y;
524 coll_box.debug_verts[4] = coll_box.relo; coll_box.debug_verts[4].z += d.z;
525 coll_box.debug_verts[5] = coll_box.relo; coll_box.debug_verts[5].x += d.x; coll_box.debug_verts[5].z += d.z;
526 coll_box.debug_verts[6] = coll_box.relo; coll_box.debug_verts[6].y += d.y; coll_box.debug_verts[6].z += d.z;
527 coll_box.debug_verts[7] = coll_box.relo; coll_box.debug_verts[7].x += d.x; coll_box.debug_verts[7].y += d.y; coll_box.debug_verts[7].z += d.z;
528 }
529
530 // register this collision box in the index
531 Vector3 ilo = Ogre::Vector3(coll_box.lo / Ogre::Real(CELL_SIZE));
532 Vector3 ihi = Ogre::Vector3(coll_box.hi / Ogre::Real(CELL_SIZE));
533
534 // clamp between 0 and MAXIMUM_CELL;
535 ilo.makeCeil(Ogre::Vector3(0.0f));
536 ilo.makeFloor(Ogre::Vector3(MAXIMUM_CELL));
537 ihi.makeCeil(Ogre::Vector3(0.0f));
538 ihi.makeFloor(Ogre::Vector3(MAXIMUM_CELL));
539
540 for (int i = ilo.x; i <= ihi.x; i++)
541 {
542 for (int j = ilo.z; j <= ihi.z; j++)
543 {
544 hash_add(i, j, coll_box_index,coll_box.hi.y);
545 }
546 }
547
548 m_collision_aab.merge(AxisAlignedBox(coll_box.lo, coll_box.hi));
549 m_collision_boxes.push_back(coll_box);
550 return coll_box_index;
551}
552
553int Collisions::addCollisionTri(Vector3 p1, Vector3 p2, Vector3 p3, ground_model_t* gm)
554{
555 int new_tri_index = (int)m_collision_tris.size();
556 collision_tri_t new_tri;
557 new_tri.a=p1;
558 new_tri.b=p2;
559 new_tri.c=p3;
560 new_tri.gm=gm;
561 new_tri.enabled=true;
562 // compute transformations
563 // base construction
564 Vector3 bx=p2-p1;
565 Vector3 by=p3-p1;
566 Vector3 bz=bx.crossProduct(by);
567 bz.normalise();
568 // coordinates change matrix
569 new_tri.reverse.SetColumn(0, bx);
570 new_tri.reverse.SetColumn(1, by);
571 new_tri.reverse.SetColumn(2, bz);
572 new_tri.forward=new_tri.reverse.Inverse();
573
574 // compute tri AAB
575 new_tri.aab.merge(p1);
576 new_tri.aab.merge(p2);
577 new_tri.aab.merge(p3);
578 new_tri.aab.setMinimum(new_tri.aab.getMinimum() - 0.1f);
579 new_tri.aab.setMaximum(new_tri.aab.getMaximum() + 0.1f);
580
581 // register this collision tri in the index
582 Ogre::Vector3 ilo(new_tri.aab.getMinimum() / Ogre::Real(CELL_SIZE));
583 Ogre::Vector3 ihi(new_tri.aab.getMaximum() / Ogre::Real(CELL_SIZE));
584
585 // clamp between 0 and MAXIMUM_CELL;
586 ilo.makeCeil(Ogre::Vector3(0.0f));
587 ilo.makeFloor(Ogre::Vector3(MAXIMUM_CELL));
588 ihi.makeCeil(Ogre::Vector3(0.0f));
589 ihi.makeFloor(Ogre::Vector3(MAXIMUM_CELL));
590
591 for (int i = ilo.x; i <= ihi.x; i++)
592 {
593 for (int j = ilo.z; j<=ihi.z; j++)
594 {
595 hash_add(i, j, new_tri_index + hash_coll_element_t::ELEMENT_TRI_BASE_INDEX, new_tri.aab.getMaximum().y);
596 }
597 }
598
599 m_collision_aab.merge(new_tri.aab);
600 m_collision_tris.push_back(new_tri);
601 return new_tri_index;
602}
603
605{
606#ifdef USE_ANGELSCRIPT
607 // check if this box is active anymore
609 return;
610
611 if (node)
612 {
613 // An actor is activating the eventbox
614 // Duplicate invocation is prevented by `Actor::m_active_eventboxes` cache.
616 }
617 else
618 {
619 // A character is activating the eventbox
620 // this prevents that the same callback gets called at 2k FPS all the time, serious hit on FPS ...
621 if (std::find(std::begin(m_last_called_cboxes), std::end(m_last_called_cboxes), cbox) == m_last_called_cboxes.end())
622 {
624 m_last_called_cboxes.push_back(cbox);
625 }
626 }
627#endif //USE_ANGELSCRIPT
628}
629
630std::pair<bool, Ogre::Real> Collisions::intersectsTris(Ogre::Ray ray)
631{
632 int steps = ray.getDirection().length() / (float)CELL_SIZE;
633
634 int lhash = -1;
635
636 for (int i = 0; i <= steps; i++)
637 {
638 Vector3 pos = ray.getPoint((float)i / (float)steps);
639
640 // find the correct cell
641 int refx = (int)(pos.x / (float)CELL_SIZE);
642 int refz = (int)(pos.z / (float)CELL_SIZE);
643 int hash = hash_find(refx, refz);
644
645 if (hash == lhash)
646 continue;
647
648 lhash = hash;
649
650 size_t num_elements = hashtable[hash].size();
651 for (size_t k = 0; k < num_elements; k++)
652 {
653 if (hashtable[hash][k].IsCollisionTri())
654 {
655 const int ctri_index = hashtable[hash][k].element_index - hash_coll_element_t::ELEMENT_TRI_BASE_INDEX;
656 collision_tri_t *ctri = &m_collision_tris[ctri_index];
657
658 if (!ctri->enabled)
659 continue;
660
661 auto result = Ogre::Math::intersects(ray, ctri->a, ctri->b, ctri->c);
662 if (result.first && result.second < 1.0f)
663 {
664 return result;
665 }
666 }
667 }
668 }
669
670 return std::make_pair(false, 0.0f);
671}
672
673std::pair<bool, Ogre::Real> Collisions::intersectsTerrain(Ogre::Ray ray)
674{
675 const Ogre::Real raydir_length = ray.getDirection().length();
676 const Ogre::Real step_size = Ogre::Real(0.1);
677
678 /*
679 * Normalise ray's direction vector, follow it and take sample points after each step_size.
680 * Check for an intersection at each sample point.
681 */
682 ray.setDirection(ray.getDirection().normalisedCopy());
683
684 for (Ogre::Real distance = Ogre::Real(0); distance <= raydir_length; distance += step_size)
685 {
686 Ogre::Vector3 position = ray.getPoint(distance);
687 Ogre::Real terrain_height = App::GetGameContext()->GetTerrain()->getHeightAt(position.x, position.z);
688
689 if (terrain_height > position.y)
690 {
691 Ogre::Real distance_in_raydir_units = distance / raydir_length;
692
693 return std::make_pair(true, distance_in_raydir_units);
694 }
695 }
696
697 return std::make_pair(false, Ogre::Real(0));
698}
699
701{
702 return getSurfaceHeightBelow(x, z, std::numeric_limits<float>::max());
703}
704
705float Collisions::getSurfaceHeightBelow(float x, float z, float height)
706{
707 float surface_height = App::GetGameContext()->GetTerrain()->getHeightAt(x, z);
708
709 // find the correct cell
710 int refx = (int)(x / (float)CELL_SIZE);
711 int refz = (int)(z / (float)CELL_SIZE);
712 int hash = hash_find(refx, refz);
713
714 Vector3 origin = Vector3(x, hashtable_height[hash], z);
715 Ray ray(origin, -Vector3::UNIT_Y);
716
717 size_t num_elements = hashtable[hash].size();
718 for (size_t k = 0; k < num_elements; k++)
719 {
720 if (hashtable[hash][k].IsCollisionBox())
721 {
722 collision_box_t* cbox = &m_collision_boxes[hashtable[hash][k].element_index];
723
724 if (!cbox->enabled)
725 continue;
726
727 if (!cbox->virt && surface_height < cbox->hi.y)
728 {
729 if (x > cbox->lo.x && z > cbox->lo.z && x < cbox->hi.x && z < cbox->hi.z)
730 {
731 Vector3 pos = origin - cbox->center;
732 Vector3 dir = -Vector3::UNIT_Y;
733 if (cbox->refined)
734 {
735 pos = cbox->unrot * pos;
736 dir = cbox->unrot * dir;
737 }
738 if (cbox->selfrotated)
739 {
740 pos = pos - cbox->selfcenter;
741 pos = cbox->selfunrot * pos;
742 pos = pos + cbox->selfcenter;
743 dir = cbox->selfunrot * dir;
744 }
745 auto result = Ogre::Math::intersects(Ray(pos, dir), AxisAlignedBox(cbox->relo, cbox->rehi));
746 if (result.first)
747 {
748 Vector3 hit = pos + dir * result.second;
749 if (cbox->selfrotated)
750 {
751 hit = cbox->selfrot * hit;
752 }
753 if (cbox->refined)
754 {
755 hit = cbox->rot * hit;
756 }
757 hit += cbox->center;
758 if (hit.y < height)
759 {
760 surface_height = std::max(surface_height, hit.y);
761 }
762 }
763 }
764 }
765 }
766 else // The element is a triangle
767 {
768 const int ctri_index = hashtable[hash][k].element_index - hash_coll_element_t::ELEMENT_TRI_BASE_INDEX;
769 collision_tri_t *ctri = &m_collision_tris[ctri_index];
770
771 if (!ctri->enabled)
772 continue;
773
774 auto lo = ctri->aab.getMinimum();
775 auto hi = ctri->aab.getMaximum();
776 if (surface_height >= hi.y)
777 continue;
778 if (x < lo.x || z < lo.z || x > hi.x || z > hi.z)
779 continue;
780
781 auto result = Ogre::Math::intersects(ray, ctri->a, ctri->b, ctri->c);
782 if (result.first)
783 {
784 if (origin.y - result.second < height)
785 {
786 surface_height = std::max(surface_height, origin.y - result.second);
787 }
788 }
789 }
790 }
791
792 return surface_height;
793}
794
795bool Collisions::collisionCorrect(Vector3 *refpos, bool envokeScriptCallbacks)
796{
797 // find the correct cell
798 int refx = (int)(refpos->x / (float)CELL_SIZE);
799 int refz = (int)(refpos->z / (float)CELL_SIZE);
800 int hash = hash_find(refx, refz);
801
802 if (refpos->y > hashtable_height[hash])
803 return false;
804
805 collision_tri_t *minctri = 0;
806 float minctridist = 100.0f;
807 Vector3 minctripoint;
808
809 bool contacted = false;
810 bool isScriptCallbackEnvoked = false;
811
812 size_t num_elements = hashtable[hash].size();
813 for (size_t k = 0; k < num_elements; k++)
814 {
815 if (hashtable[hash][k].IsCollisionBox())
816 {
817 collision_box_t* cbox = &m_collision_boxes[hashtable[hash][k].element_index];
818
819 if (!cbox->enabled)
820 continue;
821 if (!(*refpos > cbox->lo && *refpos < cbox->hi))
822 continue;
823
824 if (cbox->refined || cbox->selfrotated)
825 {
826 // we may have a collision, do a change of repere
827 Vector3 Pos = *refpos - cbox->center;
828 if (cbox->refined)
829 {
830 Pos = cbox->unrot * Pos;
831 }
832 if (cbox->selfrotated)
833 {
834 Pos = Pos - cbox->selfcenter;
835 Pos = cbox->selfunrot * Pos;
836 Pos = Pos + cbox->selfcenter;
837 }
838 // now test with the inner box
839 if (Pos > cbox->relo && Pos < cbox->rehi)
840 {
841 if (cbox->eventsourcenum!=-1 && permitEvent(nullptr, cbox->event_filter) && envokeScriptCallbacks)
842 {
844 isScriptCallbackEnvoked = true;
845 }
846 if (cbox->camforced && !forcecam)
847 {
848 forcecam = true;
849 forcecampos = cbox->campos;
850 }
851 if (!cbox->virt)
852 {
853 // collision, process as usual
854 // we have a collision
855 contacted = true;
856 // determine which side collided
857 Pos = calcCollidedSide(Pos, cbox->relo, cbox->rehi);
858
859 // resume repere
860 if (cbox->selfrotated)
861 {
862 Pos = Pos - cbox->selfcenter;
863 Pos = cbox->selfrot * Pos;
864 Pos = Pos + cbox->selfcenter;
865 }
866 if (cbox->refined)
867 {
868 Pos = cbox->rot * Pos;
869 }
870 *refpos = Pos + cbox->center;
871 }
872 }
873
874 } else
875 {
876 if (cbox->eventsourcenum!=-1 && permitEvent(nullptr, cbox->event_filter) && envokeScriptCallbacks)
877 {
879 isScriptCallbackEnvoked = true;
880 }
881 if (cbox->camforced && !forcecam)
882 {
883 forcecam = true;
884 forcecampos = cbox->campos;
885 }
886 if (!cbox->virt)
887 {
888 // we have a collision
889 contacted = true;
890 // determine which side collided
891 (*refpos) = calcCollidedSide((*refpos), cbox->lo, cbox->hi);
892 }
893 }
894 }
895 else // The element is a triangle
896 {
897 const int ctri_index = hashtable[hash][k].element_index - hash_coll_element_t::ELEMENT_TRI_BASE_INDEX;
898 collision_tri_t *ctri = &m_collision_tris[ctri_index];
899 if (!ctri->enabled)
900 continue;
901 if (!ctri->aab.contains(*refpos))
902 continue;
903 // check if this tri is minimal
904 // transform
905 Vector3 point = ctri->forward * (*refpos-ctri->a);
906 // test if within tri collision volume (potential cause of bug!)
907 if (point.x >= 0 && point.y >= 0 && (point.x + point.y) <= 1.0 && point.z < 0 && point.z > -0.1)
908 {
909 if (-point.z < minctridist)
910 {
911 minctri = ctri;
912 minctridist = -point.z;
913 minctripoint = point;
914 }
915 }
916 }
917 }
918
919 if (envokeScriptCallbacks && !isScriptCallbackEnvoked)
921
922 // process minctri collision
923 if (minctri)
924 {
925 // we have a contact
926 contacted = true;
927 // correct point
928 minctripoint.z = 0;
929 // reverse transform
930 *refpos = (minctri->reverse * minctripoint) + minctri->a;
931 }
932 return contacted;
933}
934
936{
937 switch (filter)
938 {
939 case EVENT_ALL:
940 return true;
941 case EVENT_AVATAR:
942 return !b;
943 case EVENT_TRUCK:
944 case EVENT_TRUCK_WHEELS: // The wheelnode check must be done separately
945 return b && b->ar_driveable == TRUCK;
946 case EVENT_AIRPLANE:
947 return b && b->ar_driveable == AIRPLANE;
948 case EVENT_BOAT:
949 return b && b->ar_driveable == BOAT;
950 default:
951 return false;
952 }
953}
954
956{
957 // find the correct cell
958 int refx = (int)(node->AbsPosition.x / CELL_SIZE);
959 int refz = (int)(node->AbsPosition.z / CELL_SIZE);
960 int hash = hash_find(refx, refz);
961 unsigned int cell_id = (refx << 16) + refz;
962
963 if (node->AbsPosition.y > hashtable_height[hash])
964 return false;
965
966 collision_tri_t *minctri = 0;
967 float minctridist = 100.0;
968 Vector3 minctripoint;
969
970 bool contacted = false;
971 bool isScriptCallbackEnvoked = false;
972
973 size_t num_elements = hashtable[hash].size();
974 for (size_t k=0; k < num_elements; k++)
975 {
976 if (hashtable[hash][k].cell_id != cell_id)
977 {
978 continue;
979 }
980 else if (hashtable[hash][k].IsCollisionBox())
981 {
982 collision_box_t *cbox = &m_collision_boxes[hashtable[hash][k].element_index];
983
984 if (!cbox->enabled)
985 continue;
986
987 if (node->AbsPosition > cbox->lo && node->AbsPosition < cbox->hi)
988 {
989 if (cbox->refined || cbox->selfrotated)
990 {
991 // we may have a collision, do a change of repere
992 Vector3 Pos = node->AbsPosition-cbox->center;
993 if (cbox->refined)
994 {
995 Pos = cbox->unrot * Pos;
996 }
997 if (cbox->selfrotated)
998 {
999 Pos = Pos - cbox->selfcenter;
1000 Pos = cbox->selfunrot * Pos;
1001 Pos = Pos + cbox->selfcenter;
1002 }
1003 // now test with the inner box
1004 if (Pos > cbox->relo && Pos < cbox->rehi)
1005 {
1006 if (cbox->camforced && !forcecam)
1007 {
1008 forcecam = true;
1009 forcecampos = cbox->campos;
1010 }
1011 if (!cbox->virt)
1012 {
1013 // collision, process as usual
1014 // we have a collision
1015 contacted = true;
1016 // determine which side collided
1017 float t = cbox->rehi.z - Pos.z;
1018 float min = Pos.z - cbox->relo.z;
1019 Vector3 normal = Vector3(0, 0, -1);
1020 if (t < min) { min = t; normal = Vector3(0,0,1);}; //north
1021 t = Pos.x - cbox->relo.x;
1022 if (t < min) { min = t; normal = Vector3(-1,0,0);}; //west
1023 t = cbox->rehi.x - Pos.x;
1024 if (t < min) { min = t; normal = Vector3(1,0,0);}; //east
1025 t = Pos.y - cbox->relo.y;
1026 if (t < min) { min = t; normal = Vector3(0,-1,0);}; //down
1027 t = cbox->rehi.y - Pos.y;
1028 if (t < min) { min = t; normal = Vector3(0,1,0);}; //up
1029
1030 // resume repere for the normal
1031 if (cbox->selfrotated) normal = cbox->selfrot * normal;
1032 if (cbox->refined) normal = cbox->rot * normal;
1033
1034 // collision boxes are always out of concrete as it seems
1035 node->Forces += primitiveCollision(node, node->Velocity, node->mass, normal, dt, defaultgm);
1037 }
1038 }
1039 } else
1040 {
1041 if (cbox->camforced && !forcecam)
1042 {
1043 forcecam = true;
1044 forcecampos = cbox->campos;
1045 }
1046 if (!cbox->virt)
1047 {
1048 // we have a collision
1049 contacted=true;
1050 // determine which side collided
1051 float t = cbox->hi.z - node->AbsPosition.z;
1052 float min = node->AbsPosition.z - cbox->lo.z;
1053 Vector3 normal = Vector3(0, 0, -1);
1054 if (t < min) {min = t; normal = Vector3(0,0,1);}; //north
1055 t = node->AbsPosition.x - cbox->lo.x;
1056 if (t < min) {min = t; normal = Vector3(-1,0,0);}; //west
1057 t = cbox->hi.x - node->AbsPosition.x;
1058 if (t < min) {min = t; normal = Vector3(1,0,0);}; //east
1059 t = node->AbsPosition.y - cbox->lo.y;
1060 if (t < min) {min = t; normal = Vector3(0,-1,0);}; //down
1061 t = cbox->hi.y - node->AbsPosition.y;
1062 if (t < min) {min = t; normal = Vector3(0,1,0);}; //up
1063
1064 // resume repere for the normal
1065 if (cbox->selfrotated) normal = cbox->selfrot * normal;
1066 if (cbox->refined) normal = cbox->rot * normal;
1067
1068 // collision boxes are always out of concrete as it seems
1069 node->Forces += primitiveCollision(node, node->Velocity, node->mass, normal, dt, defaultgm);
1071 }
1072 }
1073 }
1074 }
1075 else
1076 {
1077 // tri collision
1078 const int ctri_index = hashtable[hash][k].element_index - hash_coll_element_t::ELEMENT_TRI_BASE_INDEX;
1079 collision_tri_t *ctri = &m_collision_tris[ctri_index];
1080 if (!ctri->enabled)
1081 continue;
1082 if (node->AbsPosition.y > ctri->aab.getMaximum().y || node->AbsPosition.y < ctri->aab.getMinimum().y ||
1083 node->AbsPosition.x > ctri->aab.getMaximum().x || node->AbsPosition.x < ctri->aab.getMinimum().x ||
1084 node->AbsPosition.z > ctri->aab.getMaximum().z || node->AbsPosition.z < ctri->aab.getMinimum().z)
1085 continue;
1086 // check if this tri is minimal
1087 // transform
1088 Vector3 point = ctri->forward * (node->AbsPosition - ctri->a);
1089 // test if within tri collision volume (potential cause of bug!)
1090 if (point.x >= 0 && point.y >= 0 && (point.x + point.y) <= 1.0 && point.z < 0 && point.z > -0.1)
1091 {
1092 if (-point.z < minctridist)
1093 {
1094 minctri = ctri;
1095 minctridist = -point.z;
1096 minctripoint = point;
1097 }
1098 }
1099 }
1100 }
1101
1102 // process minctri collision
1103 if (minctri)
1104 {
1105 // we have a contact
1106 contacted=true;
1107 // we need the normal
1108 // resume repere for the normal
1109 Vector3 normal = minctri->reverse * Vector3::UNIT_Z;
1110 node->Forces += primitiveCollision(node, node->Velocity, node->mass, normal, dt, minctri->gm);
1111 node->nd_last_collision_gm = minctri->gm;
1112 }
1113
1114 return contacted;
1115}
1116
1118{
1119 // Find collision cells occupied by the actor (remember 'Y' is 'up').
1120 // Remember there's a dedicated bounding box `ar_evboxes_bounding_box`.
1121 // ----------------------------------------------------------------------
1122
1123 const AxisAlignedBox aabb = actor->ar_evboxes_bounding_box;
1124 const int cell_lo_x = (int)(aabb.getMinimum().x / (float)CELL_SIZE);
1125 const int cell_lo_z = (int)(aabb.getMinimum().z / (float)CELL_SIZE);
1126 const int cell_hi_x = (int)(aabb.getMaximum().x / (float)CELL_SIZE);
1127 const int cell_hi_z = (int)(aabb.getMaximum().z / (float)CELL_SIZE);
1128
1129 // Loop the collision cells
1130 for (int refx = cell_lo_x; refx <= cell_hi_x; refx++)
1131 {
1132 for (int refz = cell_lo_z; refz <= cell_hi_z; refz++)
1133 {
1134 // Find current cell
1135 const int hash = this->hash_find(refx, refz);
1136 const unsigned int cell_id = (refx << 16) + refz;
1137
1138 // Find eligible event boxes in the cell
1139 for (size_t k = 0; k < hashtable[hash].size(); k++)
1140 {
1141 if (hashtable[hash][k].cell_id != cell_id)
1142 {
1143 continue;
1144 }
1145 else if (hashtable[hash][k].IsCollisionBox())
1146 {
1147 collision_box_t* cbox = &m_collision_boxes[hashtable[hash][k].element_index];
1148
1149 if (!cbox->enabled)
1150 continue;
1151
1152 if (cbox->eventsourcenum != -1 && this->permitEvent(actor, cbox->event_filter))
1153 {
1154 out_boxes.push_back(cbox);
1155 }
1156 }
1157 }
1158 }
1159 }
1160}
1161
1162Vector3 Collisions::getPosition(const Ogre::String &inst, const Ogre::String &box)
1163{
1164 for (int i=0; i<free_eventsource; i++)
1165 {
1166 if (inst == eventsources[i].es_instance_name && box == eventsources[i].es_box_name)
1167 {
1169 }
1170 }
1171 return Vector3::ZERO;
1172}
1173
1174Quaternion Collisions::getDirection(const Ogre::String &inst, const Ogre::String &box)
1175{
1176 for (int i=0; i<free_eventsource; i++)
1177 {
1178 if (inst == eventsources[i].es_instance_name && box == eventsources[i].es_box_name)
1179 {
1181 }
1182 }
1183 return Quaternion::ZERO;
1184}
1185
1186collision_box_t *Collisions::getBox(const Ogre::String &inst, const Ogre::String &box)
1187{
1188 for (int i=0; i<free_eventsource; i++)
1189 {
1190 if (inst == eventsources[i].es_instance_name && box == eventsources[i].es_box_name)
1191 {
1193 }
1194 }
1195 return NULL;
1196}
1197
1198bool Collisions::isInside(Ogre::Vector3 pos, const Ogre::String &inst, const Ogre::String &box, float border)
1199{
1200 collision_box_t *cbox = getBox(inst, box);
1201
1202 return isInside(pos, cbox, border);
1203}
1204
1205bool Collisions::isInside(Ogre::Vector3 pos, collision_box_t *cbox, float border)
1206{
1207 if (!cbox) return false;
1208
1209 if (pos + border > cbox->lo
1210 && pos - border < cbox->hi)
1211 {
1212 if (cbox->refined || cbox->selfrotated)
1213 {
1214 // we may have a collision, do a change of repere
1215 Vector3 rpos = pos - cbox->center;
1216 if (cbox->refined)
1217 {
1218 rpos = cbox->unrot * rpos;
1219 }
1220 if (cbox->selfrotated)
1221 {
1222 rpos = rpos - cbox->selfcenter;
1223 rpos = cbox->selfunrot * rpos;
1224 rpos = rpos + cbox->selfcenter;
1225 }
1226
1227 // now test with the inner box
1228 if (rpos > cbox->relo
1229 && rpos < cbox->rehi)
1230 {
1231 return true;
1232 }
1233 } else
1234 {
1235 return true;
1236 }
1237 }
1238 return false;
1239}
1240
1242{
1243 Real v = App::GetGameContext()->GetTerrain()->getHeightAt(node->AbsPosition.x, node->AbsPosition.z);
1244 if (v > node->AbsPosition.y)
1245 {
1246 ground_model_t* ogm = landuse ? landuse->getGroundModelAt(node->AbsPosition.x, node->AbsPosition.z) : nullptr;
1247 // when landuse fails or we don't have it, use the default value
1248 if (!ogm) ogm = defaultgroundgm;
1249 Ogre::Vector3 normal = App::GetGameContext()->GetTerrain()->GetNormalAt(node->AbsPosition.x, v, node->AbsPosition.z);
1250 node->Forces += primitiveCollision(node, node->Velocity, node->mass, normal, dt, ogm, v - node->AbsPosition.y);
1251 node->nd_last_collision_gm = ogm;
1252 return true;
1253 }
1254 return false;
1255}
1256
1257Vector3 RoR::primitiveCollision(node_t *node, Ogre::Vector3 velocity, float mass, Ogre::Vector3 normal, float dt, ground_model_t* gm, float penetration)
1258{
1259 Vector3 force = Vector3::ZERO;
1260 float Vnormal = velocity.dotProduct(normal);
1261 float Fnormal = node->Forces.dotProduct(normal);
1262
1263 // if we are inside the fluid (solid ground is below us)
1264 if (gm->solid_ground_level != 0.0f && penetration >= 0)
1265 {
1266 float Vsquared = velocity.squaredLength();
1267 // First of all calculate power law fluid viscosity
1268 float m = gm->flow_consistency_index * approx_pow(Vsquared, (gm->flow_behavior_index - 1.0f) * 0.5f);
1269
1270 // Then calculate drag based on above. We'are using a simplified Stokes' drag.
1271 // Per node fluid drag surface coefficient set by node property applies here
1272 Vector3 Fdrag = velocity * (-m * node->surface_coef);
1273
1274 // If we have anisotropic drag
1275 if (gm->drag_anisotropy < 1.0f && Vnormal > 0)
1276 {
1277 float da_factor;
1278 if (Vsquared > gm->va * gm->va)
1279 da_factor = 1.0;
1280 else
1281 da_factor = Vsquared / (gm->va * gm->va);
1282 Fdrag += (Vnormal * m * (1.0f - gm->drag_anisotropy) * da_factor) * normal;
1283 }
1284 force += Fdrag;
1285
1286 // Now calculate upwards force based on a simplified boyancy equation;
1287 // If the fluid is pseudoplastic then boyancy is constrained to only "stopping" a node from going downwards
1288 // Buoyancy per node volume coefficient set by node property applies here
1289 float Fboyancy = gm->fluid_density * penetration * (-DEFAULT_GRAVITY) * node->volume_coef;
1290 if (gm->flow_behavior_index < 1.0f && Vnormal >= 0.0f)
1291 {
1292 if (Fnormal < 0 && Fboyancy>-Fnormal)
1293 {
1294 Fboyancy = -Fnormal;
1295 }
1296 }
1297 force += Fboyancy * normal;
1298 }
1299
1300 // if we are inside or touching the solid ground
1301 if (penetration >= gm->solid_ground_level)
1302 {
1303 // steady force
1304 float Freaction = -Fnormal;
1305 // impact force
1306 if (Vnormal < 0)
1307 {
1308 float penetration_depth = gm->solid_ground_level - penetration;
1309 Freaction -= (0.8f * Vnormal + 0.2f * penetration_depth / dt) * mass / dt; // Newton's second law
1310 }
1311 if (Freaction > 0)
1312 {
1313 Vector3 slipf = node->Forces - Fnormal * normal;
1314 Vector3 slip = velocity - Vnormal * normal;
1315 float slipv = slip.normalise();
1316 // If the velocity that we slip is lower than adhesion velocity and
1317 // we have a downforce and the slip forces are lower than static friction
1318 // forces then it's time to go into static friction physics mode.
1319 // This code is a direct translation of textbook static friction physics
1320 float Greaction = Freaction * gm->strength * node->friction_coef; //General moderated reaction
1321 float msGreaction = gm->ms * Greaction;
1322 if (slipv < gm->va && Greaction > 0.0f && slipf.squaredLength() <= msGreaction * msGreaction)
1323 {
1324 // Static friction model (with a little smoothing to help the integrator deal with it)
1325 float ff = -msGreaction * (1.0f - approx_exp(-slipv / gm->va));
1326 force += Freaction * normal + ff * slip - slipf;
1327 } else
1328 {
1329 // Stribek model. It also comes directly from textbooks.
1330 float g = gm->mc + (gm->ms - gm->mc) * approx_exp(-approx_pow(slipv / gm->vs, gm->alpha));
1331 float ff = -(g + std::min(gm->t2 * slipv, 5.0f)) * Greaction;
1332 force += Freaction * normal + ff * slip;
1333 }
1334 node->nd_avg_collision_slip = node->nd_avg_collision_slip * 0.995 + slipv * 0.005f;
1335 node->nd_last_collision_slip = slipv * slip;
1336 node->nd_last_collision_force = std::min(-Freaction, 0.0f) * normal;
1337 }
1338 }
1339
1340 return force;
1341}
1342
1343void Collisions::createCollisionDebugVisualization(Ogre::SceneNode* root_node, Ogre::AxisAlignedBox const& area_limit, std::vector<Ogre::SceneNode*>& out_nodes)
1344{
1345 LOG("COLL: Creating collision debug visualization ...");
1346
1347
1348
1349 for (int x=0; x<(int)(m_terrain_size.x); x+=(int)CELL_SIZE)
1350 {
1351 for (int z=0; z<(int)(m_terrain_size.z); z+=(int)CELL_SIZE)
1352 {
1353
1354 if (!area_limit.contains(Ogre::Vector3(x, 0.f, z)))
1355 continue;
1356
1357 int cellx = (int)(x/(float)CELL_SIZE);
1358 int cellz = (int)(z/(float)CELL_SIZE);
1359 const int hash = hash_find(cellx, cellz);
1360
1361 bool used = std::find_if(hashtable[hash].begin(), hashtable[hash].end(), [&](hash_coll_element_t const &c) {
1362 return c.cell_id == (cellx << 16) + cellz;
1363 }) != hashtable[hash].end();
1364
1365 if (used)
1366 {
1367 float groundheight = -9999;
1368 float x2 = x+CELL_SIZE;
1369 float z2 = z+CELL_SIZE;
1370
1371 // find a good ground height for all corners of the cell ...
1372 groundheight = std::max(groundheight, App::GetGameContext()->GetTerrain()->getHeightAt(x, z));
1373 groundheight = std::max(groundheight, App::GetGameContext()->GetTerrain()->getHeightAt(x2, z));
1374 groundheight = std::max(groundheight, App::GetGameContext()->GetTerrain()->getHeightAt(x, z2));
1375 groundheight = std::max(groundheight, App::GetGameContext()->GetTerrain()->getHeightAt(x2, z2));
1376 groundheight += 0.1; // 10 cm hover
1377
1378 float percentd = static_cast<float>(hashtable[hash].size()) / static_cast<float>(CELL_BLOCKSIZE);
1379 if (percentd > 1) percentd = 1;
1380
1381 // see `RoR::GUI::CollisionsDebug::GenerateCellDebugMaterials()`
1382 String matName = "mat-coll-dbg-"+TOSTRING((int)(percentd*100));
1383 String cell_name="("+TOSTRING(cellx)+","+ TOSTRING(cellz)+")";
1384
1385 ManualObject *mo = App::GetGfxScene()->GetSceneManager()->createManualObject("collisionDebugVisualization"+cell_name);
1386 SceneNode *mo_node = root_node->createChildSceneNode("collisionDebugVisualization_node"+cell_name);
1387
1388 mo->begin(matName, Ogre::RenderOperation::OT_TRIANGLE_LIST);
1389
1390 // 1st tri
1391 mo->position(-CELL_SIZE/(float)2.0, 0, -CELL_SIZE/(float)2.0);
1392 mo->textureCoord(0,0);
1393
1394 mo->position(CELL_SIZE/(float)2.0, 0, CELL_SIZE/(float)2.0);
1395 mo->textureCoord(1,1);
1396
1397 mo->position(CELL_SIZE/(float)2.0, 0, -CELL_SIZE/(float)2.0);
1398 mo->textureCoord(1,0);
1399
1400 // 2nd tri
1401 mo->position(-CELL_SIZE/(float)2.0, 0, CELL_SIZE/(float)2.0);
1402 mo->textureCoord(0,1);
1403
1404 mo->position(CELL_SIZE/(float)2.0, 0, CELL_SIZE/(float)2.0);
1405 mo->textureCoord(1,1);
1406
1407 mo->position(-CELL_SIZE/(float)2.0, 0, -CELL_SIZE/(float)2.0);
1408 mo->textureCoord(0,0);
1409
1410 mo->end();
1411 mo->setBoundingBox(AxisAlignedBox(0, 0, 0, CELL_SIZE, 1, CELL_SIZE));
1412 mo->setRenderingDistance(200.f);
1413 mo_node->attachObject(mo);
1414
1415#if 0
1416 // setup the label
1417 String labelName = "label_"+cell_name;
1418 String labelCaption = cell_name+" "+TOSTRING(percent*100,2) + "% usage ("+TOSTRING(cc)+"/"+TOSTRING(CELL_BLOCKSIZE)+") DEEP: " + TOSTRING(deep);
1419 MovableText *mt = new MovableText(labelName, labelCaption);
1421 mt->setFontName("CyberbitEnglish");
1422 mt->setAdditionalHeight(1);
1423 mt->setCharacterHeight(0.3);
1424 mt->setColor(ColourValue::White);
1425 mo_node->attachObject(mt);
1426#endif
1427
1428 mo_node->setVisible(true);
1429 mo_node->setPosition(Vector3(x+CELL_SIZE/(float)2.0, groundheight, z+CELL_SIZE/(float)2.0));
1430
1431 out_nodes.push_back(mo_node);
1432 }
1433 }
1434 }
1435}
1436
1437void Collisions::addCollisionMesh(Ogre::String const& srcname, Ogre::String const& meshname, Ogre::Vector3 const& pos, Ogre::Quaternion const& q, Ogre::Vector3 const& scale, ground_model_t *gm, std::vector<int> *collTris)
1438{
1439 Entity *ent = App::GetGfxScene()->GetSceneManager()->createEntity(meshname);
1440 ent->setMaterialName("tracks/debug/collision/mesh");
1441
1442 if (!gm)
1443 {
1444 gm = getGroundModelByString("concrete");
1445 }
1446
1447 // Analyze the mesh
1448 size_t vertex_count,index_count;
1449 Vector3* vertices;
1450 unsigned* indices;
1451
1452 getMeshInformation(ent->getMesh().get(),vertex_count,vertices,index_count,indices, pos, q, scale);
1453
1454 // Generate collision triangles
1455 int collision_tri_start = (int)m_collision_tris.size();
1456 for (int i=0; i<(int)index_count/3; i++)
1457 {
1458 int triID = addCollisionTri(vertices[indices[i*3]], vertices[indices[i*3+1]], vertices[indices[i*3+2]], gm);
1459 if (collTris)
1460 collTris->push_back(triID);
1461 }
1462
1463 // Submit the mesh record
1464 collision_mesh_t rec;
1465 rec.mesh_name = meshname;
1466 rec.source_name = srcname;
1467 rec.position = pos;
1468 rec.orientation = q;
1469 rec.scale = scale;
1470 rec.ground_model = gm;
1471 rec.num_verts = vertex_count;
1472 rec.num_indices = index_count;
1473 rec.collision_tri_start = collision_tri_start;
1474 rec.collision_tri_count = (int)m_collision_tris.size() - collision_tri_start;
1475 rec.bounding_box = ent->getMesh()->getBounds();
1476 m_collision_meshes.push_back(rec);
1477
1478 // Clean up
1479 delete[] vertices;
1480 delete[] indices;
1481 App::GetGfxScene()->GetSceneManager()->destroyEntity(ent);
1482}
1483
1484void Collisions::registerCollisionMesh(Ogre::String const& srcname, Ogre::String const& meshname, Ogre::Vector3 const& pos, AxisAlignedBox bounding_box, ground_model_t* gm, int ctri_start, int ctri_count)
1485{
1486 // Submit the mesh record
1487 collision_mesh_t rec;
1488 rec.mesh_name = meshname;
1489 rec.source_name = srcname;
1490 rec.position = pos;
1491 rec.ground_model = gm;
1492 rec.collision_tri_start = ctri_start;
1493 rec.collision_tri_count = ctri_count;
1494 rec.bounding_box = bounding_box;
1495 m_collision_meshes.push_back(rec);
1496}
1497
1498void Collisions::getMeshInformation(Mesh* mesh,size_t &vertex_count,Ogre::Vector3* &vertices,
1499 size_t &index_count, unsigned* &indices, const Ogre::Vector3 &position,
1500 const Ogre::Quaternion &orient, const Ogre::Vector3 &scale)
1501{
1502 vertex_count = index_count = 0;
1503
1504 bool added_shared = false;
1505 size_t current_offset = vertex_count;
1506 size_t shared_offset = vertex_count;
1507 size_t next_offset = vertex_count;
1508 size_t index_offset = index_count;
1509 //size_t prev_vert = vertex_count;
1510 //size_t prev_ind = index_count;
1511
1512 // Calculate how many vertices and indices we're going to need
1513 for (int i = 0;i < mesh->getNumSubMeshes();i++)
1514 {
1515 SubMesh* submesh = mesh->getSubMesh(i);
1516
1517 // We only need to add the shared vertices once
1518 if (submesh->useSharedVertices)
1519 {
1520 if (!added_shared)
1521 {
1522 VertexData* vertex_data = mesh->sharedVertexData;
1523 vertex_count += vertex_data->vertexCount;
1524 added_shared = true;
1525 }
1526 } else
1527 {
1528 VertexData* vertex_data = submesh->vertexData;
1529 vertex_count += vertex_data->vertexCount;
1530 }
1531
1532 // Add the indices
1533 Ogre::IndexData* index_data = submesh->indexData;
1534 index_count += index_data->indexCount;
1535 }
1536
1537 // Allocate space for the vertices and indices
1538 vertices = new Vector3[vertex_count];
1539 indices = new unsigned[index_count];
1540
1541 added_shared = false;
1542
1543 // Run through the sub-meshes again, adding the data into the arrays
1544 for (int i = 0;i < mesh->getNumSubMeshes();i++)
1545 {
1546 SubMesh* submesh = mesh->getSubMesh(i);
1547
1548 Ogre::VertexData* vertex_data = submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData;
1549 if ((!submesh->useSharedVertices)||(submesh->useSharedVertices && !added_shared))
1550 {
1551 if (submesh->useSharedVertices)
1552 {
1553 added_shared = true;
1554 shared_offset = current_offset;
1555 }
1556
1557 const Ogre::VertexElement* posElem = vertex_data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);
1558 Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_data->vertexBufferBinding->getBuffer(posElem->getSource());
1559 unsigned char* vertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
1560 Ogre::Real* pReal;
1561
1562 for (size_t j = 0; j < vertex_data->vertexCount; ++j, vertex += vbuf->getVertexSize())
1563 {
1564 posElem->baseVertexPointerToElement(vertex, &pReal);
1565
1566 Vector3 pt;
1567
1568 pt.x = (*pReal++);
1569 pt.y = (*pReal++);
1570 pt.z = (*pReal++);
1571
1572 pt = (orient * (pt * scale)) + position;
1573
1574 vertices[current_offset + j].x = pt.x;
1575 vertices[current_offset + j].y = pt.y;
1576 vertices[current_offset + j].z = pt.z;
1577 }
1578 vbuf->unlock();
1579 next_offset += vertex_data->vertexCount;
1580 }
1581
1582 Ogre::IndexData* index_data = submesh->indexData;
1583
1584 size_t numTris = index_data->indexCount / 3;
1585 unsigned short* pShort = 0;
1586 unsigned int* pInt = 0;
1587 Ogre::HardwareIndexBufferSharedPtr ibuf = index_data->indexBuffer;
1588
1589 bool use32bitindexes = (ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT);
1590
1591 if (use32bitindexes)
1592 pInt = static_cast<unsigned int*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
1593 else
1594 pShort = static_cast<unsigned short*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
1595
1596 for (size_t k = 0; k < numTris; ++k)
1597 {
1598 size_t offset = (submesh->useSharedVertices)?shared_offset:current_offset;
1599
1600 unsigned int vindex = use32bitindexes? *pInt++ : *pShort++;
1601 indices[index_offset + 0] = vindex + (unsigned int)offset;
1602 vindex = use32bitindexes? *pInt++ : *pShort++;
1603 indices[index_offset + 1] = vindex + (unsigned int)offset;
1604 vindex = use32bitindexes? *pInt++ : *pShort++;
1605 indices[index_offset + 2] = vindex + (unsigned int)offset;
1606
1607 index_offset += 3;
1608 }
1609 ibuf->unlock();
1610 current_offset = next_offset;
1611 }
1612}
1613
Central state/object manager and communications hub.
#define TOSTRING(x)
Definition Application.h:58
void LOG(const char *msg)
Legacy alias - formerly a macro.
float approx_exp(const float x)
Definition ApproxMath.h:68
float approx_pow(const float x, const float y)
Definition ApproxMath.h:91
unsigned int sbox[]
#define _L
Game state manager and message-queue provider.
This creates a billboarding object that displays a text.
Platform-specific utilities. We use narrow UTF-8 encoded strings as paths. Inspired by http://utf8eve...
Softbody object; can be anything from soda can to a space shuttle Constructed from a truck definition...
Definition Actor.h:55
Ogre::AxisAlignedBox ar_evboxes_bounding_box
bounding box around nodes eligible for eventbox triggering
Definition Actor.h:406
ActorType ar_driveable
Sim attr; marks vehicle type and features.
Definition Actor.h:466
void registerCollisionMesh(Ogre::String const &srcname, Ogre::String const &meshname, Ogre::Vector3 const &pos, Ogre::AxisAlignedBox bounding_box, ground_model_t *gm, int ctri_start, int ctri_count)
Mark already generated collision tris as belonging to (virtual) mesh.
std::pair< bool, Ogre::Real > intersectsTris(Ogre::Ray ray)
collision_box_t * getBox(const Ogre::String &inst, const Ogre::String &box)
float getSurfaceHeightBelow(float x, float z, float height)
static const int CELL_SIZE
Definition Collisions.h:128
unsigned int hashmask
Definition Collisions.h:156
std::vector< hash_coll_element_t > hashtable[HASH_SIZE]
Definition Collisions.h:143
const Ogre::Vector3 m_terrain_size
Definition Collisions.h:158
int addCollisionBox(bool rotating, bool virt, Ogre::Vector3 pos, Ogre::Vector3 rot, Ogre::Vector3 l, Ogre::Vector3 h, Ogre::Vector3 sr, const Ogre::String &eventname, const Ogre::String &instancename, const Ogre::String &reverb_preset_name, bool forcecam, Ogre::Vector3 campos, Ogre::Vector3 sc=Ogre::Vector3::UNIT_SCALE, Ogre::Vector3 dr=Ogre::Vector3::ZERO, CollisionEventFilter event_filter=EVENT_ALL, int scripthandler=-1)
bool collisionCorrect(Ogre::Vector3 *refpos, bool envokeScriptCallbacks=true)
static const int LATEST_GROUND_MODEL_VERSION
Definition Collisions.h:120
bool nodeCollision(node_t *node, float dt)
void getMeshInformation(Ogre::Mesh *mesh, size_t &vertex_count, Ogre::Vector3 *&vertices, size_t &index_count, unsigned *&indices, const Ogre::Vector3 &position=Ogre::Vector3::ZERO, const Ogre::Quaternion &orient=Ogre::Quaternion::IDENTITY, const Ogre::Vector3 &scale=Ogre::Vector3::UNIT_SCALE)
int hash_find(int cell_x, int cell_z)
std::pair< bool, Ogre::Real > intersectsTerrain(Ogre::Ray ray)
Checks whether a Ray intersects the terrain.
Ogre::Vector3 getPosition(const Ogre::String &inst, const Ogre::String &box)
static const int MAXIMUM_CELL
Definition Collisions.h:129
int loadGroundModelsConfigFile(Ogre::String filename)
Ogre::Quaternion getDirection(const Ogre::String &inst, const Ogre::String &box)
int addCollisionTri(Ogre::Vector3 p1, Ogre::Vector3 p2, Ogre::Vector3 p3, ground_model_t *gm)
bool groundCollision(node_t *node, float dt)
void removeCollisionBox(int number)
Ogre::AxisAlignedBox m_collision_aab
Definition Collisions.h:139
bool permitEvent(Actor *actor, CollisionEventFilter filter)
ground_model_t * getGroundModelByString(const Ogre::String name)
void removeCollisionTri(int number)
CollisionBoxVec m_collision_boxes
Definition Collisions.h:132
unsigned int hashfunc(unsigned int cellid)
Returns index to 'hashtable'.
void findPotentialEventBoxes(Actor *actor, CollisionBoxPtrVec &out_boxes)
static const int HASH_POWER
Definition Collisions.h:124
std::map< Ogre::String, ground_model_t > ground_models
Definition Collisions.h:146
eventsource_t eventsources[MAX_EVENT_SOURCE]
Definition Collisions.h:149
void parseGroundConfig(Ogre::ConfigFile *cfg, Ogre::String groundModel="")
std::array< float, HASH_SIZE > hashtable_height
Definition Collisions.h:142
void setupLandUse(const char *configfile)
ground_model_t * defaultgm
Definition Collisions.h:174
void hash_add(int cell_x, int cell_z, int value, float h)
float getSurfaceHeight(float x, float z)
Ogre::Vector3 forcecampos
Definition Collisions.h:173
Collisions(Ogre::Vector3 terrn_size)
std::vector< collision_box_t * > m_last_called_cboxes
Definition Collisions.h:133
static const int CELL_BLOCKSIZE
Definition Collisions.h:170
Ogre::Vector3 calcCollidedSide(const Ogre::Vector3 &pos, const Ogre::Vector3 &lo, const Ogre::Vector3 &hi)
void clearEventCache()
Definition Collisions.h:209
void createCollisionDebugVisualization(Ogre::SceneNode *root_node, Ogre::AxisAlignedBox const &area_limit, std::vector< Ogre::SceneNode * > &out_nodes)
Landusemap * landuse
Definition Collisions.h:154
CollisionTriVec m_collision_tris
Definition Collisions.h:136
CollisionMeshVec m_collision_meshes
Definition Collisions.h:137
void finishLoadingTerrain()
bool isInside(Ogre::Vector3 pos, const Ogre::String &inst, const Ogre::String &box, float border=0)
void envokeScriptCallback(collision_box_t *cbox, node_t *node=0)
void addCollisionMesh(Ogre::String const &srcname, Ogre::String const &meshname, Ogre::Vector3 const &pos, Ogre::Quaternion const &q, Ogre::Vector3 const &scale, ground_model_t *gm=0, std::vector< int > *collTris=0)
generate collision tris from existing mesh resource
ground_model_t * defaultgroundgm
Definition Collisions.h:174
const TerrainPtr & GetTerrain()
Ogre::SceneManager * GetSceneManager()
Definition GfxScene.h:83
ground_model_t * getGroundModelAt(int x, int z)
void setColor(const Ogre::ColourValue &color)
void setCharacterHeight(Ogre::Real height)
void setTextAlignment(const HorizontalAlignment &horizontalAlignment, const VerticalAlignment &verticalAlignment)
void setAdditionalHeight(Ogre::Real height)
void setFontName(const std::string &fontName)
void envokeCallback(int functionId, eventsource_t *source, NodeNum_t nodenum=NODENUM_INVALID, int type=0)
float getHeightAt(float x, float z)
Definition Terrain.cpp:511
Ogre::Vector3 GetNormalAt(float x, float y, float z)
Definition Terrain.cpp:516
std::string PathCombine(std::string a, std::string b)
std::vector< collision_box_t * > CollisionBoxPtrVec
Definition SimData.h:700
Ogre::Vector3 primitiveCollision(node_t *node, Ogre::Vector3 velocity, float mass, Ogre::Vector3 normal, float dt, ground_model_t *gm, float penetration=0)
@ TRUCK
its a truck (or other land vehicle)
Definition SimData.h:85
@ BOAT
its a boat
Definition SimData.h:87
@ AIRPLANE
its an airplane
Definition SimData.h:86
static const float DEFAULT_GRAVITY
earth gravity
GameContext * GetGameContext()
CVar * sys_config_dir
GfxScene * GetGfxScene()
ScriptEngine * GetScriptEngine()
CollisionEventFilter
Specified in terrain object (.ODEF) file, syntax: 'event <type> <filter>'.
Definition SimData.h:47
@ EVENT_ALL
(default) ~ Triggered by any node on any vehicle
Definition SimData.h:49
@ EVENT_TRUCK_WHEELS
'truck_wheels' ~ Triggered only by wheel nodes of land vehicle (ActorType::TRUCK)
Definition SimData.h:52
@ EVENT_AIRPLANE
'airplane' ~ Triggered by any node of airplane (ActorType::AIRPLANE)
Definition SimData.h:53
@ EVENT_AVATAR
'avatar' ~ Triggered by the character only
Definition SimData.h:50
@ EVENT_TRUCK
'truck' ~ Triggered by any node of land vehicle (ActorType::TRUCK)
Definition SimData.h:51
@ EVENT_BOAT
'boat' ~ Triggered by any node of boats (ActorType::BOAT)
Definition SimData.h:54
static int ShowError(const std::string &title, const std::string &message)
shows a simple error message box
Ogre::Vector3 campos
camera position
Definition SimData.h:696
std::string reverb_preset_name
name of the reverb preset that applies to the inside of the collision box
Definition SimData.h:698
Ogre::Vector3 hi
absolute collision box
Definition SimData.h:687
Ogre::Vector3 relo
relative collision box
Definition SimData.h:694
Ogre::Quaternion selfunrot
self rotation
Definition SimData.h:693
Ogre::Vector3 center
center of rotation
Definition SimData.h:688
Ogre::Vector3 rehi
relative collision box
Definition SimData.h:695
Ogre::Quaternion rot
rotation
Definition SimData.h:689
Ogre::Vector3 debug_verts[8]
box corners in absolute world position
Definition SimData.h:697
Ogre::Quaternion unrot
rotation
Definition SimData.h:690
Ogre::Quaternion selfrot
self rotation
Definition SimData.h:692
Ogre::Vector3 selfcenter
center of self rotation
Definition SimData.h:691
Ogre::Vector3 lo
absolute collision box
Definition SimData.h:686
CollisionEventFilter event_filter
Definition SimData.h:684
Records which collision triangles belong to which mesh.
Definition Collisions.h:65
Ogre::Vector3 scale
Definition Collisions.h:70
std::string source_name
Definition Collisions.h:67
Ogre::Vector3 position
Definition Collisions.h:68
Ogre::Quaternion orientation
Definition Collisions.h:69
std::string mesh_name
Definition Collisions.h:66
Ogre::AxisAlignedBox bounding_box
Definition Collisions.h:71
ground_model_t * ground_model
Definition Collisions.h:72
Ogre::Matrix3 reverse
Definition Collisions.h:57
Ogre::Matrix3 forward
Definition Collisions.h:56
ground_model_t * gm
Definition Collisions.h:58
Ogre::Vector3 a
Definition Collisions.h:52
Ogre::AxisAlignedBox aab
Definition Collisions.h:55
Ogre::Vector3 b
Definition Collisions.h:53
Ogre::Vector3 c
Definition Collisions.h:54
int es_script_handler
AngelScript function ID.
Definition Collisions.h:45
std::string es_box_name
Specified in ODEF file as "event".
Definition Collisions.h:43
int es_cbox
Collision box ID.
Definition Collisions.h:46
Ogre::Quaternion es_direction
Definition Collisions.h:44
std::string es_instance_name
Specified by user when calling "GameScript::spawnObject()".
Definition Collisions.h:42
Surface friction properties.
Definition SimData.h:704
float fluid_density
Density of liquid.
Definition SimData.h:713
float ms
static friction coefficient
Definition SimData.h:706
float flow_behavior_index
if flow_behavior_index<1 then liquid is Pseudoplastic (ketchup, whipped cream, paint) if =1 then liqu...
Definition SimData.h:719
float t2
hydrodynamic friction (s/m)
Definition SimData.h:708
float flow_consistency_index
general drag coefficient
Definition SimData.h:714
float solid_ground_level
how deep the solid ground is
Definition SimData.h:722
float vs
stribeck velocity (m/s)
Definition SimData.h:709
float drag_anisotropy
Upwards/Downwards drag anisotropy.
Definition SimData.h:723
float strength
ground strength
Definition SimData.h:711
float mc
sliding friction coefficient
Definition SimData.h:707
float va
adhesion velocity
Definition SimData.h:705
float alpha
steady-steady
Definition SimData.h:710
Physics: A vertex in the softbody structure.
Definition SimData.h:260
Ogre::Vector3 AbsPosition
absolute position in the world (shaky)
Definition SimData.h:267
Ogre::Real mass
Definition SimData.h:271
Ogre::Real nd_avg_collision_slip
Physics state; average slip velocity across the last few physics frames.
Definition SimData.h:297
Ogre::Vector3 Velocity
Definition SimData.h:268
Ogre::Vector3 nd_last_collision_force
Physics state; last collision force.
Definition SimData.h:299
Ogre::Real volume_coef
Definition SimData.h:275
Ogre::Vector3 Forces
Definition SimData.h:269
Ogre::Real surface_coef
Definition SimData.h:274
Ogre::Vector3 nd_last_collision_slip
Physics state; last collision slip vector.
Definition SimData.h:298
ground_model_t * nd_last_collision_gm
Physics state; last collision 'ground model' (surface definition)
Definition SimData.h:300
Ogre::Real friction_coef
Definition SimData.h:273
NodeNum_t pos
This node's index in Actor::ar_nodes array.
Definition SimData.h:277