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
RigDef_SequentialImporter.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
25
26#include <iomanip>
28
29#include "Actor.h"
30#include "Application.h"
31#include "Console.h"
32#include "RigDef_Parser.h"
33
34using namespace RigDef;
35
53
54void SequentialImporter::GenerateNodesForWheel(Keyword generated_from, int num_rays, bool has_rigidity_node)
55{
56 // Old parser logic:
57 // Note: Axis nodes are A/B, where B has bigger Z coordinate.
58 //
59 // Section | Function() | Generated nodes
60 // -------------------------------------------------------------------------------------------
61 // wheels | addWheel() | Tyre nodes: A, B, A, B... (num_rays*2)
62 // wheels2 | addWheel2() | Rim nodes: A, B, A, B... (num_rays*2), Tyre nodes: A, B, A, B... (num_rays*2)
63 // meshwheels | addWheel() | Tyre nodes: A, B, A, B... (num_rays*2)
64 // meshwheels2 | addWheel() | Tyre nodes: A, B, A, B... (num_rays*2)
65 // flexbodywheels | addWheel3() | Rim nodes: A, B, A, B... (num_rays*2), Tyre nodes: A, B, A, B... (num_rays*2)
66
68 if (generated_from == Keyword::FLEXBODYWHEELS || generated_from == Keyword::WHEELS2)
69 {
70 // Rim nodes
71 for (int i = 0; i < num_rays*2; ++i)
72 {
74 this->AddGeneratedNode(generated_from, detail);
75 }
76 }
77 // Tyre nodes
78 for (int i = 0; i < num_rays*2; ++i)
79 {
81 this->AddGeneratedNode(generated_from, detail);
82 }
83}
84
86{
87 this->ProcessModule(def->root_module);
88
89 auto end = def->user_modules.end();
90 auto itor = def->user_modules.begin();
91 for (;itor != end; ++itor)
92 {
93 this->ProcessModule((*itor).second);
94 }
95
97 {
99 }
101 {
103 }
104}
105
106void SequentialImporter::AddMessage(Message::Type msg_type, std::string text)
107{
108 RoR::Str<2000> txt;
109 txt << text << "(";
110 if (m_current_module) // Not present while adding nodes, only while resolving
111 {
112 txt << "sectionconfig: " << m_current_module->name << ", ";
113 }
114 txt << "keyword: " << KeywordToString(m_current_keyword) << ")";
115
117 switch (msg_type)
118 {
121 break;
122
126 break;
127
128 default:
130 break;
131 }
132
134}
135
137{
138 // Reverse order of elements in nodes-array
139 // "break" keyword is omitted - control falls through!
140 unsigned out_offset = 0;
141 switch (keyword)
142 {
143 case Keyword::FLEXBODYWHEELS: out_offset += m_num_meshwheels2_nodes; // NO break!
144 case Keyword::MESHWHEELS2: out_offset += m_num_meshwheels_nodes; // NO break!
145 case Keyword::MESHWHEELS: out_offset += m_num_wheels2_nodes; // NO break!
146 case Keyword::WHEELS2: out_offset += m_num_wheels_nodes; // NO break!
147 case Keyword::WHEELS: out_offset += m_num_cinecam_nodes; // NO break!
148 case Keyword::CINECAM: out_offset += m_num_named_nodes; // NO break!
149 case Keyword::NODES2: out_offset += m_num_numbered_nodes; // NO break!
150 case Keyword::NODES: break; // Starts at 0
151 default: break;
152 }
153 return out_offset;
154}
155
156Node::Ref SequentialImporter::ResolveNodeByIndex(unsigned int index_in, unsigned int def_line_number)
157{
158 if (index_in >= m_all_nodes.size())
159 {
160 std::stringstream msg;
161 msg << "Cannot resolve node by index [" << index_in << "], node is not defined, highest available number is: " << m_all_nodes.size() - 1;
162 this->AddMessage(Message::TYPE_ERROR, msg.str());
163 return Node::Ref(); // Invalid reference
164 }
165 NodeMapEntry node_entry = m_all_nodes[index_in];
166 unsigned index_out = this->GetNodeArrayOffset(node_entry.origin_keyword) + node_entry.node_sub_index;
167 if (index_out != index_in)
168 {
169 std::stringstream msg;
170 msg << "Node resolved by index.\n\tSource: [" << index_in << "]\n\tResult: [" << index_out << "]";
171 if (node_entry.origin_keyword != Keyword::NODES && node_entry.origin_keyword != Keyword::NODES2)
172 {
173 msg << " (generated by: " << KeywordToString(node_entry.origin_keyword)
174 << ", sub-index: " << node_entry.node_sub_index << ")";
175 }
176 this->AddMessage(Message::TYPE_INFO, msg.str());
177 }
178 else
179 {
180 ++m_num_resolved_to_self; // The happy statistic -> nodes which resolved to the same index.
181 }
182 return Node::Ref(TOSTRING(index_out), index_out, Node::Ref::IMPORT_STATE_IS_VALID | Node::Ref::IMPORT_STATE_IS_RESOLVED_NUMBERED, def_line_number);
183}
184
186{
188 if (!noderef_in.GetImportState_IsValid())
189 {
190 return noderef_in;
191 }
192
193 // Work like legacy function SerializedRig::parse_node_number()
194 // If node may be named, try resolving it as named first
195 // If that fails, try numbered
196
197 if (noderef_in.GetImportState_MustCheckNamedFirst())
198 {
199 auto result = m_named_nodes.find(noderef_in.Str());
200 if (result != m_named_nodes.end())
201 {
202 return Node::Ref(noderef_in.Str(), 0, Node::Ref::IMPORT_STATE_IS_VALID | Node::Ref::IMPORT_STATE_IS_RESOLVED_NAMED, noderef_in.GetLineNumber());
203 }
204 }
205 if (noderef_in.Num() >= m_all_nodes.size())
206 {
207 // Return exactly what SerializedRig::parse_node_number() would return on this error, for compatibility.
208 // This definitely isn't valid, but it behaves exactly as RoR 0.38, so it's perfectly IMPORT_VALID! :D
209 Node::Ref out_ref("0", 0, Node::Ref::IMPORT_STATE_IS_VALID | Node::Ref::IMPORT_STATE_IS_RESOLVED_NUMBERED, noderef_in.GetLineNumber());
210 std::stringstream msg;
211 msg << "Cannot resolve " << noderef_in.ToString() << " - not a named node, and index is not defined (highest is: "
212 << m_all_nodes.size() - 1 << "). For backwards compatibility, converting to: " << out_ref.ToString();
213 this->AddMessage(Message::TYPE_ERROR, msg.str());
214 return out_ref; // Invalid
215 }
216 auto entry = m_all_nodes[noderef_in.Num()];
217 if (entry.node_id.IsTypeNamed())
218 {
219 Node::Ref out_ref(entry.node_id.Str(), 0, Node::Ref::IMPORT_STATE_IS_VALID | Node::Ref::IMPORT_STATE_IS_RESOLVED_NAMED, noderef_in.GetLineNumber());
220 /* TODO: make optional (debug) or remove
221 std::stringstream msg;
222 msg << "Node resolved\n\tSource: " << noderef_in.ToString() << "\n\tResult: " << out_ref.ToString();
223 this->AddMessage(Message::TYPE_INFO, msg.str());
224 */
225 return out_ref;
226 }
227 else if (entry.node_id.IsTypeNumbered())
228 {
229 unsigned out_index = this->GetNodeArrayOffset(entry.origin_keyword) + entry.node_sub_index;
230 Node::Ref out_ref(TOSTRING(out_index), out_index, Node::Ref::IMPORT_STATE_IS_VALID | Node::Ref::IMPORT_STATE_IS_RESOLVED_NUMBERED, noderef_in.GetLineNumber());
231 /* TODO: make optional (debug) or remove
232 std::stringstream msg;
233 msg << "Node resolved\n\tSource: " << noderef_in.ToString() << "\n\tResult: " << out_ref.ToString()
234 << "\n\tOrigin: " << RigDef::Document::KeywordToString(entry.origin_keyword) << " SubIndex: " << entry.node_sub_index;
235 this->AddMessage(Message::TYPE_INFO, msg.str());
236 */
237 return out_ref;
238 }
239 else
240 {
241 std::stringstream msg;
242 msg << "Cannot resolve " << noderef_in.ToString() << " - found node is not valid";
243 this->AddMessage(Message::TYPE_ERROR, msg.str());
244 return Node::Ref(); // Invalid
245 }
246}
247
248void SequentialImporter::ResolveFlexbodyForset(std::vector<Node::Range>& in_ranges, std::vector<Node::Ref>& out_nodes)
249{
250 auto range_itor = in_ranges.begin();
251 auto range_end = in_ranges.end();
252 for ( ; range_itor != range_end; ++range_itor)
253 {
254 Node::Range& range = *range_itor;
255 if (!range.IsRange())
256 {
257 if (!range.start.GetImportState_IsValid())
258 {
259 std::stringstream msg;
260 msg << "ResolveFlexbodyForset(): Skipping node because it's marked INVALID:" << range.start.ToString();
261 this->AddMessage(Message::TYPE_ERROR, msg.str());
262 }
263 else
264 {
265 Node::Ref node_ref = this->ResolveNodeByIndex(range.start.Num(), range.start.GetLineNumber()); // Forset nodes are numbered-only
266 // Invalid nodes are simply thrown away, for backwards compatibility
267 // (FlexBody loops through existing nodes first, and then evaluates if they are in the SET)
268 if (node_ref.IsValidAnyState())
269 {
270 out_nodes.push_back(node_ref);
271 }
272 else
273 {
274 std::stringstream msg;
275 msg << "ResolveFlexbodyForset(): Stand-alone node [" << range.start.ToString() << "] resolved invalid, removing from FORSET";
276 this->AddMessage(Message::TYPE_WARNING, msg.str());
277 }
278 }
279 }
280 else // It's a range
281 {
282 if (!range.start.GetImportState_IsValid() || !range.end.GetImportState_IsValid())
283 {
284 std::stringstream msg;
285 msg << "ResolveFlexbodyForset(): Skipping range because some nodes are marked INVALID, start: [" << range.start.ToString() << "], end: [" << range.end.ToString() << "]";
286 this->AddMessage(Message::TYPE_ERROR, msg.str());
287 }
288 else if (range.start.GetImportState_IsResolvedNamed() || range.end.GetImportState_IsResolvedNamed())
289 {
290 std::stringstream msg;
291 msg << "ResolveFlexbodyForset(): Some nodes in range are already resolved as named, unable to process, start: [" << range.start.ToString() << "], end: [" << range.end.ToString() << "]";
292 this->AddMessage(Message::TYPE_ERROR, msg.str());
293 }
294 else
295 {
296 unsigned int end_index = range.end.Num();
297 unsigned int line_num = range.start.GetLineNumber();
298 for (unsigned int i = range.start.Num(); i <= end_index; ++i)
299 {
300 Node::Ref node_ref = this->ResolveNodeByIndex(i, line_num); // Forset nodes are numbered-only
301 // Invalid nodes are simply thrown away, for backwards compatibility
302 // (FlexBody loops through existing nodes first, and then evaluates if they are in the SET -> invalid nodes are silently ignored)
303 if (node_ref.IsValidAnyState())
304 {
305 out_nodes.push_back(node_ref);
306 }
307 else
308 {
309 std::stringstream msg;
310 msg << "ResolveFlexbodyForset(): Node ["<<i<<"] from range [" << range.start.ToString() << " - " << range.end.ToString() << "] resolved invalid, removing from FORSET";
311 this->AddMessage(Message::TYPE_WARNING, msg.str());
312 }
313 }
314 }
315 }
316 }
317}
318
319void SequentialImporter::ResolveNodeRanges(std::vector<Node::Range>& ranges)
320{
321 auto in_ranges = ranges; // Copy vector
322 ranges.clear();
323
324 auto range_itor = in_ranges.begin();
325 auto range_end = in_ranges.end();
326 for ( ; range_itor != range_end; ++range_itor)
327 {
328 Node::Range& range = *range_itor;
329 if (!range.IsRange())
330 {
331 ranges.push_back(Node::Range(this->ResolveNode(range.start)));
332 }
333 else if (!range.start.GetImportState_IsValid() || !range.end.GetImportState_IsValid())
334 {
335 std::stringstream msg;
336 msg << "Some nodes in range are invalid, start: [" << range.start.ToString() << "], end: [" << range.end.ToString() << "]";
337 this->AddMessage(Message::TYPE_ERROR, msg.str());
338 }
339 else if (range.start.GetImportState_IsResolvedNamed() || range.end.GetImportState_IsResolvedNamed())
340 {
341 std::stringstream msg;
342 msg << "Some nodes in range are already resolved as named, unable to process, start: [" << range.start.ToString() << "], end: [" << range.end.ToString() << "]";
343 this->AddMessage(Message::TYPE_ERROR, msg.str());
344 }
345 else
346 {
347 unsigned int end_index = range.end.Num();
348 for (unsigned int i = range.start.Num(); i < end_index; ++i)
349 {
350 ranges.push_back(Node::Range(this->ResolveNodeByIndex(i, range.start.GetLineNumber())));
351 }
352 }
353 }
354}
355
356#define RESOLVE_OPTIONAL_SECTION(KEYWORD, PTRNAME, BLOCK) \
357{ \
358 m_current_keyword = KEYWORD; \
359 if (PTRNAME) \
360 { \
361 BLOCK \
362 } \
363 m_current_keyword = Keyword::INVALID; \
364}
365
366#define FOR_EACH(KEYWORD, VECTOR, VARNAME, BLOCK) \
367{ \
368 m_current_keyword = KEYWORD; \
369 auto itor_ = VECTOR.begin(); \
370 auto end_ = VECTOR.end(); \
371 for (; itor_ != end_; ++itor_) \
372 { \
373 auto& VARNAME = *itor_; \
374 BLOCK \
375 } \
376 m_current_keyword = Keyword::INVALID; \
377}
378
379#define RESOLVE(VARNAME) VARNAME = this->ResolveNode(VARNAME);
380
381#define RESOLVE_VECTOR(VECTORNAME) \
382{ \
383 auto end = VECTORNAME.end(); \
384 for ( auto itor = VECTORNAME.begin(); itor != end; ++itor) \
385 { \
386 RESOLVE(*itor); \
387 } \
388}
389
390// Support for named nodes in parsers [version 0.39, version 0.4.0.7]
391// airbrakes = yes,yes
392// axles = NO, NO
393
394void SequentialImporter::ProcessModule(std::shared_ptr<RigDef::Document::Module> module)
395{
396 m_current_module = module;
397
398 FOR_EACH (Keyword::AIRBRAKES, module->airbrakes, airbrake,
399 {
400 RESOLVE(airbrake.reference_node);
401 RESOLVE(airbrake.x_axis_node );
402 RESOLVE(airbrake.y_axis_node );
403 RESOLVE(airbrake.aditional_node);
404 });
405
406 FOR_EACH (Keyword::AXLES, module->axles, axle,
407 {
408 RESOLVE(axle.wheels[0][0]);
409 RESOLVE(axle.wheels[0][1]);
410 RESOLVE(axle.wheels[1][0]);
411 RESOLVE(axle.wheels[1][1]);
412 });
413
414 FOR_EACH (Keyword::BEAMS, module->beams, beam,
415 {
416 RESOLVE(beam.nodes[0]);
417 RESOLVE(beam.nodes[1]);
418 });
419
420 FOR_EACH (Keyword::CAMERAS, module->cameras, camera,
421 {
422 RESOLVE(camera.center_node);
423 RESOLVE(camera.back_node );
424 RESOLVE(camera.left_node );
425 });
426
427 FOR_EACH (Keyword::CAMERARAIL, module->camerarail, rail,
428 {
429 RESOLVE_VECTOR(rail.nodes);
430 });
431
432 FOR_EACH (Keyword::CINECAM, module->cinecam, cinecam,
433 {
434 RESOLVE(cinecam.nodes[0]);
435 RESOLVE(cinecam.nodes[1]);
436 RESOLVE(cinecam.nodes[2]);
437 RESOLVE(cinecam.nodes[3]);
438 RESOLVE(cinecam.nodes[4]);
439 RESOLVE(cinecam.nodes[5]);
440 RESOLVE(cinecam.nodes[6]);
441 RESOLVE(cinecam.nodes[7]);
442 });
443
444 FOR_EACH (Keyword::COLLISIONBOXES, module->collisionboxes, box,
445 {
446 RESOLVE_VECTOR(box.nodes);
447 });
448
449 FOR_EACH (Keyword::COMMANDS2, module->commands2, command,
450 {
451 RESOLVE(command.nodes[0]);
452 RESOLVE(command.nodes[1]);
453 });
454
455 FOR_EACH (Keyword::CONTACTERS, module->contacters, contacter_node,
456 {
457 RESOLVE(contacter_node);
458 });
459
460 FOR_EACH (Keyword::EXHAUSTS, module->exhausts, exhaust,
461 {
462 RESOLVE(exhaust.reference_node);
463 RESOLVE(exhaust.direction_node);
464 });
465
466 FOR_EACH (Keyword::EXTCAMERA, module->extcamera, extcamera,
467 {
468 RESOLVE(extcamera.node);
469 });
470
471 FOR_EACH (Keyword::FIXES, module->fixes, fixed_node,
472 {
473 RESOLVE(fixed_node);
474 });
475
476 FOR_EACH (Keyword::FLARES2, module->flares2, flare2,
477 {
478 RESOLVE(flare2.reference_node);
479 RESOLVE(flare2.node_axis_x );
480 RESOLVE(flare2.node_axis_y );
481 });
482
483 FOR_EACH (Keyword::FLEXBODIES, module->flexbodies, flexbody,
484 {
485 RESOLVE(flexbody.reference_node);
486 RESOLVE(flexbody.x_axis_node );
487 RESOLVE(flexbody.y_axis_node );
488
489 ResolveFlexbodyForset(flexbody.node_list_to_import, flexbody.node_list);
490 flexbody.node_list_to_import.clear();
491 });
492
493 FOR_EACH (Keyword::FLEXBODYWHEELS, module->flexbodywheels, flexbodywheel,
494 {
495 RESOLVE(flexbodywheel.nodes[0] );
496 RESOLVE(flexbodywheel.nodes[1] );
497 RESOLVE(flexbodywheel.rigidity_node );
498 RESOLVE(flexbodywheel.reference_arm_node);
499 });
500
501 FOR_EACH (Keyword::FUSEDRAG, module->fusedrag, fusedrag,
502 {
503 RESOLVE(fusedrag.front_node);
504 RESOLVE(fusedrag.rear_node);
505 });
506
507 FOR_EACH (Keyword::HOOKS, module->hooks, hook,
508 {
509 RESOLVE(hook.node);
510 });
511
512 FOR_EACH (Keyword::HYDROS, module->hydros, hydro,
513 {
514 RESOLVE(hydro.nodes[0]);
515 RESOLVE(hydro.nodes[1]);
516 });
517
518 FOR_EACH (Keyword::MESHWHEELS, module->meshwheels, meshwheel,
519 {
520 RESOLVE(meshwheel.nodes[0] );
521 RESOLVE(meshwheel.nodes[1] );
522 RESOLVE(meshwheel.rigidity_node );
523 RESOLVE(meshwheel.reference_arm_node);
524 });
525 FOR_EACH (Keyword::MESHWHEELS2, module->meshwheels2, meshwheel2,
526 {
527 RESOLVE(meshwheel2.nodes[0] );
528 RESOLVE(meshwheel2.nodes[1] );
529 RESOLVE(meshwheel2.rigidity_node );
530 RESOLVE(meshwheel2.reference_arm_node);
531 });
532
533 FOR_EACH (Keyword::PARTICLES, module->particles, particle,
534 {
535 RESOLVE(particle.emitter_node);
536 RESOLVE(particle.reference_node);
537 });
538
539 FOR_EACH (Keyword::PROPS, module->props, prop,
540 {
541 RESOLVE(prop.reference_node);
542 RESOLVE(prop.x_axis_node );
543 RESOLVE(prop.y_axis_node );
544 });
545
546 FOR_EACH (Keyword::RAILGROUPS, module->railgroups, railgroup,
547 {
548 ResolveNodeRanges(railgroup.node_list);
549 });
550
551 FOR_EACH (Keyword::ROPABLES, module->ropables, ropable,
552 {
553 RESOLVE(ropable.node);
554 });
555
556 FOR_EACH (Keyword::ROPES, module->ropes, rope,
557 {
558 RESOLVE(rope.root_node);
559 RESOLVE(rope.end_node );
560 });
561
562 FOR_EACH (Keyword::ROTATORS, module->rotators, rotator,
563 {
564 RESOLVE(rotator.axis_nodes[0] );
565 RESOLVE(rotator.axis_nodes[1] );
566 RESOLVE(rotator.base_plate_nodes[0] );
567 RESOLVE(rotator.base_plate_nodes[1] );
568 RESOLVE(rotator.base_plate_nodes[2] );
569 RESOLVE(rotator.base_plate_nodes[3] );
570 RESOLVE(rotator.rotating_plate_nodes[0]);
571 RESOLVE(rotator.rotating_plate_nodes[1]);
572 RESOLVE(rotator.rotating_plate_nodes[2]);
573 RESOLVE(rotator.rotating_plate_nodes[3]);
574 });
575
576 FOR_EACH (Keyword::ROTATORS2, module->rotators2, rotator2,
577 {
578 RESOLVE(rotator2.axis_nodes[0] );
579 RESOLVE(rotator2.axis_nodes[1] );
580 RESOLVE(rotator2.base_plate_nodes[0] );
581 RESOLVE(rotator2.base_plate_nodes[1] );
582 RESOLVE(rotator2.base_plate_nodes[2] );
583 RESOLVE(rotator2.base_plate_nodes[3] );
584 RESOLVE(rotator2.rotating_plate_nodes[0]);
585 RESOLVE(rotator2.rotating_plate_nodes[1]);
586 RESOLVE(rotator2.rotating_plate_nodes[2]);
587 RESOLVE(rotator2.rotating_plate_nodes[3]);
588 });
589
590 FOR_EACH (Keyword::SCREWPROPS, module->screwprops, screwprop,
591 {
592 RESOLVE(screwprop.prop_node);
593 RESOLVE(screwprop.back_node);
594 RESOLVE(screwprop.top_node );
595 });
596
597 FOR_EACH (Keyword::SHOCKS, module->shocks, shock,
598 {
599 RESOLVE(shock.nodes[0]);
600 RESOLVE(shock.nodes[1]);
601 });
602
603 FOR_EACH (Keyword::SHOCKS2, module->shocks2, shock2,
604 {
605 RESOLVE(shock2.nodes[0]);
606 RESOLVE(shock2.nodes[1]);
607 });
608
609 FOR_EACH (Keyword::SHOCKS3, module->shocks3, shock3,
610 {
611 RESOLVE(shock3.nodes[0]);
612 RESOLVE(shock3.nodes[1]);
613 });
614
615 FOR_EACH (Keyword::SLIDENODES, module->slidenodes, slidenode,
616 {
617 RESOLVE(slidenode.slide_node);
618
619 ResolveNodeRanges(slidenode.rail_node_ranges);
620 });
621
622 FOR_EACH (Keyword::SOUNDSOURCES, module->soundsources, soundsource,
623 {
624 RESOLVE(soundsource.node);
625 });
626
627 FOR_EACH (Keyword::SOUNDSOURCES2, module->soundsources2, soundsource2,
628 {
629 RESOLVE(soundsource2.node);
630 });
631
632 FOR_EACH (Keyword::SUBMESH, module->submeshes, submesh,
633 {
634 m_current_keyword = Keyword::TEXCOORDS;
635 auto texcoord_itor = submesh.texcoords.begin();
636 auto texcoord_end = submesh.texcoords.end();
637 for (; texcoord_itor != texcoord_end; ++texcoord_itor)
638 {
639 RESOLVE(texcoord_itor->node);
640 }
641
643 auto cab_itor = submesh.cab_triangles.begin();
644 auto cab_end = submesh.cab_triangles.end();
645 for (; cab_itor != cab_end; ++cab_itor)
646 {
647 RESOLVE(cab_itor->nodes[0]);
648 RESOLVE(cab_itor->nodes[1]);
649 RESOLVE(cab_itor->nodes[2]);
650 }
651 });
652
653 FOR_EACH (Keyword::TIES, module->ties, tie,
654 {
655 RESOLVE(tie.root_node);
656 });
657
658 FOR_EACH (Keyword::TRIGGERS, module->triggers, trigger,
659 {
660 RESOLVE(trigger.nodes[0]);
661 RESOLVE(trigger.nodes[1]);
662 });
663
664 FOR_EACH (Keyword::TURBOJETS, module->turbojets, turbojet,
665 {
666 RESOLVE(turbojet.front_node);
667 RESOLVE(turbojet.back_node );
668 RESOLVE(turbojet.side_node );
669 });
670
671 FOR_EACH (Keyword::TURBOPROPS2, module->turboprops2, turboprop2,
672 {
673 RESOLVE(turboprop2.reference_node );
674 RESOLVE(turboprop2.axis_node );
675 RESOLVE(turboprop2.blade_tip_nodes[0]);
676 RESOLVE(turboprop2.blade_tip_nodes[1]);
677 RESOLVE(turboprop2.blade_tip_nodes[2]);
678 RESOLVE(turboprop2.blade_tip_nodes[3]);
679 });
680
681 FOR_EACH (Keyword::WHEELS, module->wheels, wheel,
682 {
683 RESOLVE(wheel.nodes[0] );
684 RESOLVE(wheel.nodes[1] );
685 RESOLVE(wheel.rigidity_node );
686 RESOLVE(wheel.reference_arm_node);
687 });
688
689 FOR_EACH (Keyword::WHEELS2, module->wheels2, wheel2,
690 {
691 RESOLVE(wheel2.nodes[0] );
692 RESOLVE(wheel2.nodes[1] );
693 RESOLVE(wheel2.rigidity_node );
694 RESOLVE(wheel2.reference_arm_node);
695 });
696
697 FOR_EACH (Keyword::VIDEOCAMERA, module->videocameras, videocamera,
698 {
699 RESOLVE(videocamera.reference_node );
700 RESOLVE(videocamera.left_node );
701 RESOLVE(videocamera.bottom_node );
702 RESOLVE(videocamera.alt_reference_node );
703 RESOLVE(videocamera.alt_orientation_node);
704 });
705
706 FOR_EACH (Keyword::WINGS, module->wings, wing,
707 {
708 RESOLVE(wing.nodes[0]);
709 RESOLVE(wing.nodes[1]);
710 RESOLVE(wing.nodes[2]);
711 RESOLVE(wing.nodes[3]);
712 RESOLVE(wing.nodes[4]);
713 RESOLVE(wing.nodes[5]);
714 RESOLVE(wing.nodes[6]);
715 RESOLVE(wing.nodes[7]);
716 });
717
718 m_current_module.reset();
719}
720
721bool SequentialImporter::AddNumberedNode(unsigned int number)
722{
723 if (number != m_all_nodes.size()) // Check node sync, like legacy parser did.
724 {
725 std::stringstream msg;
726 msg << "Lost sync in node numbers, got numbered node [" << number << "], expected [" << m_all_nodes.size() << "]. Ignoring node.";
727 this->AddMessage(Message::TYPE_FATAL_ERROR, msg.str());
728 return false;
729 }
732 return true;
733}
734
735bool SequentialImporter::AddNamedNode(std::string const & name)
736{
737 auto node_entry = NodeMapEntry(Keyword::NODES2, Node::Id(name), m_num_named_nodes);
738 auto result = m_named_nodes.insert(std::make_pair(name, node_entry));
739 if (!result.second)
740 {
741 std::stringstream msg;
742 msg << "Duplicate node name [" << name << "]. Ignoring node.";
743 this->AddMessage(Message::TYPE_FATAL_ERROR, msg.str());
744 return false;
745 }
746 m_all_nodes.push_back(node_entry);
748 return true;
749}
750
751void SequentialImporter::AddGeneratedNode(Keyword generated_from, NodeMapEntry::OriginDetail detail /* = NodeMapEntry::DETAIL_UNDEFINED*/ )
752{
753 unsigned int new_number = static_cast<int>(m_all_nodes.size());
754 unsigned int node_sub_index = 0;
755 switch (generated_from)
756 {
757 case Keyword::CINECAM: node_sub_index = m_num_cinecam_nodes ++; break;
758 case Keyword::WHEELS: node_sub_index = m_num_wheels_nodes ++; break;
759 case Keyword::WHEELS2: node_sub_index = m_num_wheels2_nodes ++; break;
760 case Keyword::MESHWHEELS: node_sub_index = m_num_meshwheels_nodes ++; break;
761 case Keyword::MESHWHEELS2: node_sub_index = m_num_meshwheels2_nodes ++; break;
762 case Keyword::FLEXBODYWHEELS: node_sub_index = m_num_flexbodywheels_nodes++; break;
763 default: break;
764 }
765 m_all_nodes.push_back( NodeMapEntry(generated_from, Node::Id(new_number), node_sub_index, detail));
766}
767
768#define STAT_LINE(STREAM, TITLE, COUNT_VAR, KEYWORD) \
769{ \
770 unsigned offset = this->GetNodeArrayOffset(KEYWORD); \
771 STREAM << "\n\t" << TITLE << std::setw(4) << COUNT_VAR; \
772 if (COUNT_VAR != 0) { STREAM << " (after conversion: start index = " << std::setw(4) << offset << ", end index = " << (offset + COUNT_VAR) - 1 << ")"; } \
773}
774
776{
777 std::stringstream msg;
778 msg << "~~~ Node statistics: ~~~"
779 << "\n\tTotal: " << m_all_nodes.size();
780 STAT_LINE(msg, " nodes: ", m_num_numbered_nodes , Keyword::NODES );
781 STAT_LINE(msg, " nodes2: ", m_num_named_nodes , Keyword::NODES2 );
782 STAT_LINE(msg, " cinecam: ", m_num_cinecam_nodes , Keyword::CINECAM );
783 STAT_LINE(msg, " wheels: ", m_num_wheels_nodes , Keyword::WHEELS );
784 STAT_LINE(msg, " wheels2: ", m_num_wheels2_nodes , Keyword::WHEELS2 );
785 STAT_LINE(msg, " meshwheels: ", m_num_meshwheels_nodes , Keyword::MESHWHEELS );
786 STAT_LINE(msg, " meshwheels2: ", m_num_meshwheels2_nodes , Keyword::MESHWHEELS2 );
788
789 msg << "\nResolved " << m_total_resolved << " nodes ("
790 << m_num_resolved_to_self << " resolved without change)";
791 return msg.str();
792}
793
795{
796 std::stringstream report;
797 report << "~~~ Iterating all nodes, in order as defined (total: " << m_all_nodes.size() << ") ~~~\n";
798 auto itor = m_all_nodes.begin();
799 auto end = m_all_nodes.end();
800 int index = 0;
801 for (; itor != end; ++itor)
802 {
803 NodeMapEntry& entry = *itor;
804 // Resolve type specifics
805 bool is_wheel = false;
806 switch (entry.origin_keyword)
807 {
808 case Keyword::WHEELS:
809 case Keyword::WHEELS2:
813 is_wheel = true;
814 break;
815 default:
816 break;
817 }
818 // Add line entry
819 report << "\n\t" << std::setw(3) << index << ": " << entry.node_id.ToString()
820 << " (from=" << KeywordToString(entry.origin_keyword)
821 << ", sub-index=" << entry.node_sub_index;
822 if (is_wheel)
823 {
824 switch (entry.origin_detail)
825 {
826 case NodeMapEntry::DETAIL_WHEEL_RIM_A: report << "[rim, A]"; break;
827 case NodeMapEntry::DETAIL_WHEEL_RIM_B: report << "[rim, B]"; break;
828 case NodeMapEntry::DETAIL_WHEEL_TYRE_A: report << "[tyre, A]"; break;
829 case NodeMapEntry::DETAIL_WHEEL_TYRE_B: report << "[tyre, B]"; break;
830 default: break;
831 }
832 }
833 report << ")";
834 ++index;
835 }
836 return report.str();
837}
Central state/object manager and communications hub.
#define TOSTRING(x)
Definition Application.h:57
Checks the rig-def file syntax and loads data to memory.
#define STAT_LINE(STREAM, TITLE, COUNT_VAR, KEYWORD)
#define RESOLVE(VARNAME)
#define FOR_EACH(KEYWORD, VECTOR, VARNAME, BLOCK)
Abstract node ID (numbered or named) Node name is always available.
Definition RigDef_Node.h:45
std::string ToString() const
Legacy parser resolved references on-the-fly and the condition to check named nodes was "are there an...
Definition RigDef_Node.h:78
bool IsValidAnyState() const
unsigned int Num() const
Definition RigDef_Node.h:95
std::string ToString() const
unsigned GetLineNumber() const
std::string const & Str() const
Definition RigDef_Node.h:94
std::map< std::string, NodeMapEntry > m_named_nodes
bool AddNamedNode(std::string const &name)
void AddGeneratedNode(Keyword generated_from, NodeMapEntry::OriginDetail detail=NodeMapEntry::DETAIL_UNDEFINED)
void Process(RigDef::DocumentPtr def)
Traverse whole rig definition and resolve all node references.
void ResolveNodeRanges(std::vector< Node::Range > &ranges)
void ResolveFlexbodyForset(std::vector< Node::Range > &ranges, std::vector< Node::Ref > &out_nodes)
unsigned GetNodeArrayOffset(Keyword keyword)
void GenerateNodesForWheel(Keyword generated_from, int num_rays, bool has_rigidity_node)
Node::Ref ResolveNode(Node::Ref const &noderef_in)
std::vector< NodeMapEntry > m_all_nodes
void AddMessage(Message::Type msg_type, std::string text)
void ProcessModule(std::shared_ptr< RigDef::Document::Module > module)
Node::Ref ResolveNodeByIndex(unsigned int index, unsigned int def_line_number)
std::shared_ptr< Document::Module > m_current_module
bool AddNumberedNode(unsigned int number)
@ CONSOLE_MSGTYPE_ACTOR
Parsing/spawn/simulation messages for actors.
Definition Console.h:63
void putMessage(MessageArea area, MessageType type, std::string const &msg, std::string icon="")
Definition Console.cpp:103
@ CONSOLE_SYSTEM_ERROR
Definition Console.h:52
@ CONSOLE_SYSTEM_NOTICE
Definition Console.h:51
@ CONSOLE_SYSTEM_WARNING
Definition Console.h:53
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
const char * KeywordToString(RigDef::Keyword keyword)
std::shared_ptr< Document > DocumentPtr
CVar * diag_rig_log_node_import
CVar * diag_rig_log_node_stats
Console * GetConsole()
bool IsRange() const
unsigned node_sub_index
RigDef::Keyword origin_keyword
Node::Id node_id
OriginDetail
@ DETAIL_WHEEL_RIM_B
@ DETAIL_WHEEL_TYRE_A
@ DETAIL_WHEEL_TYRE_B
@ DETAIL_WHEEL_RIM_A
OriginDetail origin_detail