Rigs of Rods 2026.01
Soft-body Physics Simulation
Loading...
Searching...
No Matches
ProceduralRoad.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
6 For more information, see http://www.rigsofrods.org/
7
8 Rigs of Rods is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License version 3, as
10 published by the Free Software Foundation.
11
12 Rigs of Rods is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
19*/
20
21#include "ProceduralRoad.h"
22
23#include "Actor.h"
24#include "Application.h"
25#include "Collisions.h"
26#include "Console.h"
27#include "ContentManager.h"
28#include "GameContext.h"
29#include "GfxScene.h"
30#include "Terrain.h"
31
32#include <Ogre.h>
33
34using namespace Ogre;
35using namespace RoR;
36
37static int id_counter = 0;
38
39ProceduralRoad::ProceduralRoad(const std::string& _groundmodelName)
40{
41 mid = id_counter++;
42 if (_groundmodelName != "")
43 {
46 {
47 LOG(fmt::format("[RoR|ProceduralRoad] Custom ground model '{}' not found, using defaults.", _groundmodelName));
48 }
49 }
50}
51
53{
54 if (snode)
55 {
56 App::GetGfxScene()->GetSceneManager()->destroySceneNode(snode);
57 snode = nullptr;
58 }
59 if (msh)
60 {
61 MeshManager::getSingleton().remove(msh->getName());
62 msh.reset();
63 }
64 for (int number : registeredCollTris)
65 {
67 }
68}
69
70void ProceduralRoad::finish(Ogre::SceneNode* groupingSceneNode, const std::string& materialName)
71{
72 Vector3 pts[8];
74 addQuad(pts[7], pts[6], pts[5], pts[4], TextureFit::TEXFIT_NONE, lastpos, lastpos, lastwidth);
75 addQuad(pts[7], pts[4], pts[3], pts[0], TextureFit::TEXFIT_NONE, lastpos, lastpos, lastwidth);
76 addQuad(pts[3], pts[2], pts[1], pts[0], TextureFit::TEXFIT_NONE, lastpos, lastpos, lastwidth);
77
78 createMesh(materialName);
79 String entity_name = String("RoadSystem_Instance-").append(StringConverter::toString(mid));
80 String mesh_name = String("RoadSystem-").append(StringConverter::toString(mid));
81 Entity* ec = App::GetGfxScene()->GetSceneManager()->createEntity(entity_name, mesh_name);
82 snode = groupingSceneNode->createChildSceneNode();
83 snode->attachObject(ec);
84
85 if (collision)
86 {
88 "RoadSystem", mesh_name,
89 ec->getBoundingBox().getCenter(), ec->getMesh()->getBounds(),
90 /*groundmodel:*/nullptr, registeredCollTris[0], (int)registeredCollTris.size());
91 }
92}
93
94void ProceduralRoad::addBlock(Vector3 pos, Quaternion rot, RoadType type, float width, float bwidth, float bheight, int pillartype)
95{
96 if (type == RoadType::ROAD_AUTOMATIC)
97 {
98 width = 10.0;
99 bwidth = 1.4;
100 bheight = 0.2;
101 //define type
102 Vector3 leftv = pos + rot * Vector3(0, 0, bwidth + width / 2.0);
103 Vector3 rightv = pos + rot * Vector3(0, 0, -bwidth - width / 2.0);
104 float dleft = leftv.y - RoR::App::GetGameContext()->GetTerrain()->getHeightAt(leftv.x, leftv.z);
105 float dright = rightv.y - RoR::App::GetGameContext()->GetTerrain()->getHeightAt(rightv.x, rightv.z);
106 if (dleft < bheight + 0.1 && dright < bheight + 0.1)
107 type = RoadType::ROAD_FLAT;
108 if (dleft < bheight + 0.1 && dright >= bheight + 0.1 && dright < 4.0)
109 type = RoadType::ROAD_LEFT;
110 if (dleft >= bheight + 0.1 && dleft < 4.0 && dright < bheight + 0.1)
112 if (dleft >= bheight + 0.1 && dleft < 4.0 && dright >= bheight + 0.1 && dright < 4.0)
113 type = RoadType::ROAD_BOTH;
114 if (type == RoadType::ROAD_AUTOMATIC)
116 if (type != RoadType::ROAD_FLAT)
117 {
118 width = 10.0;
119 bwidth = 0.4;
120 bheight = 0.5;
121 };
122 }
123
124 Vector3 pts[8];
125 if (!first)
126 {
127 Vector3 lpts[8];
128 if (type == RoadType::ROAD_MONORAIL)
129 pos.y += 2;
130
131 computePoints(pts, pos, rot, type, width, bwidth, bheight);
133
134 //tarmac
135 if (type == RoadType::ROAD_MONORAIL)
136 addQuad(pts[4], lpts[4], lpts[3], pts[3], TextureFit::TEXFIT_CONCRETETOP, pos, lastpos, width);
137 else
138 addQuad(pts[4], lpts[4], lpts[3], pts[3], TextureFit::TEXFIT_ROAD, pos, lastpos, width);
139
141 {
142 //sides (close)
143 addQuad(pts[5], lpts[5], lpts[4], pts[4], TextureFit::TEXFIT_ROADS3, pos, lastpos, width);
144 addQuad(pts[3], lpts[3], lpts[2], pts[2], TextureFit::TEXFIT_ROADS2, pos, lastpos, width);
145 //sides (far)
146 addQuad(pts[6], lpts[6], lpts[5], pts[5], TextureFit::TEXFIT_ROADS4, pos, lastpos, width);
147 addQuad(pts[2], lpts[2], lpts[1], pts[1], TextureFit::TEXFIT_ROADS1, pos, lastpos, width);
148 }
149 else
150 {
151 //sides (close)
152 addQuad(pts[5], lpts[5], lpts[4], pts[4], TextureFit::TEXFIT_CONCRETEWALLI, pos, lastpos, width, (type == RoadType::ROAD_FLAT || type == RoadType::ROAD_LEFT));
153 addQuad(pts[3], lpts[3], lpts[2], pts[2], TextureFit::TEXFIT_CONCRETEWALLI, pos, lastpos, width, !(type == RoadType::ROAD_FLAT || type == RoadType::ROAD_RIGHT));
154 //sides (far)
155 addQuad(pts[6], lpts[6], lpts[5], pts[5], TextureFit::TEXFIT_CONCRETETOP, pos, lastpos, width, (type == RoadType::ROAD_FLAT || type == RoadType::ROAD_LEFT));
156 addQuad(pts[2], lpts[2], lpts[1], pts[1], TextureFit::TEXFIT_CONCRETETOP, pos, lastpos, width, !(type == RoadType::ROAD_FLAT || type == RoadType::ROAD_RIGHT));
157 }
159 {
160 //walls
161 addQuad(pts[1], lpts[1], lpts[0], pts[0], TextureFit::TEXFIT_CONCRETEWALL, pos, lastpos, width);
162 addQuad(lpts[6], pts[6], pts[7], lpts[7], TextureFit::TEXFIT_CONCRETEWALL, pos, lastpos, width);
163 //underside - we flip the underside so it folds gracefully with the top
164 addQuad(pts[0], lpts[0], lpts[7], pts[7], TextureFit::TEXFIT_CONCRETEUNDER, pos, lastpos, width, true);
165 }
166 else
167 {
168 //walls
169 addQuad(pts[1], lpts[1], lpts[0], pts[0], TextureFit::TEXFIT_BRICKWALL, pos, lastpos, width);
170 addQuad(lpts[6], pts[6], pts[7], lpts[7], TextureFit::TEXFIT_BRICKWALL, pos, lastpos, width);
171 }
172 if ((type == RoadType::ROAD_BRIDGE || type == RoadType::ROAD_MONORAIL) && pillartype > 0)
173 {
174 /* this is the basic bridge pillar mod.
175 * it will create on pillar for each segment!
176 * @todo: create only a few pillars instead of so much!
177 */
178 // construct the pillars
179 Vector3 leftv = pos + rot * Vector3(0, 0, bwidth + width / 2.0);
180 Vector3 rightv = pos + rot * Vector3(0, 0, -bwidth - width / 2.0);
181 Vector3 middle = lpts[0] - ((lpts[0] + (pts[1] - lpts[0]) / 2) -
182 (lpts[7] + (pts[6] - lpts[7]) / 2)) * 0.5;
183 float heightleft = RoR::App::GetGameContext()->GetTerrain()->getHeightAt(leftv.x, leftv.z);
184 float heightright = RoR::App::GetGameContext()->GetTerrain()->getHeightAt(rightv.x, rightv.z);
185 float heightmiddle = RoR::App::GetGameContext()->GetTerrain()->getHeightAt(middle.x, middle.z);
186
187 bool builtpillars = true;
188
189 float sidefactor = 0.5; // 0.5 = middle
190 // only re-position short pillars! (< 10 meters)
191 // so big bridge pillars do not get repositioned
192 if (pos.y - heightmiddle < 10)
193 {
194 if (heightleft >= heightright)
195 sidefactor = 0.8;
196 else
197 sidefactor = 0.2;
198 }
199
200 static int pillarcounter = 0;
201 pillarcounter++;
202
203 if (pillartype == 2)
204 {
205 // always in the middle
206 sidefactor = 0.5;
207 // only build every fifth pillar
208 if (pillarcounter % 5)
209 builtpillars = false;
210 }
211
212 middle = lpts[0] - ((lpts[0] + (pts[1] - lpts[0]) / 2) -
213 (lpts[7] + (pts[6] - lpts[7]) / 2)) * sidefactor;
214 float len = middle.y - RoR::App::GetGameContext()->GetTerrain()->getHeightAt(middle.x, middle.z) + 5;
215 float width2 = len / 30;
216
217 if (pillartype == 2 && len > 20)
218 // no over-long pillars
219 builtpillars = false;
220
221 // do not draw too small pillars, the bridge may hold without them ;)
222 if (width2 > 5)
223 width2 = 5;
224
225 if (pillartype == 2)
226 width2 = 0.2;
227
228 if (width2 >= 0.2 && builtpillars)
229 {
230 //sides
231 addQuad(middle + Vector3(-width2, -len, -width2),
232 middle + Vector3(-width2, 0, -width2),
233 middle + Vector3(width2, 0, -width2),
234 middle + Vector3(width2, -len, -width2),
236
237 addQuad(middle + Vector3(width2, -len, width2),
238 middle + Vector3(width2, 0, width2),
239 middle + Vector3(-width2, 0, width2),
240 middle + Vector3(-width2, -len, width2),
242
243 addQuad(middle + Vector3(-width2, -len, width2),
244 middle + Vector3(-width2, 0, width2),
245 middle + Vector3(-width2, 0, -width2),
246 middle + Vector3(-width2, -len, -width2),
248
249 addQuad(middle + Vector3(width2, -len, -width2),
250 middle + Vector3(width2, 0, -width2),
251 middle + Vector3(width2, 0, width2),
252 middle + Vector3(width2, -len, width2),
254 }
255 }
256 }
257 else
258 {
259 first = false;
260 computePoints(pts, pos, rot, type, width, bwidth, bheight);
261 addQuad(pts[0], pts[1], pts[2], pts[3], TextureFit::TEXFIT_NONE, pos, pos, width);
262 addQuad(pts[0], pts[3], pts[4], pts[7], TextureFit::TEXFIT_NONE, pos, pos, width);
263 addQuad(pts[4], pts[5], pts[6], pts[7], TextureFit::TEXFIT_NONE, pos, pos, width);
264 }
265 lastpos = pos;
266 lastrot = rot;
267 lastwidth = width;
268 lastbwidth = bwidth;
269 lastbheight = bheight;
270 lasttype = type;
271
272 if (App::diag_terrn_log_roads->getBool())
273 {
274 Str<2000> msg; msg << "[RoR] Road Block |";
275 msg << " pos=(" << pos.x << " " << pos.y << " " << pos.z << ")";
276 msg << " rot=(" << rot.x << " " << rot.y << " " << rot.z << ")";
277 msg << " width=" << width;
278 msg << " bwidth=" << bwidth;
279 msg << " bheight=" << bheight;
280 msg << " type=" << (int)type;
281 for (int i = 0; i < 8; ++i)
282 {
283 msg << "\n\t Point#" << i << ": " << pts[i].x << " " << pts[i].y << " " << pts[i].z;
284 }
285 Log(msg.ToCStr());
286 }
287}
288
289void ProceduralRoad::computePoints(Vector3* pts, Vector3 pos, Quaternion rot, RoadType type, float width, float bwidth, float bheight)
290{
291 if (type == RoadType::ROAD_FLAT)
292 {
293 pts[1] = pos + rot * Vector3(0, -bheight, bwidth + width / 2.0);
294 pts[0] = baseOf(pts[1]);
295 pts[2] = pos + rot * Vector3(0, -bheight / 4.0, bwidth / 3.0 + width / 2.0);
296 pts[3] = pos + rot * Vector3(0, 0, width / 2.0);
297 pts[4] = pos + rot * Vector3(0, 0, -width / 2.0);
298 pts[5] = pos + rot * Vector3(0, -bheight / 4.0, -bwidth / 3.0 - width / 2.0);
299 pts[6] = pos + rot * Vector3(0, -bheight, -bwidth - width / 2.0);
300 pts[7] = baseOf(pts[6]);
301 }
302 if (type == RoadType::ROAD_BOTH)
303 {
304 pts[1] = pos + rot * Vector3(0, bheight, bwidth + width / 2.0);
305 pts[0] = baseOf(pts[1]);
306 pts[2] = pos + rot * Vector3(0, bheight, width / 2.0);
307 pts[3] = pos + rot * Vector3(0, 0, width / 2.0);
308 pts[4] = pos + rot * Vector3(0, 0, -width / 2.0);
309 pts[5] = pos + rot * Vector3(0, bheight, -width / 2.0);
310 pts[6] = pos + rot * Vector3(0, bheight, -bwidth - width / 2.0);
311 pts[7] = baseOf(pts[6]);
312 }
313 if (type == RoadType::ROAD_LEFT)
314 {
315 pts[1] = pos + rot * Vector3(0, -bheight, bwidth + width / 2.0);
316 pts[0] = baseOf(pts[1]);
317 pts[2] = pos + rot * Vector3(0, -bheight / 4.0, bwidth / 3.0 + width / 2.0);
318 pts[3] = pos + rot * Vector3(0, 0, width / 2.0);
319 pts[4] = pos + rot * Vector3(0, 0, -width / 2.0);
320 pts[5] = pos + rot * Vector3(0, bheight, -width / 2.0);
321 pts[6] = pos + rot * Vector3(0, bheight, -bwidth - width / 2.0);
322 pts[7] = baseOf(pts[6]);
323 }
324 if (type == RoadType::ROAD_RIGHT)
325 {
326 pts[1] = pos + rot * Vector3(0, bheight, bwidth + width / 2.0);
327 pts[0] = baseOf(pts[1]);
328 pts[2] = pos + rot * Vector3(0, bheight, width / 2.0);
329 pts[3] = pos + rot * Vector3(0, 0, width / 2.0);
330 pts[4] = pos + rot * Vector3(0, 0, -width / 2.0);
331 pts[5] = pos + rot * Vector3(0, -bheight / 4.0, -bwidth / 3.0 - width / 2.0);
332 pts[6] = pos + rot * Vector3(0, -bheight, -bwidth - width / 2.0);
333 pts[7] = baseOf(pts[6]);
334 }
335 if (type == RoadType::ROAD_BRIDGE)
336 {
337 pts[0] = pos + rot * Vector3(0, -0.4, bwidth + width / 2.0);
338 pts[1] = pos + rot * Vector3(0, bheight, bwidth + width / 2.0);
339 pts[2] = pos + rot * Vector3(0, bheight, width / 2.0);
340 pts[3] = pos + rot * Vector3(0, 0, width / 2.0);
341 pts[4] = pos + rot * Vector3(0, 0, -width / 2.0);
342 pts[5] = pos + rot * Vector3(0, bheight, -width / 2.0);
343 pts[6] = pos + rot * Vector3(0, bheight, -bwidth - width / 2.0);
344 pts[7] = pos + rot * Vector3(0, -0.4, -bwidth - width / 2.0);
345 }
346 if (type == RoadType::ROAD_MONORAIL)
347 {
348 pts[0] = pos + rot * Vector3(0, -1.4, bwidth + width / 2.0);
349 pts[1] = pos + rot * Vector3(0, bheight, bwidth + width / 2.0);
350 pts[2] = pos + rot * Vector3(0, bheight, width / 2.0);
351 pts[3] = pos + rot * Vector3(0, 0, width / 2.0);
352 pts[4] = pos + rot * Vector3(0, 0, -width / 2.0);
353 pts[5] = pos + rot * Vector3(0, bheight, -width / 2.0);
354 pts[6] = pos + rot * Vector3(0, bheight, -bwidth - width / 2.0);
355 pts[7] = pos + rot * Vector3(0, -1.4, -bwidth - width / 2.0);
356 }
357}
358
359inline Vector3 ProceduralRoad::baseOf(Vector3 p)
360{
361 float y = RoR::App::GetGameContext()->GetTerrain()->getHeightAt(p.x, p.z) - 0.01;
362
363 if (y > p.y)
364 {
365 y = p.y - 0.01;
366 }
367
368 return Vector3(p.x, y, p.z);
369}
370
371void ProceduralRoad::addQuad(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4, TextureFit texfit, Vector3 pos, Vector3 lastpos, float width, bool flip)
372{
373 if (vertexcount + 3 >= MAX_VERTEX || tricount * 3 + 3 + 2 >= MAX_TRIS * 3)
374 return;
375 Vector2 texf[4];
376 textureFit(p1, p2, p3, p4, texfit, texf, pos, lastpos, width);
377 //vertexes
378 vertex[vertexcount] = p1;
379 tex[vertexcount] = texf[0];
380 vertex[vertexcount + 1] = p2;
381 tex[vertexcount + 1] = texf[1];
382 vertex[vertexcount + 2] = p3;
383 tex[vertexcount + 2] = texf[2];
384 vertex[vertexcount + 3] = p4;
385 tex[vertexcount + 3] = texf[3];
386 //tris
387 if (flip)
388 {
390 tris[tricount * 3 + 1] = vertexcount + 1;
391 tris[tricount * 3 + 2] = vertexcount + 3;
392 tris[tricount * 3 + 3] = vertexcount + 1;
393 tris[tricount * 3 + 3 + 1] = vertexcount + 2;
394 tris[tricount * 3 + 3 + 2] = vertexcount + 3;
395 }
396 else
397 {
399 tris[tricount * 3 + 1] = vertexcount + 1;
400 tris[tricount * 3 + 2] = vertexcount + 2;
401 tris[tricount * 3 + 3] = vertexcount;
402 tris[tricount * 3 + 3 + 1] = vertexcount + 2;
403 tris[tricount * 3 + 3 + 2] = vertexcount + 3;
404 }
405 if (collision)
406 {
408 if (!gm)
409 {
412 else
414 }
415 addCollisionQuad(p1, p2, p3, p4, gm, flip);
416 }
417 tricount += 2;
418 vertexcount += 4;
419}
420
421void ProceduralRoad::textureFit(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4, TextureFit texfit, Vector2* texc, Vector3 pos, Vector3 lastpos, float width)
422{
423 int i;
424
426 {
427 Vector3 ps[4];
428 ps[0] = p1;
429 ps[1] = p2;
430 ps[2] = p3;
431 ps[3] = p4;
432 Vector3 pref1 = pos;
433 Vector3 pref2 = lastpos;
434 //make matrix
435 Vector3 bx = pref2 - pref1;
436 bx.normalise();
437 Vector3 by = Vector3::UNIT_Y;
438 Vector3 bz = bx.crossProduct(by);
439 //coordinates change matrix
440 Matrix3 reverse;
441 reverse.SetColumn(0, bx);
442 reverse.SetColumn(1, by);
443 reverse.SetColumn(2, bz);
444 Matrix3 forward;
445 forward = reverse.Inverse();
446 //transpose
447 for (i = 0; i < 4; i++)
448 {
449 Vector3 trv = forward * (ps[i] - pref1);
450 if (texfit == TextureFit::TEXFIT_BRICKWALL)
451 {
452 float ty = 0.746 - trv.y * 0.25 / 4.5;
453 // fix overlapping
454 if (ty > 1)
455 ty = 1;
456 texc[i] = Vector2(trv.x / 10.0, ty);
457 }
459 {
460 // fix overlapping
461 float ty = 0.496 - (trv.y - 0.7) * 0.25 / 4.5;
462 if (ty > 1)
463 ty = 1;
464 texc[i] = Vector2(trv.x / 10.0, ty);
465 }
467 {
468 float ty = 0.496 + trv.y * 0.25 / 4.5;
469 // fix overlapping
470 if (ty > 1)
471 ty = 1;
472 texc[i] = Vector2(trv.x / 10.0, ty);
473 }
474 }
475 return;
476 }
478 {
479 Vector3 ps[4];
480 ps[0] = p1;
481 ps[1] = p2;
482 ps[2] = p3;
483 ps[3] = p4;
484 Vector3 pref1 = pos;
485 Vector3 pref2 = lastpos;
486 //project
487 for (i = 0; i < 4; i++)
488 ps[i].y = 0;
489 pref1.y = 0;
490 pref2.y = 0;
491 //make matrix
492 Vector3 bx = pref2 - pref1;
493 bx.normalise();
494 Vector3 by = Vector3::UNIT_Y;
495 Vector3 bz = bx.crossProduct(by);
496 //coordinates change matrix
497 Matrix3 reverse;
498 reverse.SetColumn(0, bx);
499 reverse.SetColumn(1, by);
500 reverse.SetColumn(2, bz);
501 Matrix3 forward;
502 forward = reverse.Inverse();
503 //transpose
504 float trvrefz = 0.0;
505 for (i = 0; i < 4; i++)
506 {
507 Vector3 trv = forward * (ps[i] - pref1);
508 if (texfit == TextureFit::TEXFIT_CONCRETETOP)
509 {
510 if (i == 0)
511 trvrefz = trv.z;
512 texc[i] = Vector2(trv.x / 10.0, 0.621 + (trv.z - trvrefz) * 0.25 / 4.5);
513 }
514 else
515 {
516 float v1 = 0.072;
517 float v2 = 0.423;
518 if (texfit == TextureFit::TEXFIT_ROADS1)
519 {
520 v1 = 0.001;
521 v2 = 0.036;
522 };
523 if (texfit == TextureFit::TEXFIT_ROADS2)
524 {
525 v1 = 0.036;
526 v2 = 0.072;
527 };
528 if (texfit == TextureFit::TEXFIT_ROADS3)
529 {
530 v1 = 0.423;
531 v2 = 0.458;
532 };
533 if (texfit == TextureFit::TEXFIT_ROADS4)
534 {
535 v1 = 0.458;
536 v2 = 0.493;
537 };
539 {
540 v1 = 0.496;
541 v2 = 0.745;
542 };
543 if (i < 2)
544 texc[i] = Vector2(trv.x / 10.0, v1);
545 else
546 texc[i] = Vector2(trv.x / 10.0, v2);
547 }
548 }
549 return;
550 }
551 //default
552 for (i = 0; i < 4; i++)
553 texc[i] = Vector2(0, 0);
554}
555
556void ProceduralRoad::addCollisionQuad(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4, ground_model_t* gm, bool flip)
557{
558 int triID = 0;
559 if (flip)
560 {
561 triID = App::GetGameContext()->GetTerrain()->GetCollisions()->addCollisionTri(p1, p2, p4, gm);
562 if (triID >= 0)
563 registeredCollTris.push_back(triID);
564
565 triID = App::GetGameContext()->GetTerrain()->GetCollisions()->addCollisionTri(p4, p2, p3, gm);
566 if (triID >= 0)
567 registeredCollTris.push_back(triID);
568 }
569 else
570 {
571 triID = App::GetGameContext()->GetTerrain()->GetCollisions()->addCollisionTri(p1, p2, p3, gm);
572 if (triID >= 0)
573 registeredCollTris.push_back(triID);
574
575 triID = App::GetGameContext()->GetTerrain()->GetCollisions()->addCollisionTri(p1, p3, p4, gm);
576 if (triID >= 0)
577 registeredCollTris.push_back(triID);
578 }
579}
580
581void ProceduralRoad::addCollisionQuad(Ogre::Vector3 p1, Ogre::Vector3 p2, Ogre::Vector3 p3, Ogre::Vector3 p4, std::string const& gm_name, bool flip /*= false*/)
582{
584 if (gm)
585 {
586 this->addCollisionQuad(p1, p2, p3, p4, gm, flip);
587 }
588 else
589 {
591 fmt::format("ProceduralRoad::addCollisionQuad() - ground model '{}' does not exist", gm_name));
592 }
593}
594
595void ProceduralRoad::createMesh(const std::string& materialName /*= "" */)
596{
597 ROR_ASSERT(App::GetGameContext()->GetTerrain());
598
599 AxisAlignedBox aab;
600 union
601 {
602 float* vertices;
603 CoVertice_t* covertices;
604 };
606 Ogre::String mesh_name = Ogre::String("RoadSystem-").append(Ogre::StringConverter::toString(mid));
607 msh = MeshManager::getSingleton().createManual(mesh_name, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
608
609 mainsub = msh->createSubMesh();
610 // Make sure custom material exists
611 if (materialName == "")
612 {
613 mainsub->setMaterialName(PROROAD_DEFAULT_MATERIAL, ContentManager::ResourcePack::MATERIALS.resource_group_name);
614 }
615 else if (!MaterialManager::getSingleton().resourceExists(materialName, App::GetGameContext()->GetTerrain()->getTerrainFileResourceGroup()))
616 {
617 LOG(fmt::format("[RoR|ProceduralRoad] Custom material '{}' does not exist. Using default.", materialName));
618 mainsub->setMaterialName(PROROAD_DEFAULT_MATERIAL, ContentManager::ResourcePack::MATERIALS.resource_group_name);
619 }
620 else
621 {
622 mainsub->setMaterialName(materialName, App::GetGameContext()->GetTerrain()->getTerrainFileResourceGroup());
623 }
624
626 size_t vbufCount = (2 * 3 + 2) * vertexcount;
627 vertices = (float*)malloc(vbufCount * sizeof(float));
628 int i;
629 //fill values
630 for (i = 0; i < vertexcount; i++)
631 {
632 covertices[i].texcoord = tex[i];
633 covertices[i].vertex = vertex[i];
634 //normals are computed later
635 covertices[i].normal = Vector3::ZERO;
636 aab.merge(vertex[i]);
637 }
638
640 size_t ibufCount = 3 * tricount;
641
642 //compute normals
643 for (i = 0; i < tricount && i * 3 + 2 < MAX_TRIS * 3; i++)
644 {
645 Vector3 v1, v2;
646 v1 = covertices[tris[i * 3 + 1]].vertex - covertices[tris[i * 3]].vertex;
647 v2 = covertices[tris[i * 3 + 2]].vertex - covertices[tris[i * 3]].vertex;
648 v1 = v1.crossProduct(v2);
649 v1.normalise();
650 covertices[tris[i * 3]].normal += v1;
651 covertices[tris[i * 3 + 1]].normal += v1;
652 covertices[tris[i * 3 + 2]].normal += v1;
653 }
654 //normalize
655 for (i = 0; i < vertexcount; i++)
656 {
657 covertices[i].normal.normalise();
658 }
659
661 msh->sharedVertexData = new VertexData();
662 msh->sharedVertexData->vertexCount = vertexcount;
663
665 VertexDeclaration* decl = msh->sharedVertexData->vertexDeclaration;
666 size_t offset = 0;
667 decl->addElement(0, offset, VET_FLOAT3, VES_POSITION);
668 offset += VertexElement::getTypeSize(VET_FLOAT3);
669 decl->addElement(0, offset, VET_FLOAT3, VES_NORMAL);
670 offset += VertexElement::getTypeSize(VET_FLOAT3);
671 decl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0);
672 offset += VertexElement::getTypeSize(VET_FLOAT2);
673
676 HardwareVertexBufferSharedPtr vbuf =
677 HardwareBufferManager::getSingleton().createVertexBuffer(
678 offset, msh->sharedVertexData->vertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
679
681 vbuf->writeData(0, vbuf->getSizeInBytes(), vertices, true);
682
684 VertexBufferBinding* bind = msh->sharedVertexData->vertexBufferBinding;
685 bind->setBinding(0, vbuf);
686
687 //for the face
689 HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton().
690 createIndexBuffer(
691 HardwareIndexBuffer::IT_16BIT,
692 ibufCount,
693 HardwareBuffer::HBU_STATIC_WRITE_ONLY);
694
696 ibuf->writeData(0, ibuf->getSizeInBytes(), tris, true);
697
699 mainsub->useSharedVertices = true;
700 mainsub->indexData->indexBuffer = ibuf;
701 mainsub->indexData->indexCount = ibufCount;
702 mainsub->indexData->indexStart = 0;
703
704 msh->_setBounds(aab, true);
705
707 msh->load();
708
709 free(vertices);
710};
711
quaternion Log() const
Central state/object manager and communications hub.
#define ROR_ASSERT(_EXPR)
Definition Application.h:40
void LOG(const char *msg)
Legacy alias - formerly a macro.
Game state manager and message-queue provider.
static int id_counter
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.
int addCollisionTri(Ogre::Vector3 p1, Ogre::Vector3 p2, Ogre::Vector3 p3, ground_model_t *gm)
ground_model_t * getGroundModelByString(const Ogre::String name)
void removeCollisionTri(int number)
@ CONSOLE_MSGTYPE_TERRN
Parsing/spawn/simulation messages for terrain.
Definition Console.h:64
void putMessage(MessageArea area, MessageType type, std::string const &msg, std::string icon="")
Definition Console.cpp:103
@ CONSOLE_SYSTEM_WARNING
Definition Console.h:53
const TerrainPtr & GetTerrain()
Ogre::SceneManager * GetSceneManager()
Definition GfxScene.h:83
static const unsigned int MAX_TRIS
static const unsigned int MAX_VERTEX
Ogre::SceneNode * snode
void textureFit(Ogre::Vector3 p1, Ogre::Vector3 p2, Ogre::Vector3 p3, Ogre::Vector3 p4, TextureFit texfit, Ogre::Vector2 *texc, Ogre::Vector3 pos, Ogre::Vector3 lastpos, float width)
uint16_t tris[MAX_TRIS *3]
ProceduralRoad(const std::string &_groundmodelName="")
void finish(Ogre::SceneNode *snode, const std::string &materialName="")
If materialName is empty, the default material will be used.
Ogre::Vector2 tex[MAX_VERTEX]
virtual ~ProceduralRoad() override
void addCollisionQuad(Ogre::Vector3 p1, Ogre::Vector3 p2, Ogre::Vector3 p3, Ogre::Vector3 p4, ground_model_t *gm, bool flip=false)
std::vector< int > registeredCollTris
void addBlock(Ogre::Vector3 pos, Ogre::Quaternion rot, RoadType type, float width, float bwidth, float bheight, int pillartype=1)
void addQuad(Ogre::Vector3 p1, Ogre::Vector3 p2, Ogre::Vector3 p3, Ogre::Vector3 p4, TextureFit texfit, Ogre::Vector3 pos, Ogre::Vector3 lastpos, float width, bool flip=false)
bool collision
Register collision triangles?
Ogre::Vector3 vertex[MAX_VERTEX]
Ogre::Vector3 baseOf(Ogre::Vector3 p)
void createMesh(const std::string &materialName="")
If materialName is empty, the default material will be used.
Ogre::Vector3 lastpos
Ogre::SubMesh * mainsub
ground_model_t * m_customGroundmodel
Ogre::Quaternion lastrot
void computePoints(Ogre::Vector3 *pts, Ogre::Vector3 pos, Ogre::Quaternion rot, RoadType type, float width, float bwidth, float bheight)
Wrapper for classic c-string (local buffer) Refresher: strlen() excludes '\0' terminator; strncat() A...
Definition Str.h:36
const char * ToCStr() const
Definition Str.h:46
float getHeightAt(float x, float z)
Definition Terrain.cpp:511
Collisions * GetCollisions()
Definition Terrain.h:86
static constexpr const char * PROROAD_DEFAULT_MATERIAL
GameContext * GetGameContext()
CVar * diag_terrn_log_roads
GfxScene * GetGfxScene()
Console * GetConsole()
static const ResourcePack MATERIALS
Surface friction properties.
Definition SimData.h:704