Rigs of Rods 2023.09
Soft-body Physics Simulation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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#ifdef USE_PAGED
336 if (landuse) return;
337 landuse = new Landusemap(configfile);
338#else
339 LOG("RoR was not compiled with PagedGeometry support. You cannot use Landuse maps with it.");
340#endif //USE_PAGED
341}
342
344{
345 if (number > -1 && number < m_collision_boxes.size())
346 {
347 m_collision_boxes[number].enabled = false;
348 if (m_collision_boxes[number].eventsourcenum >= 0 && m_collision_boxes[number].eventsourcenum < free_eventsource)
349 {
350 eventsources[m_collision_boxes[number].eventsourcenum].es_enabled = false;
351 }
352 // Is it worth to update the hashmap? ~ ulteq 01/19
353 }
354}
355
357{
358 if (number > -1 && number < m_collision_tris.size())
359 {
360 m_collision_tris[number].enabled = false;
361 // Is it worth to update the hashmap? ~ ulteq 01/19
362 }
363}
364
366{
367 if (!ground_models.size() || ground_models.find(name) == ground_models.end())
368 return 0;
369
370 return &ground_models[name];
371}
372
373unsigned int Collisions::hashfunc(unsigned int cellid)
374{
375 unsigned int hash = 0;
376 for (int i=0; i < 4; i++)
377 {
378 hash ^= sbox[((unsigned char*)&cellid)[i]];
379 hash *= 3;
380 }
381 return hash&hashmask;
382}
383
384void Collisions::hash_add(int cell_x, int cell_z, int value, float h)
385{
386 unsigned int cell_id = (cell_x << 16) + cell_z;
387 unsigned int pos = hashfunc(cell_id);
388
389 hashtable[pos].emplace_back(cell_id, value);
390 hashtable_height[pos] = std::max(hashtable_height[pos], h);
391}
392
393int Collisions::hash_find(int cell_x, int cell_z)
394{
395 unsigned int cellid = (cell_x << 16) + cell_z;
396 unsigned int pos = hashfunc(cellid);
397
398 return static_cast<int>(pos);
399}
400
401int 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 */)
402{
403 Quaternion rotation = Quaternion(Degree(rot.x), Vector3::UNIT_X) * Quaternion(Degree(rot.y), Vector3::UNIT_Y) * Quaternion(Degree(rot.z), Vector3::UNIT_Z);
404 Quaternion direction = Quaternion(Degree(dr.x), Vector3::UNIT_X) * Quaternion(Degree(dr.y), Vector3::UNIT_Y) * Quaternion(Degree(dr.z), Vector3::UNIT_Z);
405 int coll_box_index = (int)m_collision_boxes.size();
406 collision_box_t coll_box;
407
408 coll_box.enabled = true;
409
410 // set refined box anyway
411 coll_box.relo = l*sc;
412 coll_box.rehi = h*sc;
413
414 // calculate selfcenter anyway
415 coll_box.selfcenter = coll_box.relo;
416 coll_box.selfcenter += coll_box.rehi;
417 coll_box.selfcenter *= 0.5;
418
419 // and center too (we need it)
420 coll_box.center = pos;
421 coll_box.virt = virt;
422 coll_box.event_filter = event_filter;
423
424 // camera stuff
425 coll_box.camforced = forcecam;
426 if (forcecam)
427 {
428 coll_box.campos = coll_box.center + rotation * campos;
429 }
430
431 // audio stuff
432 if (!reverb_preset_name.empty())
433 {
434 coll_box.reverb_preset_name = reverb_preset_name;
435 }
436
437 // first, self-rotate
438 if (rotating)
439 {
440 // we have a self-rotated block
441 coll_box.selfrotated = true;
442 coll_box.selfrot = Quaternion(Degree(sr.x), Vector3::UNIT_X) * Quaternion(Degree(sr.y), Vector3::UNIT_Y) * Quaternion(Degree(sr.z), Vector3::UNIT_Z);
443 coll_box.selfunrot = coll_box.selfrot.Inverse();
444 } else
445 {
446 coll_box.selfrotated = false;
447 }
448
449 coll_box.eventsourcenum = -1;
450
451 if (!eventname.empty())
452 {
453 //LOG("COLL: adding "+TOSTRING(free_eventsource)+" "+String(instancename)+" "+String(eventname));
454 // this is event-generating
458 eventsources[free_eventsource].es_cbox = coll_box_index;
463 }
464
465 // next, global rotate
466 if (fabs(rot.x) < 0.0001f && fabs(rot.y) < 0.0001f && fabs(rot.z) < 0.0001f)
467 {
468 // unrefined box
469 coll_box.refined = false;
470 } else
471 {
472 // refined box
473 coll_box.refined = true;
474 // build rotation
475 coll_box.rot = rotation;
476 coll_box.unrot = rotation.Inverse();
477 }
478
479 // set raw box
480 // 8 points of a cube
481 if (coll_box.selfrotated || coll_box.refined)
482 {
483 coll_box.debug_verts[0] = Ogre::Vector3(l.x, l.y, l.z) * sc;
484 coll_box.debug_verts[1] = Ogre::Vector3(h.x, l.y, l.z) * sc;
485 coll_box.debug_verts[2] = Ogre::Vector3(l.x, h.y, l.z) * sc;
486 coll_box.debug_verts[3] = Ogre::Vector3(h.x, h.y, l.z) * sc;
487 coll_box.debug_verts[4] = Ogre::Vector3(l.x, l.y, h.z) * sc;
488 coll_box.debug_verts[5] = Ogre::Vector3(h.x, l.y, h.z) * sc;
489 coll_box.debug_verts[6] = Ogre::Vector3(l.x, h.y, h.z) * sc;
490 coll_box.debug_verts[7] = Ogre::Vector3(h.x, h.y, h.z) * sc;
491
492 // rotate box
493 if (coll_box.selfrotated)
494 for (int i=0; i < 8; i++)
495 {
496 coll_box.debug_verts[i]=coll_box.debug_verts[i]-coll_box.selfcenter;
497 coll_box.debug_verts[i]=coll_box.selfrot*coll_box.debug_verts[i];
498 coll_box.debug_verts[i]=coll_box.debug_verts[i]+coll_box.selfcenter;
499 }
500 if (coll_box.refined)
501 {
502 for (int i=0; i < 8; i++)
503 {
504 coll_box.debug_verts[i] = coll_box.rot * coll_box.debug_verts[i];
505 }
506 }
507 // find min/max
508 coll_box.lo = coll_box.debug_verts[0];
509 coll_box.hi = coll_box.debug_verts[0];
510 for (int i=1; i < 8; i++)
511 {
512 coll_box.lo.makeFloor(coll_box.debug_verts[i]);
513 coll_box.hi.makeCeil(coll_box.debug_verts[i]);
514 }
515 // set absolute coords
516 coll_box.lo += pos;
517 coll_box.hi += pos;
518 } else
519 {
520 // unrefined box
521 coll_box.lo = pos + coll_box.relo;
522 coll_box.hi = pos + coll_box.rehi;
523 Vector3 d = (coll_box.rehi - coll_box.relo);
524 coll_box.debug_verts[0] = coll_box.relo;
525 coll_box.debug_verts[1] = coll_box.relo; coll_box.debug_verts[1].x += d.x;
526 coll_box.debug_verts[2] = coll_box.relo; coll_box.debug_verts[2].y += d.y;
527 coll_box.debug_verts[3] = coll_box.relo; coll_box.debug_verts[3].x += d.x; coll_box.debug_verts[3].y += d.y;
528 coll_box.debug_verts[4] = coll_box.relo; coll_box.debug_verts[4].z += d.z;
529 coll_box.debug_verts[5] = coll_box.relo; coll_box.debug_verts[5].x += d.x; coll_box.debug_verts[5].z += d.z;
530 coll_box.debug_verts[6] = coll_box.relo; coll_box.debug_verts[6].y += d.y; coll_box.debug_verts[6].z += d.z;
531 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;
532 }
533
534 // register this collision box in the index
535 Vector3 ilo = Ogre::Vector3(coll_box.lo / Ogre::Real(CELL_SIZE));
536 Vector3 ihi = Ogre::Vector3(coll_box.hi / Ogre::Real(CELL_SIZE));
537
538 // clamp between 0 and MAXIMUM_CELL;
539 ilo.makeCeil(Ogre::Vector3(0.0f));
540 ilo.makeFloor(Ogre::Vector3(MAXIMUM_CELL));
541 ihi.makeCeil(Ogre::Vector3(0.0f));
542 ihi.makeFloor(Ogre::Vector3(MAXIMUM_CELL));
543
544 for (int i = ilo.x; i <= ihi.x; i++)
545 {
546 for (int j = ilo.z; j <= ihi.z; j++)
547 {
548 hash_add(i, j, coll_box_index,coll_box.hi.y);
549 }
550 }
551
552 m_collision_aab.merge(AxisAlignedBox(coll_box.lo, coll_box.hi));
553 m_collision_boxes.push_back(coll_box);
554 return coll_box_index;
555}
556
557int Collisions::addCollisionTri(Vector3 p1, Vector3 p2, Vector3 p3, ground_model_t* gm)
558{
559 int new_tri_index = (int)m_collision_tris.size();
560 collision_tri_t new_tri;
561 new_tri.a=p1;
562 new_tri.b=p2;
563 new_tri.c=p3;
564 new_tri.gm=gm;
565 new_tri.enabled=true;
566 // compute transformations
567 // base construction
568 Vector3 bx=p2-p1;
569 Vector3 by=p3-p1;
570 Vector3 bz=bx.crossProduct(by);
571 bz.normalise();
572 // coordinates change matrix
573 new_tri.reverse.SetColumn(0, bx);
574 new_tri.reverse.SetColumn(1, by);
575 new_tri.reverse.SetColumn(2, bz);
576 new_tri.forward=new_tri.reverse.Inverse();
577
578 // compute tri AAB
579 new_tri.aab.merge(p1);
580 new_tri.aab.merge(p2);
581 new_tri.aab.merge(p3);
582 new_tri.aab.setMinimum(new_tri.aab.getMinimum() - 0.1f);
583 new_tri.aab.setMaximum(new_tri.aab.getMaximum() + 0.1f);
584
585 // register this collision tri in the index
586 Ogre::Vector3 ilo(new_tri.aab.getMinimum() / Ogre::Real(CELL_SIZE));
587 Ogre::Vector3 ihi(new_tri.aab.getMaximum() / Ogre::Real(CELL_SIZE));
588
589 // clamp between 0 and MAXIMUM_CELL;
590 ilo.makeCeil(Ogre::Vector3(0.0f));
591 ilo.makeFloor(Ogre::Vector3(MAXIMUM_CELL));
592 ihi.makeCeil(Ogre::Vector3(0.0f));
593 ihi.makeFloor(Ogre::Vector3(MAXIMUM_CELL));
594
595 for (int i = ilo.x; i <= ihi.x; i++)
596 {
597 for (int j = ilo.z; j<=ihi.z; j++)
598 {
599 hash_add(i, j, new_tri_index + hash_coll_element_t::ELEMENT_TRI_BASE_INDEX, new_tri.aab.getMaximum().y);
600 }
601 }
602
603 m_collision_aab.merge(new_tri.aab);
604 m_collision_tris.push_back(new_tri);
605 return new_tri_index;
606}
607
609{
610#ifdef USE_ANGELSCRIPT
611 // check if this box is active anymore
613 return;
614
615 if (node)
616 {
617 // An actor is activating the eventbox
618 // Duplicate invocation is prevented by `Actor::m_active_eventboxes` cache.
620 }
621 else
622 {
623 // A character is activating the eventbox
624 // this prevents that the same callback gets called at 2k FPS all the time, serious hit on FPS ...
625 if (std::find(std::begin(m_last_called_cboxes), std::end(m_last_called_cboxes), cbox) == m_last_called_cboxes.end())
626 {
628 m_last_called_cboxes.push_back(cbox);
629 }
630 }
631#endif //USE_ANGELSCRIPT
632}
633
634std::pair<bool, Ogre::Real> Collisions::intersectsTris(Ogre::Ray ray)
635{
636 int steps = ray.getDirection().length() / (float)CELL_SIZE;
637
638 int lhash = -1;
639
640 for (int i = 0; i <= steps; i++)
641 {
642 Vector3 pos = ray.getPoint((float)i / (float)steps);
643
644 // find the correct cell
645 int refx = (int)(pos.x / (float)CELL_SIZE);
646 int refz = (int)(pos.z / (float)CELL_SIZE);
647 int hash = hash_find(refx, refz);
648
649 if (hash == lhash)
650 continue;
651
652 lhash = hash;
653
654 size_t num_elements = hashtable[hash].size();
655 for (size_t k = 0; k < num_elements; k++)
656 {
657 if (hashtable[hash][k].IsCollisionTri())
658 {
659 const int ctri_index = hashtable[hash][k].element_index - hash_coll_element_t::ELEMENT_TRI_BASE_INDEX;
660 collision_tri_t *ctri = &m_collision_tris[ctri_index];
661
662 if (!ctri->enabled)
663 continue;
664
665 auto result = Ogre::Math::intersects(ray, ctri->a, ctri->b, ctri->c);
666 if (result.first && result.second < 1.0f)
667 {
668 return result;
669 }
670 }
671 }
672 }
673
674 return std::make_pair(false, 0.0f);
675}
676
677std::pair<bool, Ogre::Real> Collisions::intersectsTerrain(Ogre::Ray ray)
678{
679 const Ogre::Real raydir_length = ray.getDirection().length();
680 const Ogre::Real step_size = Ogre::Real(0.1);
681
682 /*
683 * Normalise ray's direction vector, follow it and take sample points after each step_size.
684 * Check for an intersection at each sample point.
685 */
686 ray.setDirection(ray.getDirection().normalisedCopy());
687
688 for (Ogre::Real distance = Ogre::Real(0); distance <= raydir_length; distance += step_size)
689 {
690 Ogre::Vector3 position = ray.getPoint(distance);
691 Ogre::Real terrain_height = App::GetGameContext()->GetTerrain()->getHeightAt(position.x, position.z);
692
693 if (terrain_height > position.y)
694 {
695 Ogre::Real distance_in_raydir_units = distance / raydir_length;
696
697 return std::make_pair(true, distance_in_raydir_units);
698 }
699 }
700
701 return std::make_pair(false, Ogre::Real(0));
702}
703
705{
706 return getSurfaceHeightBelow(x, z, std::numeric_limits<float>::max());
707}
708
709float Collisions::getSurfaceHeightBelow(float x, float z, float height)
710{
711 float surface_height = App::GetGameContext()->GetTerrain()->getHeightAt(x, z);
712
713 // find the correct cell
714 int refx = (int)(x / (float)CELL_SIZE);
715 int refz = (int)(z / (float)CELL_SIZE);
716 int hash = hash_find(refx, refz);
717
718 Vector3 origin = Vector3(x, hashtable_height[hash], z);
719 Ray ray(origin, -Vector3::UNIT_Y);
720
721 size_t num_elements = hashtable[hash].size();
722 for (size_t k = 0; k < num_elements; k++)
723 {
724 if (hashtable[hash][k].IsCollisionBox())
725 {
726 collision_box_t* cbox = &m_collision_boxes[hashtable[hash][k].element_index];
727
728 if (!cbox->enabled)
729 continue;
730
731 if (!cbox->virt && surface_height < cbox->hi.y)
732 {
733 if (x > cbox->lo.x && z > cbox->lo.z && x < cbox->hi.x && z < cbox->hi.z)
734 {
735 Vector3 pos = origin - cbox->center;
736 Vector3 dir = -Vector3::UNIT_Y;
737 if (cbox->refined)
738 {
739 pos = cbox->unrot * pos;
740 dir = cbox->unrot * dir;
741 }
742 if (cbox->selfrotated)
743 {
744 pos = pos - cbox->selfcenter;
745 pos = cbox->selfunrot * pos;
746 pos = pos + cbox->selfcenter;
747 dir = cbox->selfunrot * dir;
748 }
749 auto result = Ogre::Math::intersects(Ray(pos, dir), AxisAlignedBox(cbox->relo, cbox->rehi));
750 if (result.first)
751 {
752 Vector3 hit = pos + dir * result.second;
753 if (cbox->selfrotated)
754 {
755 hit = cbox->selfrot * hit;
756 }
757 if (cbox->refined)
758 {
759 hit = cbox->rot * hit;
760 }
761 hit += cbox->center;
762 if (hit.y < height)
763 {
764 surface_height = std::max(surface_height, hit.y);
765 }
766 }
767 }
768 }
769 }
770 else // The element is a triangle
771 {
772 const int ctri_index = hashtable[hash][k].element_index - hash_coll_element_t::ELEMENT_TRI_BASE_INDEX;
773 collision_tri_t *ctri = &m_collision_tris[ctri_index];
774
775 if (!ctri->enabled)
776 continue;
777
778 auto lo = ctri->aab.getMinimum();
779 auto hi = ctri->aab.getMaximum();
780 if (surface_height >= hi.y)
781 continue;
782 if (x < lo.x || z < lo.z || x > hi.x || z > hi.z)
783 continue;
784
785 auto result = Ogre::Math::intersects(ray, ctri->a, ctri->b, ctri->c);
786 if (result.first)
787 {
788 if (origin.y - result.second < height)
789 {
790 surface_height = std::max(surface_height, origin.y - result.second);
791 }
792 }
793 }
794 }
795
796 return surface_height;
797}
798
799bool Collisions::collisionCorrect(Vector3 *refpos, bool envokeScriptCallbacks)
800{
801 // find the correct cell
802 int refx = (int)(refpos->x / (float)CELL_SIZE);
803 int refz = (int)(refpos->z / (float)CELL_SIZE);
804 int hash = hash_find(refx, refz);
805
806 if (refpos->y > hashtable_height[hash])
807 return false;
808
809 collision_tri_t *minctri = 0;
810 float minctridist = 100.0f;
811 Vector3 minctripoint;
812
813 bool contacted = false;
814 bool isScriptCallbackEnvoked = false;
815
816 size_t num_elements = hashtable[hash].size();
817 for (size_t k = 0; k < num_elements; k++)
818 {
819 if (hashtable[hash][k].IsCollisionBox())
820 {
821 collision_box_t* cbox = &m_collision_boxes[hashtable[hash][k].element_index];
822
823 if (!cbox->enabled)
824 continue;
825 if (!(*refpos > cbox->lo && *refpos < cbox->hi))
826 continue;
827
828 if (cbox->refined || cbox->selfrotated)
829 {
830 // we may have a collision, do a change of repere
831 Vector3 Pos = *refpos - cbox->center;
832 if (cbox->refined)
833 {
834 Pos = cbox->unrot * Pos;
835 }
836 if (cbox->selfrotated)
837 {
838 Pos = Pos - cbox->selfcenter;
839 Pos = cbox->selfunrot * Pos;
840 Pos = Pos + cbox->selfcenter;
841 }
842 // now test with the inner box
843 if (Pos > cbox->relo && Pos < cbox->rehi)
844 {
845 if (cbox->eventsourcenum!=-1 && permitEvent(nullptr, cbox->event_filter) && envokeScriptCallbacks)
846 {
848 isScriptCallbackEnvoked = true;
849 }
850 if (cbox->camforced && !forcecam)
851 {
852 forcecam = true;
853 forcecampos = cbox->campos;
854 }
855 if (!cbox->virt)
856 {
857 // collision, process as usual
858 // we have a collision
859 contacted = true;
860 // determine which side collided
861 Pos = calcCollidedSide(Pos, cbox->relo, cbox->rehi);
862
863 // resume repere
864 if (cbox->selfrotated)
865 {
866 Pos = Pos - cbox->selfcenter;
867 Pos = cbox->selfrot * Pos;
868 Pos = Pos + cbox->selfcenter;
869 }
870 if (cbox->refined)
871 {
872 Pos = cbox->rot * Pos;
873 }
874 *refpos = Pos + cbox->center;
875 }
876 }
877
878 } else
879 {
880 if (cbox->eventsourcenum!=-1 && permitEvent(nullptr, cbox->event_filter) && envokeScriptCallbacks)
881 {
883 isScriptCallbackEnvoked = true;
884 }
885 if (cbox->camforced && !forcecam)
886 {
887 forcecam = true;
888 forcecampos = cbox->campos;
889 }
890 if (!cbox->virt)
891 {
892 // we have a collision
893 contacted = true;
894 // determine which side collided
895 (*refpos) = calcCollidedSide((*refpos), cbox->lo, cbox->hi);
896 }
897 }
898 }
899 else // The element is a triangle
900 {
901 const int ctri_index = hashtable[hash][k].element_index - hash_coll_element_t::ELEMENT_TRI_BASE_INDEX;
902 collision_tri_t *ctri = &m_collision_tris[ctri_index];
903 if (!ctri->enabled)
904 continue;
905 if (!ctri->aab.contains(*refpos))
906 continue;
907 // check if this tri is minimal
908 // transform
909 Vector3 point = ctri->forward * (*refpos-ctri->a);
910 // test if within tri collision volume (potential cause of bug!)
911 if (point.x >= 0 && point.y >= 0 && (point.x + point.y) <= 1.0 && point.z < 0 && point.z > -0.1)
912 {
913 if (-point.z < minctridist)
914 {
915 minctri = ctri;
916 minctridist = -point.z;
917 minctripoint = point;
918 }
919 }
920 }
921 }
922
923 if (envokeScriptCallbacks && !isScriptCallbackEnvoked)
925
926 // process minctri collision
927 if (minctri)
928 {
929 // we have a contact
930 contacted = true;
931 // correct point
932 minctripoint.z = 0;
933 // reverse transform
934 *refpos = (minctri->reverse * minctripoint) + minctri->a;
935 }
936 return contacted;
937}
938
940{
941 switch (filter)
942 {
943 case EVENT_ALL:
944 return true;
945 case EVENT_AVATAR:
946 return !b;
947 case EVENT_TRUCK:
948 case EVENT_TRUCK_WHEELS: // The wheelnode check must be done separately
949 return b && b->ar_driveable == TRUCK;
950 case EVENT_AIRPLANE:
951 return b && b->ar_driveable == AIRPLANE;
952 case EVENT_BOAT:
953 return b && b->ar_driveable == BOAT;
954 default:
955 return false;
956 }
957}
958
960{
961 // find the correct cell
962 int refx = (int)(node->AbsPosition.x / CELL_SIZE);
963 int refz = (int)(node->AbsPosition.z / CELL_SIZE);
964 int hash = hash_find(refx, refz);
965 unsigned int cell_id = (refx << 16) + refz;
966
967 if (node->AbsPosition.y > hashtable_height[hash])
968 return false;
969
970 collision_tri_t *minctri = 0;
971 float minctridist = 100.0;
972 Vector3 minctripoint;
973
974 bool contacted = false;
975 bool isScriptCallbackEnvoked = false;
976
977 size_t num_elements = hashtable[hash].size();
978 for (size_t k=0; k < num_elements; k++)
979 {
980 if (hashtable[hash][k].cell_id != cell_id)
981 {
982 continue;
983 }
984 else if (hashtable[hash][k].IsCollisionBox())
985 {
986 collision_box_t *cbox = &m_collision_boxes[hashtable[hash][k].element_index];
987
988 if (!cbox->enabled)
989 continue;
990
991 if (node->AbsPosition > cbox->lo && node->AbsPosition < cbox->hi)
992 {
993 if (cbox->refined || cbox->selfrotated)
994 {
995 // we may have a collision, do a change of repere
996 Vector3 Pos = node->AbsPosition-cbox->center;
997 if (cbox->refined)
998 {
999 Pos = cbox->unrot * Pos;
1000 }
1001 if (cbox->selfrotated)
1002 {
1003 Pos = Pos - cbox->selfcenter;
1004 Pos = cbox->selfunrot * Pos;
1005 Pos = Pos + cbox->selfcenter;
1006 }
1007 // now test with the inner box
1008 if (Pos > cbox->relo && Pos < cbox->rehi)
1009 {
1010 if (cbox->camforced && !forcecam)
1011 {
1012 forcecam = true;
1013 forcecampos = cbox->campos;
1014 }
1015 if (!cbox->virt)
1016 {
1017 // collision, process as usual
1018 // we have a collision
1019 contacted = true;
1020 // determine which side collided
1021 float t = cbox->rehi.z - Pos.z;
1022 float min = Pos.z - cbox->relo.z;
1023 Vector3 normal = Vector3(0, 0, -1);
1024 if (t < min) { min = t; normal = Vector3(0,0,1);}; //north
1025 t = Pos.x - cbox->relo.x;
1026 if (t < min) { min = t; normal = Vector3(-1,0,0);}; //west
1027 t = cbox->rehi.x - Pos.x;
1028 if (t < min) { min = t; normal = Vector3(1,0,0);}; //east
1029 t = Pos.y - cbox->relo.y;
1030 if (t < min) { min = t; normal = Vector3(0,-1,0);}; //down
1031 t = cbox->rehi.y - Pos.y;
1032 if (t < min) { min = t; normal = Vector3(0,1,0);}; //up
1033
1034 // resume repere for the normal
1035 if (cbox->selfrotated) normal = cbox->selfrot * normal;
1036 if (cbox->refined) normal = cbox->rot * normal;
1037
1038 // collision boxes are always out of concrete as it seems
1039 node->Forces += primitiveCollision(node, node->Velocity, node->mass, normal, dt, defaultgm);
1041 }
1042 }
1043 } else
1044 {
1045 if (cbox->camforced && !forcecam)
1046 {
1047 forcecam = true;
1048 forcecampos = cbox->campos;
1049 }
1050 if (!cbox->virt)
1051 {
1052 // we have a collision
1053 contacted=true;
1054 // determine which side collided
1055 float t = cbox->hi.z - node->AbsPosition.z;
1056 float min = node->AbsPosition.z - cbox->lo.z;
1057 Vector3 normal = Vector3(0, 0, -1);
1058 if (t < min) {min = t; normal = Vector3(0,0,1);}; //north
1059 t = node->AbsPosition.x - cbox->lo.x;
1060 if (t < min) {min = t; normal = Vector3(-1,0,0);}; //west
1061 t = cbox->hi.x - node->AbsPosition.x;
1062 if (t < min) {min = t; normal = Vector3(1,0,0);}; //east
1063 t = node->AbsPosition.y - cbox->lo.y;
1064 if (t < min) {min = t; normal = Vector3(0,-1,0);}; //down
1065 t = cbox->hi.y - node->AbsPosition.y;
1066 if (t < min) {min = t; normal = Vector3(0,1,0);}; //up
1067
1068 // resume repere for the normal
1069 if (cbox->selfrotated) normal = cbox->selfrot * normal;
1070 if (cbox->refined) normal = cbox->rot * normal;
1071
1072 // collision boxes are always out of concrete as it seems
1073 node->Forces += primitiveCollision(node, node->Velocity, node->mass, normal, dt, defaultgm);
1075 }
1076 }
1077 }
1078 }
1079 else
1080 {
1081 // tri collision
1082 const int ctri_index = hashtable[hash][k].element_index - hash_coll_element_t::ELEMENT_TRI_BASE_INDEX;
1083 collision_tri_t *ctri = &m_collision_tris[ctri_index];
1084 if (!ctri->enabled)
1085 continue;
1086 if (node->AbsPosition.y > ctri->aab.getMaximum().y || node->AbsPosition.y < ctri->aab.getMinimum().y ||
1087 node->AbsPosition.x > ctri->aab.getMaximum().x || node->AbsPosition.x < ctri->aab.getMinimum().x ||
1088 node->AbsPosition.z > ctri->aab.getMaximum().z || node->AbsPosition.z < ctri->aab.getMinimum().z)
1089 continue;
1090 // check if this tri is minimal
1091 // transform
1092 Vector3 point = ctri->forward * (node->AbsPosition - ctri->a);
1093 // test if within tri collision volume (potential cause of bug!)
1094 if (point.x >= 0 && point.y >= 0 && (point.x + point.y) <= 1.0 && point.z < 0 && point.z > -0.1)
1095 {
1096 if (-point.z < minctridist)
1097 {
1098 minctri = ctri;
1099 minctridist = -point.z;
1100 minctripoint = point;
1101 }
1102 }
1103 }
1104 }
1105
1106 // process minctri collision
1107 if (minctri)
1108 {
1109 // we have a contact
1110 contacted=true;
1111 // we need the normal
1112 // resume repere for the normal
1113 Vector3 normal = minctri->reverse * Vector3::UNIT_Z;
1114 node->Forces += primitiveCollision(node, node->Velocity, node->mass, normal, dt, minctri->gm);
1115 node->nd_last_collision_gm = minctri->gm;
1116 }
1117
1118 return contacted;
1119}
1120
1122{
1123 // Find collision cells occupied by the actor (remember 'Y' is 'up').
1124 // Remember there's a dedicated bounding box `ar_evboxes_bounding_box`.
1125 // ----------------------------------------------------------------------
1126
1127 const AxisAlignedBox aabb = actor->ar_evboxes_bounding_box;
1128 const int cell_lo_x = (int)(aabb.getMinimum().x / (float)CELL_SIZE);
1129 const int cell_lo_z = (int)(aabb.getMinimum().z / (float)CELL_SIZE);
1130 const int cell_hi_x = (int)(aabb.getMaximum().x / (float)CELL_SIZE);
1131 const int cell_hi_z = (int)(aabb.getMaximum().z / (float)CELL_SIZE);
1132
1133 // Loop the collision cells
1134 for (int refx = cell_lo_x; refx <= cell_hi_x; refx++)
1135 {
1136 for (int refz = cell_lo_z; refz <= cell_hi_z; refz++)
1137 {
1138 // Find current cell
1139 const int hash = this->hash_find(refx, refz);
1140 const unsigned int cell_id = (refx << 16) + refz;
1141
1142 // Find eligible event boxes in the cell
1143 for (size_t k = 0; k < hashtable[hash].size(); k++)
1144 {
1145 if (hashtable[hash][k].cell_id != cell_id)
1146 {
1147 continue;
1148 }
1149 else if (hashtable[hash][k].IsCollisionBox())
1150 {
1151 collision_box_t* cbox = &m_collision_boxes[hashtable[hash][k].element_index];
1152
1153 if (!cbox->enabled)
1154 continue;
1155
1156 if (cbox->eventsourcenum != -1 && this->permitEvent(actor, cbox->event_filter))
1157 {
1158 out_boxes.push_back(cbox);
1159 }
1160 }
1161 }
1162 }
1163 }
1164}
1165
1166Vector3 Collisions::getPosition(const Ogre::String &inst, const Ogre::String &box)
1167{
1168 for (int i=0; i<free_eventsource; i++)
1169 {
1170 if (inst == eventsources[i].es_instance_name && box == eventsources[i].es_box_name)
1171 {
1173 }
1174 }
1175 return Vector3::ZERO;
1176}
1177
1178Quaternion Collisions::getDirection(const Ogre::String &inst, const Ogre::String &box)
1179{
1180 for (int i=0; i<free_eventsource; i++)
1181 {
1182 if (inst == eventsources[i].es_instance_name && box == eventsources[i].es_box_name)
1183 {
1185 }
1186 }
1187 return Quaternion::ZERO;
1188}
1189
1190collision_box_t *Collisions::getBox(const Ogre::String &inst, const Ogre::String &box)
1191{
1192 for (int i=0; i<free_eventsource; i++)
1193 {
1194 if (inst == eventsources[i].es_instance_name && box == eventsources[i].es_box_name)
1195 {
1197 }
1198 }
1199 return NULL;
1200}
1201
1202bool Collisions::isInside(Ogre::Vector3 pos, const Ogre::String &inst, const Ogre::String &box, float border)
1203{
1204 collision_box_t *cbox = getBox(inst, box);
1205
1206 return isInside(pos, cbox, border);
1207}
1208
1209bool Collisions::isInside(Ogre::Vector3 pos, collision_box_t *cbox, float border)
1210{
1211 if (!cbox) return false;
1212
1213 if (pos + border > cbox->lo
1214 && pos - border < cbox->hi)
1215 {
1216 if (cbox->refined || cbox->selfrotated)
1217 {
1218 // we may have a collision, do a change of repere
1219 Vector3 rpos = pos - cbox->center;
1220 if (cbox->refined)
1221 {
1222 rpos = cbox->unrot * rpos;
1223 }
1224 if (cbox->selfrotated)
1225 {
1226 rpos = rpos - cbox->selfcenter;
1227 rpos = cbox->selfunrot * rpos;
1228 rpos = rpos + cbox->selfcenter;
1229 }
1230
1231 // now test with the inner box
1232 if (rpos > cbox->relo
1233 && rpos < cbox->rehi)
1234 {
1235 return true;
1236 }
1237 } else
1238 {
1239 return true;
1240 }
1241 }
1242 return false;
1243}
1244
1246{
1247 Real v = App::GetGameContext()->GetTerrain()->getHeightAt(node->AbsPosition.x, node->AbsPosition.z);
1248 if (v > node->AbsPosition.y)
1249 {
1250 ground_model_t* ogm = landuse ? landuse->getGroundModelAt(node->AbsPosition.x, node->AbsPosition.z) : nullptr;
1251 // when landuse fails or we don't have it, use the default value
1252 if (!ogm) ogm = defaultgroundgm;
1253 Ogre::Vector3 normal = App::GetGameContext()->GetTerrain()->GetNormalAt(node->AbsPosition.x, v, node->AbsPosition.z);
1254 node->Forces += primitiveCollision(node, node->Velocity, node->mass, normal, dt, ogm, v - node->AbsPosition.y);
1255 node->nd_last_collision_gm = ogm;
1256 return true;
1257 }
1258 return false;
1259}
1260
1261Vector3 RoR::primitiveCollision(node_t *node, Ogre::Vector3 velocity, float mass, Ogre::Vector3 normal, float dt, ground_model_t* gm, float penetration)
1262{
1263 Vector3 force = Vector3::ZERO;
1264 float Vnormal = velocity.dotProduct(normal);
1265 float Fnormal = node->Forces.dotProduct(normal);
1266
1267 // if we are inside the fluid (solid ground is below us)
1268 if (gm->solid_ground_level != 0.0f && penetration >= 0)
1269 {
1270 float Vsquared = velocity.squaredLength();
1271 // First of all calculate power law fluid viscosity
1272 float m = gm->flow_consistency_index * approx_pow(Vsquared, (gm->flow_behavior_index - 1.0f) * 0.5f);
1273
1274 // Then calculate drag based on above. We'are using a simplified Stokes' drag.
1275 // Per node fluid drag surface coefficient set by node property applies here
1276 Vector3 Fdrag = velocity * (-m * node->surface_coef);
1277
1278 // If we have anisotropic drag
1279 if (gm->drag_anisotropy < 1.0f && Vnormal > 0)
1280 {
1281 float da_factor;
1282 if (Vsquared > gm->va * gm->va)
1283 da_factor = 1.0;
1284 else
1285 da_factor = Vsquared / (gm->va * gm->va);
1286 Fdrag += (Vnormal * m * (1.0f - gm->drag_anisotropy) * da_factor) * normal;
1287 }
1288 force += Fdrag;
1289
1290 // Now calculate upwards force based on a simplified boyancy equation;
1291 // If the fluid is pseudoplastic then boyancy is constrained to only "stopping" a node from going downwards
1292 // Buoyancy per node volume coefficient set by node property applies here
1293 float Fboyancy = gm->fluid_density * penetration * (-DEFAULT_GRAVITY) * node->volume_coef;
1294 if (gm->flow_behavior_index < 1.0f && Vnormal >= 0.0f)
1295 {
1296 if (Fnormal < 0 && Fboyancy>-Fnormal)
1297 {
1298 Fboyancy = -Fnormal;
1299 }
1300 }
1301 force += Fboyancy * normal;
1302 }
1303
1304 // if we are inside or touching the solid ground
1305 if (penetration >= gm->solid_ground_level)
1306 {
1307 // steady force
1308 float Freaction = -Fnormal;
1309 // impact force
1310 if (Vnormal < 0)
1311 {
1312 float penetration_depth = gm->solid_ground_level - penetration;
1313 Freaction -= (0.8f * Vnormal + 0.2f * penetration_depth / dt) * mass / dt; // Newton's second law
1314 }
1315 if (Freaction > 0)
1316 {
1317 Vector3 slipf = node->Forces - Fnormal * normal;
1318 Vector3 slip = velocity - Vnormal * normal;
1319 float slipv = slip.normalise();
1320 // If the velocity that we slip is lower than adhesion velocity and
1321 // we have a downforce and the slip forces are lower than static friction
1322 // forces then it's time to go into static friction physics mode.
1323 // This code is a direct translation of textbook static friction physics
1324 float Greaction = Freaction * gm->strength * node->friction_coef; //General moderated reaction
1325 float msGreaction = gm->ms * Greaction;
1326 if (slipv < gm->va && Greaction > 0.0f && slipf.squaredLength() <= msGreaction * msGreaction)
1327 {
1328 // Static friction model (with a little smoothing to help the integrator deal with it)
1329 float ff = -msGreaction * (1.0f - approx_exp(-slipv / gm->va));
1330 force += Freaction * normal + ff * slip - slipf;
1331 } else
1332 {
1333 // Stribek model. It also comes directly from textbooks.
1334 float g = gm->mc + (gm->ms - gm->mc) * approx_exp(-approx_pow(slipv / gm->vs, gm->alpha));
1335 float ff = -(g + std::min(gm->t2 * slipv, 5.0f)) * Greaction;
1336 force += Freaction * normal + ff * slip;
1337 }
1338 node->nd_avg_collision_slip = node->nd_avg_collision_slip * 0.995 + slipv * 0.005f;
1339 node->nd_last_collision_slip = slipv * slip;
1340 node->nd_last_collision_force = std::min(-Freaction, 0.0f) * normal;
1341 }
1342 }
1343
1344 return force;
1345}
1346
1347void Collisions::createCollisionDebugVisualization(Ogre::SceneNode* root_node, Ogre::AxisAlignedBox const& area_limit, std::vector<Ogre::SceneNode*>& out_nodes)
1348{
1349 LOG("COLL: Creating collision debug visualization ...");
1350
1351
1352
1353 for (int x=0; x<(int)(m_terrain_size.x); x+=(int)CELL_SIZE)
1354 {
1355 for (int z=0; z<(int)(m_terrain_size.z); z+=(int)CELL_SIZE)
1356 {
1357
1358 if (!area_limit.contains(Ogre::Vector3(x, 0.f, z)))
1359 continue;
1360
1361 int cellx = (int)(x/(float)CELL_SIZE);
1362 int cellz = (int)(z/(float)CELL_SIZE);
1363 const int hash = hash_find(cellx, cellz);
1364
1365 bool used = std::find_if(hashtable[hash].begin(), hashtable[hash].end(), [&](hash_coll_element_t const &c) {
1366 return c.cell_id == (cellx << 16) + cellz;
1367 }) != hashtable[hash].end();
1368
1369 if (used)
1370 {
1371 float groundheight = -9999;
1372 float x2 = x+CELL_SIZE;
1373 float z2 = z+CELL_SIZE;
1374
1375 // find a good ground height for all corners of the cell ...
1376 groundheight = std::max(groundheight, App::GetGameContext()->GetTerrain()->getHeightAt(x, z));
1377 groundheight = std::max(groundheight, App::GetGameContext()->GetTerrain()->getHeightAt(x2, z));
1378 groundheight = std::max(groundheight, App::GetGameContext()->GetTerrain()->getHeightAt(x, z2));
1379 groundheight = std::max(groundheight, App::GetGameContext()->GetTerrain()->getHeightAt(x2, z2));
1380 groundheight += 0.1; // 10 cm hover
1381
1382 float percentd = static_cast<float>(hashtable[hash].size()) / static_cast<float>(CELL_BLOCKSIZE);
1383 if (percentd > 1) percentd = 1;
1384
1385 // see `RoR::GUI::CollisionsDebug::GenerateCellDebugMaterials()`
1386 String matName = "mat-coll-dbg-"+TOSTRING((int)(percentd*100));
1387 String cell_name="("+TOSTRING(cellx)+","+ TOSTRING(cellz)+")";
1388
1389 ManualObject *mo = App::GetGfxScene()->GetSceneManager()->createManualObject("collisionDebugVisualization"+cell_name);
1390 SceneNode *mo_node = root_node->createChildSceneNode("collisionDebugVisualization_node"+cell_name);
1391
1392 mo->begin(matName, Ogre::RenderOperation::OT_TRIANGLE_LIST);
1393
1394 // 1st tri
1395 mo->position(-CELL_SIZE/(float)2.0, 0, -CELL_SIZE/(float)2.0);
1396 mo->textureCoord(0,0);
1397
1398 mo->position(CELL_SIZE/(float)2.0, 0, CELL_SIZE/(float)2.0);
1399 mo->textureCoord(1,1);
1400
1401 mo->position(CELL_SIZE/(float)2.0, 0, -CELL_SIZE/(float)2.0);
1402 mo->textureCoord(1,0);
1403
1404 // 2nd tri
1405 mo->position(-CELL_SIZE/(float)2.0, 0, CELL_SIZE/(float)2.0);
1406 mo->textureCoord(0,1);
1407
1408 mo->position(CELL_SIZE/(float)2.0, 0, CELL_SIZE/(float)2.0);
1409 mo->textureCoord(1,1);
1410
1411 mo->position(-CELL_SIZE/(float)2.0, 0, -CELL_SIZE/(float)2.0);
1412 mo->textureCoord(0,0);
1413
1414 mo->end();
1415 mo->setBoundingBox(AxisAlignedBox(0, 0, 0, CELL_SIZE, 1, CELL_SIZE));
1416 mo->setRenderingDistance(200.f);
1417 mo_node->attachObject(mo);
1418
1419#if 0
1420 // setup the label
1421 String labelName = "label_"+cell_name;
1422 String labelCaption = cell_name+" "+TOSTRING(percent*100,2) + "% usage ("+TOSTRING(cc)+"/"+TOSTRING(CELL_BLOCKSIZE)+") DEEP: " + TOSTRING(deep);
1423 MovableText *mt = new MovableText(labelName, labelCaption);
1425 mt->setFontName("CyberbitEnglish");
1426 mt->setAdditionalHeight(1);
1427 mt->setCharacterHeight(0.3);
1428 mt->setColor(ColourValue::White);
1429 mo_node->attachObject(mt);
1430#endif
1431
1432 mo_node->setVisible(true);
1433 mo_node->setPosition(Vector3(x+CELL_SIZE/(float)2.0, groundheight, z+CELL_SIZE/(float)2.0));
1434
1435 out_nodes.push_back(mo_node);
1436 }
1437 }
1438 }
1439}
1440
1441void 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)
1442{
1443 Entity *ent = App::GetGfxScene()->GetSceneManager()->createEntity(meshname);
1444 ent->setMaterialName("tracks/debug/collision/mesh");
1445
1446 if (!gm)
1447 {
1448 gm = getGroundModelByString("concrete");
1449 }
1450
1451 // Analyze the mesh
1452 size_t vertex_count,index_count;
1453 Vector3* vertices;
1454 unsigned* indices;
1455
1456 getMeshInformation(ent->getMesh().get(),vertex_count,vertices,index_count,indices, pos, q, scale);
1457
1458 // Generate collision triangles
1459 int collision_tri_start = (int)m_collision_tris.size();
1460 for (int i=0; i<(int)index_count/3; i++)
1461 {
1462 int triID = addCollisionTri(vertices[indices[i*3]], vertices[indices[i*3+1]], vertices[indices[i*3+2]], gm);
1463 if (collTris)
1464 collTris->push_back(triID);
1465 }
1466
1467 // Submit the mesh record
1468 collision_mesh_t rec;
1469 rec.mesh_name = meshname;
1470 rec.source_name = srcname;
1471 rec.position = pos;
1472 rec.orientation = q;
1473 rec.scale = scale;
1474 rec.ground_model = gm;
1475 rec.num_verts = vertex_count;
1476 rec.num_indices = index_count;
1477 rec.collision_tri_start = collision_tri_start;
1478 rec.collision_tri_count = (int)m_collision_tris.size() - collision_tri_start;
1479 rec.bounding_box = ent->getMesh()->getBounds();
1480 m_collision_meshes.push_back(rec);
1481
1482 // Clean up
1483 delete[] vertices;
1484 delete[] indices;
1485 App::GetGfxScene()->GetSceneManager()->destroyEntity(ent);
1486}
1487
1488void 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)
1489{
1490 // Submit the mesh record
1491 collision_mesh_t rec;
1492 rec.mesh_name = meshname;
1493 rec.source_name = srcname;
1494 rec.position = pos;
1495 rec.ground_model = gm;
1496 rec.collision_tri_start = ctri_start;
1497 rec.collision_tri_count = ctri_count;
1498 rec.bounding_box = bounding_box;
1499 m_collision_meshes.push_back(rec);
1500}
1501
1502void Collisions::getMeshInformation(Mesh* mesh,size_t &vertex_count,Ogre::Vector3* &vertices,
1503 size_t &index_count, unsigned* &indices, const Ogre::Vector3 &position,
1504 const Ogre::Quaternion &orient, const Ogre::Vector3 &scale)
1505{
1506 vertex_count = index_count = 0;
1507
1508 bool added_shared = false;
1509 size_t current_offset = vertex_count;
1510 size_t shared_offset = vertex_count;
1511 size_t next_offset = vertex_count;
1512 size_t index_offset = index_count;
1513 //size_t prev_vert = vertex_count;
1514 //size_t prev_ind = index_count;
1515
1516 // Calculate how many vertices and indices we're going to need
1517 for (int i = 0;i < mesh->getNumSubMeshes();i++)
1518 {
1519 SubMesh* submesh = mesh->getSubMesh(i);
1520
1521 // We only need to add the shared vertices once
1522 if (submesh->useSharedVertices)
1523 {
1524 if (!added_shared)
1525 {
1526 VertexData* vertex_data = mesh->sharedVertexData;
1527 vertex_count += vertex_data->vertexCount;
1528 added_shared = true;
1529 }
1530 } else
1531 {
1532 VertexData* vertex_data = submesh->vertexData;
1533 vertex_count += vertex_data->vertexCount;
1534 }
1535
1536 // Add the indices
1537 Ogre::IndexData* index_data = submesh->indexData;
1538 index_count += index_data->indexCount;
1539 }
1540
1541 // Allocate space for the vertices and indices
1542 vertices = new Vector3[vertex_count];
1543 indices = new unsigned[index_count];
1544
1545 added_shared = false;
1546
1547 // Run through the sub-meshes again, adding the data into the arrays
1548 for (int i = 0;i < mesh->getNumSubMeshes();i++)
1549 {
1550 SubMesh* submesh = mesh->getSubMesh(i);
1551
1552 Ogre::VertexData* vertex_data = submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData;
1553 if ((!submesh->useSharedVertices)||(submesh->useSharedVertices && !added_shared))
1554 {
1555 if (submesh->useSharedVertices)
1556 {
1557 added_shared = true;
1558 shared_offset = current_offset;
1559 }
1560
1561 const Ogre::VertexElement* posElem = vertex_data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);
1562 Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_data->vertexBufferBinding->getBuffer(posElem->getSource());
1563 unsigned char* vertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
1564 Ogre::Real* pReal;
1565
1566 for (size_t j = 0; j < vertex_data->vertexCount; ++j, vertex += vbuf->getVertexSize())
1567 {
1568 posElem->baseVertexPointerToElement(vertex, &pReal);
1569
1570 Vector3 pt;
1571
1572 pt.x = (*pReal++);
1573 pt.y = (*pReal++);
1574 pt.z = (*pReal++);
1575
1576 pt = (orient * (pt * scale)) + position;
1577
1578 vertices[current_offset + j].x = pt.x;
1579 vertices[current_offset + j].y = pt.y;
1580 vertices[current_offset + j].z = pt.z;
1581 }
1582 vbuf->unlock();
1583 next_offset += vertex_data->vertexCount;
1584 }
1585
1586 Ogre::IndexData* index_data = submesh->indexData;
1587
1588 size_t numTris = index_data->indexCount / 3;
1589 unsigned short* pShort = 0;
1590 unsigned int* pInt = 0;
1591 Ogre::HardwareIndexBufferSharedPtr ibuf = index_data->indexBuffer;
1592
1593 bool use32bitindexes = (ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT);
1594
1595 if (use32bitindexes)
1596 pInt = static_cast<unsigned int*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
1597 else
1598 pShort = static_cast<unsigned short*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
1599
1600 for (size_t k = 0; k < numTris; ++k)
1601 {
1602 size_t offset = (submesh->useSharedVertices)?shared_offset:current_offset;
1603
1604 unsigned int vindex = use32bitindexes? *pInt++ : *pShort++;
1605 indices[index_offset + 0] = vindex + (unsigned int)offset;
1606 vindex = use32bitindexes? *pInt++ : *pShort++;
1607 indices[index_offset + 1] = vindex + (unsigned int)offset;
1608 vindex = use32bitindexes? *pInt++ : *pShort++;
1609 indices[index_offset + 2] = vindex + (unsigned int)offset;
1610
1611 index_offset += 3;
1612 }
1613 ibuf->unlock();
1614 current_offset = next_offset;
1615 }
1616}
1617
Central state/object manager and communications hub.
#define TOSTRING(x)
Definition Application.h:57
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:371
ActorType ar_driveable
Sim attr; marks vehicle type and features.
Definition Actor.h:431
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:512
Ogre::Vector3 GetNormalAt(float x, float y, float z)
Definition Terrain.cpp:517
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