1 // =============================================================================
2 // PROJECT CHRONO - http://projectchrono.org
3 //
4 // Copyright (c) 2014 projectchrono.org
5 // All rights reserved.
6 //
7 // Use of this source code is governed by a BSD-style license that can be found
8 // in the LICENSE file at the top level of the distribution and at
9 // http://projectchrono.org/license-chrono.txt.
10 //
11 // =============================================================================
12
13 #include "chrono/solver/ChIterativeSolverVI.h"
14 #include "chrono/physics/ChContactContainer.h"
15 #include "chrono/physics/ChLinkMate.h"
16 #include "chrono/assets/ChColor.h"
17 #include "chrono_irrlicht/ChIrrTools.h"
18 #include "chrono/utils/ChProfiler.h"
19 #include "chrono/collision/bullet/LinearMath/btIDebugDraw.h"
20 #include "chrono/collision/ChCollisionSystemBullet.h"
21
22 namespace chrono {
23 namespace irrlicht {
24 namespace tools {
25
26 using namespace irr;
27
28 // -----------------------------------------------------------------------------
29 // Function to align an Irrlicht object to a Chrono::Engine coordsys.
30 // -----------------------------------------------------------------------------
alignIrrlichtNodeToChronoCsys(scene::ISceneNode * mnode,const ChCoordsys<> & mcoords)31 void alignIrrlichtNodeToChronoCsys(scene::ISceneNode* mnode, const ChCoordsys<>& mcoords) {
32 // Output: will be an Irrlicht 4x4 matrix
33 irr::core::matrix4 irrMat;
34
35 // Get the rigid body actual rotation, as a 3x3 matrix [A]
36 ChMatrix33<> chMat(mcoords.rot);
37
38 //// RADU
39 //// Is this correct? ChMatrix33 is also stored row-major (even pre-Eigen)!
40 //// Or is Irrlicht actually using column-major?
41
42 // Fill the upper 3x3 submatrix with the [A] matrix
43 // transposed, since Irrlicht uses the row-major style as in D3D
44 irrMat[0] = (irr::f32)chMat(0);
45 irrMat[1] = (irr::f32)chMat(3);
46 irrMat[2] = (irr::f32)chMat(6);
47
48 irrMat[4] = (irr::f32)chMat(1);
49 irrMat[5] = (irr::f32)chMat(4);
50 irrMat[6] = (irr::f32)chMat(7);
51
52 irrMat[8] = (irr::f32)chMat(2);
53 irrMat[9] = (irr::f32)chMat(5);
54 irrMat[10] = (irr::f32)chMat(8);
55
56 irrMat[12] = (irr::f32)mcoords.pos.x();
57 irrMat[13] = (irr::f32)mcoords.pos.y();
58 irrMat[14] = (irr::f32)mcoords.pos.z();
59
60 // Clear the last column to 0 and set low-right corner to 1
61 // as in Denavitt-Hartemberg matrices, transposed.
62 irrMat[3] = irrMat[7] = irrMat[11] = 0.0f;
63 irrMat[15] = 1.0f;
64
65 // Set position and rotation of node using the 4x4 Irrlicht matrix.
66 mnode->setPosition(irrMat.getTranslation());
67 mnode->setRotation(irrMat.getRotationDegrees());
68 }
69
70 // -----------------------------------------------------------------------------
71 // Draw contact points.
72 // Uses the _draw_reporter_class callback class.
73 // -----------------------------------------------------------------------------
74 class _draw_reporter_class : public ChContactContainer::ReportContactCallback {
75 public:
OnReportContact(const ChVector<> & pA,const ChVector<> & pB,const ChMatrix33<> & plane_coord,const double & distance,const double & eff_Radius,const ChVector<> & react_forces,const ChVector<> & react_torques,ChContactable * modA,ChContactable * modB)76 virtual bool OnReportContact(const ChVector<>& pA,
77 const ChVector<>& pB,
78 const ChMatrix33<>& plane_coord,
79 const double& distance,
80 const double& eff_Radius,
81 const ChVector<>& react_forces,
82 const ChVector<>& react_torques,
83 ChContactable* modA,
84 ChContactable* modB) override {
85 ChMatrix33<>& mplanecoord = const_cast<ChMatrix33<>&>(plane_coord);
86 ChVector<> v1 = pA;
87 ChVector<> v2;
88 ChVector<> vn = mplanecoord.Get_A_Xaxis();
89
90 irr::video::SColor mcol = irr::video::SColor(200, 255, 0, 0);
91
92 switch (drawtype) {
93 case IrrContactsDrawMode::CONTACT_DISTANCES:
94 v2 = pB;
95 if (distance > 0.0)
96 mcol = irr::video::SColor(200, 20, 255, 0); // green: non penetration
97 else
98 mcol = irr::video::SColor(200, 255, 60, 60); // red: penetration
99 break;
100 case IrrContactsDrawMode::CONTACT_NORMALS:
101 v2 = pA + vn * clen;
102 mcol = irr::video::SColor(200, 0, 100, 255);
103 break;
104 case IrrContactsDrawMode::CONTACT_FORCES_N:
105 v2 = pA + vn * clen * react_forces.x();
106 break;
107 case IrrContactsDrawMode::CONTACT_FORCES:
108 v2 = pA + (mplanecoord * (react_forces * clen));
109 break;
110 default:
111 break;
112 }
113
114 this->cdriver->draw3DLine(irr::core::vector3dfCH(v1), irr::core::vector3dfCH(v2), mcol);
115 return true; // to continue scanning contacts
116 }
117
118 irr::video::IVideoDriver* cdriver;
119 IrrContactsDrawMode drawtype;
120 double clen;
121 };
122
drawAllContactPoints(std::shared_ptr<ChContactContainer> mcontainer,irr::video::IVideoDriver * driver,double mlen,IrrContactsDrawMode drawtype)123 int drawAllContactPoints(std::shared_ptr<ChContactContainer> mcontainer,
124 irr::video::IVideoDriver* driver,
125 double mlen,
126 IrrContactsDrawMode drawtype) {
127 if (drawtype == IrrContactsDrawMode::CONTACT_NONE)
128 return 0;
129
130 // if (mphysicalSystem.GetNcontacts() == 0)
131 // return 0;
132
133 driver->setTransform(irr::video::ETS_WORLD, irr::core::matrix4());
134 irr::video::SMaterial mattransp;
135 mattransp.ZBuffer = false;
136 mattransp.Lighting = false;
137 driver->setMaterial(mattransp);
138
139 auto my_drawer = chrono_types::make_shared<_draw_reporter_class>();
140
141 my_drawer->cdriver = driver;
142 my_drawer->clen = mlen;
143 my_drawer->drawtype = drawtype;
144
145 // scan all contacts
146 mcontainer->ReportAllContacts(my_drawer);
147
148 return 0;
149 }
150
151 // -----------------------------------------------------------------------------
152 // Draw contact information as labels at the contact point.
153 // Uses the _label_reporter_class callback class.
154 // -----------------------------------------------------------------------------
155 class _label_reporter_class : public ChContactContainer::ReportContactCallback {
156 public:
OnReportContact(const ChVector<> & pA,const ChVector<> & pB,const ChMatrix33<> & plane_coord,const double & distance,const double & eff_radius,const ChVector<> & react_forces,const ChVector<> & react_torques,ChContactable * modA,ChContactable * modB)157 virtual bool OnReportContact(const ChVector<>& pA,
158 const ChVector<>& pB,
159 const ChMatrix33<>& plane_coord,
160 const double& distance,
161 const double& eff_radius,
162 const ChVector<>& react_forces,
163 const ChVector<>& react_torques,
164 ChContactable* modA,
165 ChContactable* modB) override {
166 char buffer[25];
167 irr::core::vector3df mpos((irr::f32)pA.x(), (irr::f32)pA.y(), (irr::f32)pA.z());
168 irr::core::position2d<s32> spos =
169 this->cdevice->getSceneManager()->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(mpos);
170 gui::IGUIFont* font = this->cdevice->getGUIEnvironment()->getBuiltInFont();
171
172 switch (labeltype) {
173 case IrrContactsLabelMode::CONTACT_DISTANCES_VAL:
174 sprintf(buffer, "% 6.3g", distance);
175 break;
176 case IrrContactsLabelMode::CONTACT_FORCES_N_VAL:
177 sprintf(buffer, "% 6.3g", react_forces.x());
178 break;
179 case IrrContactsLabelMode::CONTACT_FORCES_T_VAL:
180 sprintf(buffer, "% 6.3g", ChVector<>(0, react_forces.y(), react_forces.z()).Length());
181 break;
182 case IrrContactsLabelMode::CONTACT_FORCES_VAL:
183 sprintf(buffer, "% 6.3g", ChVector<>(react_forces).Length());
184 break;
185 case IrrContactsLabelMode::CONTACT_TORQUES_VAL:
186 sprintf(buffer, "% 6.3g", ChVector<>(react_torques).Length());
187 break;
188 case IrrContactsLabelMode::CONTACT_TORQUES_S_VAL:
189 sprintf(buffer, "% 6.3g", react_torques.x());
190 break;
191 case IrrContactsLabelMode::CONTACT_TORQUES_R_VAL:
192 sprintf(buffer, "% 6.3g", ChVector<>(0, react_torques.y(), react_torques.z()).Length());
193 break;
194 default:
195 break;
196 }
197
198 font->draw(irr::core::stringw(buffer).c_str(),
199 irr::core::rect<s32>(spos.X - 15, spos.Y, spos.X + 15, spos.Y + 10), ccol);
200
201 return true; // to continue scanning contacts
202 }
203
204 irr::IrrlichtDevice* cdevice;
205 IrrContactsLabelMode labeltype;
206 irr::video::SColor ccol;
207 };
208
drawAllContactLabels(std::shared_ptr<ChContactContainer> mcontainer,irr::IrrlichtDevice * device,IrrContactsLabelMode labeltype,irr::video::SColor mcol)209 int drawAllContactLabels(std::shared_ptr<ChContactContainer> mcontainer,
210 irr::IrrlichtDevice* device,
211 IrrContactsLabelMode labeltype,
212 irr::video::SColor mcol) {
213 if (labeltype == IrrContactsLabelMode::CONTACT_NONE_VAL)
214 return 0;
215
216 // if (mphysicalSystem.GetNcontacts() == 0)
217 // return 0;
218
219 auto my_label_rep = chrono_types::make_shared<_label_reporter_class>();
220
221 my_label_rep->cdevice = device;
222 my_label_rep->ccol = mcol;
223 my_label_rep->labeltype = labeltype;
224
225 // scan all contacts
226 mcontainer->ReportAllContacts(my_label_rep);
227
228 return 0;
229 }
230
231 // -----------------------------------------------------------------------------
232 // Draw links as glyps.
233 // ---------------------------------------------------------------------------
drawAllLinks(ChSystem & mphysicalSystem,irr::video::IVideoDriver * driver,double mlen,IrrLinkDrawMode drawtype)234 int drawAllLinks(ChSystem& mphysicalSystem, irr::video::IVideoDriver* driver, double mlen, IrrLinkDrawMode drawtype) {
235 if (drawtype == IrrLinkDrawMode::LINK_NONE)
236 return 0;
237
238 driver->setTransform(irr::video::ETS_WORLD, irr::core::matrix4());
239 irr::video::SMaterial mattransp;
240 mattransp.ZBuffer = false;
241 mattransp.Lighting = false;
242 driver->setMaterial(mattransp);
243
244 for (auto link : mphysicalSystem.Get_linklist()) {
245 ChCoordsys<> mlinkframe = link->GetLinkAbsoluteCoords();
246 ChVector<> v1abs = mlinkframe.pos;
247 ChVector<> v2;
248 switch (drawtype) {
249 case IrrLinkDrawMode::LINK_REACT_FORCE:
250 v2 = link->Get_react_force();
251 break;
252 case IrrLinkDrawMode::LINK_REACT_TORQUE:
253 v2 = link->Get_react_torque();
254 break;
255 default:
256 break;
257 }
258 v2 *= mlen;
259 ChVector<> v2abs = v2 >> mlinkframe;
260
261 irr::video::SColor mcol(200, 250, 250, 0); // yellow vectors
262
263 driver->draw3DLine(irr::core::vector3dfCH(v1abs), irr::core::vector3dfCH(v2abs), mcol);
264 }
265
266 return 0;
267 }
268
269 // -----------------------------------------------------------------------------
270 // Draw links as labels
271 // ---------------------------------------------------------------------------
drawAllLinkLabels(ChSystem & mphysicalSystem,irr::IrrlichtDevice * device,IrrLinkLabelMode labeltype,irr::video::SColor mcol)272 int drawAllLinkLabels(ChSystem& mphysicalSystem,
273 irr::IrrlichtDevice* device,
274 IrrLinkLabelMode labeltype,
275 irr::video::SColor mcol) {
276 if (labeltype == IrrLinkLabelMode::LINK_NONE_VAL)
277 return 0;
278
279 for (auto link : mphysicalSystem.Get_linklist()) {
280 ChCoordsys<> mlinkframe = link->GetLinkAbsoluteCoords();
281
282 char buffer[25];
283 irr::core::vector3df mpos((irr::f32)mlinkframe.pos.x(), (irr::f32)mlinkframe.pos.y(),
284 (irr::f32)mlinkframe.pos.z());
285 irr::core::position2d<s32> spos =
286 device->getSceneManager()->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(mpos);
287 gui::IGUIFont* font = device->getGUIEnvironment()->getBuiltInFont();
288
289 switch (labeltype) {
290 case IrrLinkLabelMode::LINK_REACT_FORCE_VAL:
291 sprintf(buffer, "% 6.3g", link->Get_react_force().Length());
292 break;
293 case IrrLinkLabelMode::LINK_REACT_FORCE_X:
294 sprintf(buffer, "% 6.3g", link->Get_react_force().x());
295 break;
296 case IrrLinkLabelMode::LINK_REACT_FORCE_Y:
297 sprintf(buffer, "% 6.3g", link->Get_react_force().y());
298 break;
299 case IrrLinkLabelMode::LINK_REACT_FORCE_Z:
300 sprintf(buffer, "% 6.3g", link->Get_react_force().z());
301 break;
302 case IrrLinkLabelMode::LINK_REACT_TORQUE_VAL:
303 sprintf(buffer, "% 6.3g", link->Get_react_torque().Length());
304 break;
305 case IrrLinkLabelMode::LINK_REACT_TORQUE_X:
306 sprintf(buffer, "% 6.3g", link->Get_react_torque().x());
307 break;
308 case IrrLinkLabelMode::LINK_REACT_TORQUE_Y:
309 sprintf(buffer, "% 6.3g", link->Get_react_torque().y());
310 break;
311 case IrrLinkLabelMode::LINK_REACT_TORQUE_Z:
312 sprintf(buffer, "% 6.3g", link->Get_react_torque().z());
313 break;
314 default:
315 break;
316 }
317
318 font->draw(irr::core::stringw(buffer).c_str(),
319 irr::core::rect<s32>(spos.X - 15, spos.Y, spos.X + 15, spos.Y + 10), mcol);
320 }
321
322 return 0;
323 }
324
325 // -----------------------------------------------------------------------------
326 // Draw collision objects bounding boxes for rigid bodies.
327 // -----------------------------------------------------------------------------
drawAllBoundingBoxes(ChSystem & mphysicalSystem,irr::video::IVideoDriver * driver)328 int drawAllBoundingBoxes(ChSystem& mphysicalSystem, irr::video::IVideoDriver* driver) {
329 driver->setTransform(irr::video::ETS_WORLD, irr::core::matrix4());
330 irr::video::SMaterial mattransp;
331 mattransp.ZBuffer = true;
332 mattransp.Lighting = false;
333 driver->setMaterial(mattransp);
334
335 for (auto body : mphysicalSystem.Get_bodylist()) {
336 irr::video::SColor mcol;
337
338 if (body->GetSleeping())
339 mcol = irr::video::SColor(70, 0, 50, 255); // blue: sleeping
340 else
341 mcol = irr::video::SColor(70, 30, 200, 200); // cyan: not sleeping
342
343 ChVector<> hi = VNULL;
344 ChVector<> lo = VNULL;
345 body->GetTotalAABB(lo, hi);
346 ChVector<> p1(hi.x(), lo.y(), lo.z());
347 ChVector<> p2(lo.x(), hi.y(), lo.z());
348 ChVector<> p3(lo.x(), lo.y(), hi.z());
349 ChVector<> p4(hi.x(), hi.y(), lo.z());
350 ChVector<> p5(lo.x(), hi.y(), hi.z());
351 ChVector<> p6(hi.x(), lo.y(), hi.z());
352 ChVector<> p7(lo.x(), lo.y(), hi.z());
353 ChVector<> p8(lo.x(), lo.y(), hi.z());
354 ChVector<> p9(lo.x(), hi.y(), lo.z());
355 ChVector<> p10(lo.x(), hi.y(), lo.z());
356 ChVector<> p11(hi.x(), lo.y(), lo.z());
357 ChVector<> p12(hi.x(), lo.y(), lo.z());
358 ChVector<> p14(hi.x(), lo.y(), hi.z());
359 ChVector<> p15(lo.x(), hi.y(), hi.z());
360 ChVector<> p16(lo.x(), hi.y(), hi.z());
361 ChVector<> p17(hi.x(), hi.y(), lo.z());
362 ChVector<> p18(hi.x(), lo.y(), hi.z());
363 ChVector<> p19(hi.x(), hi.y(), lo.z());
364 driver->draw3DLine(irr::core::vector3dfCH(lo), irr::core::vector3dfCH(p1), mcol);
365 driver->draw3DLine(irr::core::vector3dfCH(lo), irr::core::vector3dfCH(p2), mcol);
366 driver->draw3DLine(irr::core::vector3dfCH(lo), irr::core::vector3dfCH(p3), mcol);
367 driver->draw3DLine(irr::core::vector3dfCH(hi), irr::core::vector3dfCH(p4), mcol);
368 driver->draw3DLine(irr::core::vector3dfCH(hi), irr::core::vector3dfCH(p5), mcol);
369 driver->draw3DLine(irr::core::vector3dfCH(hi), irr::core::vector3dfCH(p6), mcol);
370 driver->draw3DLine(irr::core::vector3dfCH(p7), irr::core::vector3dfCH(p14), mcol);
371 driver->draw3DLine(irr::core::vector3dfCH(p8), irr::core::vector3dfCH(p15), mcol);
372 driver->draw3DLine(irr::core::vector3dfCH(p9), irr::core::vector3dfCH(p16), mcol);
373 driver->draw3DLine(irr::core::vector3dfCH(p10), irr::core::vector3dfCH(p17), mcol);
374 driver->draw3DLine(irr::core::vector3dfCH(p11), irr::core::vector3dfCH(p18), mcol);
375 driver->draw3DLine(irr::core::vector3dfCH(p12), irr::core::vector3dfCH(p19), mcol);
376 }
377
378 return 0;
379 }
380
381 // -----------------------------------------------------------------------------
382 // Draw coordinate systems of ChBody objects bodies.
383 // -----------------------------------------------------------------------------
drawAllCOGs(ChSystem & mphysicalSystem,irr::video::IVideoDriver * driver,double scale)384 int drawAllCOGs(ChSystem& mphysicalSystem, irr::video::IVideoDriver* driver, double scale) {
385 driver->setTransform(irr::video::ETS_WORLD, irr::core::matrix4());
386 irr::video::SMaterial mattransp;
387 mattransp.ZBuffer = true;
388 mattransp.Lighting = false;
389 driver->setMaterial(mattransp);
390
391 for (auto body : mphysicalSystem.Get_bodylist()) {
392 irr::video::SColor mcol;
393 const ChFrame<>& mframe_cog = body->GetFrame_COG_to_abs();
394 const ChFrame<>& mframe_ref = body->GetFrame_REF_to_abs();
395
396 ChVector<> p0 = mframe_cog.GetPos();
397 ChVector<> px = p0 + mframe_cog.GetA().Get_A_Xaxis() * 0.5 * scale;
398 ChVector<> py = p0 + mframe_cog.GetA().Get_A_Yaxis() * 0.5 * scale;
399 ChVector<> pz = p0 + mframe_cog.GetA().Get_A_Zaxis() * 0.5 * scale;
400
401 mcol = irr::video::SColor(70, 125, 0, 0); // X red
402 driver->draw3DLine(irr::core::vector3dfCH(p0), irr::core::vector3dfCH(px), mcol);
403 mcol = irr::video::SColor(70, 0, 125, 0); // Y green
404 driver->draw3DLine(irr::core::vector3dfCH(p0), irr::core::vector3dfCH(py), mcol);
405 mcol = irr::video::SColor(70, 0, 0, 125); // Z blue
406 driver->draw3DLine(irr::core::vector3dfCH(p0), irr::core::vector3dfCH(pz), mcol);
407
408 p0 = mframe_ref.GetPos();
409 px = p0 + mframe_ref.GetA().Get_A_Xaxis() * scale;
410 py = p0 + mframe_ref.GetA().Get_A_Yaxis() * scale;
411 pz = p0 + mframe_ref.GetA().Get_A_Zaxis() * scale;
412
413 mcol = irr::video::SColor(70, 255, 0, 0); // X red
414 driver->draw3DLine(irr::core::vector3dfCH(p0), irr::core::vector3dfCH(px), mcol);
415 mcol = irr::video::SColor(70, 0, 255, 0); // Y green
416 driver->draw3DLine(irr::core::vector3dfCH(p0), irr::core::vector3dfCH(py), mcol);
417 mcol = irr::video::SColor(70, 0, 0, 255); // Z blue
418 driver->draw3DLine(irr::core::vector3dfCH(p0), irr::core::vector3dfCH(pz), mcol);
419 }
420
421 return 0;
422 }
423
424 // -----------------------------------------------------------------------------
425 // Draw coordinate systems of frames used by links.
426 // -----------------------------------------------------------------------------
drawAllLinkframes(ChSystem & mphysicalSystem,irr::video::IVideoDriver * driver,double scale)427 int drawAllLinkframes(ChSystem& mphysicalSystem, irr::video::IVideoDriver* driver, double scale) {
428 driver->setTransform(irr::video::ETS_WORLD, irr::core::matrix4());
429 irr::video::SMaterial mattransp;
430 mattransp.ZBuffer = true;
431 mattransp.Lighting = false;
432 driver->setMaterial(mattransp);
433
434 for (auto link : mphysicalSystem.Get_linklist()) {
435 ChFrame<> frAabs;
436 ChFrame<> frBabs;
437
438 // default frame alignment:
439
440 frAabs = link->GetAssetsFrame();
441 frBabs = frAabs;
442
443 // special cases:
444
445 if (auto mylink = std::dynamic_pointer_cast<ChLinkMarkers>(link)) {
446 frAabs = *mylink->GetMarker1() >> *mylink->GetBody1();
447 frBabs = *mylink->GetMarker2() >> *mylink->GetBody2();
448 }
449
450 if (auto mylink = std::dynamic_pointer_cast<ChLinkMateGeneric>(link)) {
451 frAabs = mylink->GetFrame1() >> *mylink->GetBody1();
452 frBabs = mylink->GetFrame2() >> *mylink->GetBody2();
453 }
454
455 irr::video::SColor mcol;
456
457 ChVector<> p0 = frAabs.GetPos();
458 ChVector<> px = p0 + frAabs.GetA().Get_A_Xaxis() * 0.7 * scale;
459 ChVector<> py = p0 + frAabs.GetA().Get_A_Yaxis() * 0.7 * scale;
460 ChVector<> pz = p0 + frAabs.GetA().Get_A_Zaxis() * 0.7 * scale;
461
462 mcol = irr::video::SColor(70, 125, 0, 0); // X red
463 driver->draw3DLine(irr::core::vector3dfCH(p0), irr::core::vector3dfCH(px), mcol);
464 mcol = irr::video::SColor(70, 0, 125, 0); // Y green
465 driver->draw3DLine(irr::core::vector3dfCH(p0), irr::core::vector3dfCH(py), mcol);
466 mcol = irr::video::SColor(70, 0, 0, 125); // Z blue
467 driver->draw3DLine(irr::core::vector3dfCH(p0), irr::core::vector3dfCH(pz), mcol);
468
469 p0 = frBabs.GetPos();
470 px = p0 + frBabs.GetA().Get_A_Xaxis() * scale;
471 py = p0 + frBabs.GetA().Get_A_Yaxis() * scale;
472 pz = p0 + frBabs.GetA().Get_A_Zaxis() * scale;
473
474 mcol = irr::video::SColor(70, 255, 0, 0); // X red
475 driver->draw3DLine(irr::core::vector3dfCH(p0), irr::core::vector3dfCH(px), mcol);
476 mcol = irr::video::SColor(70, 0, 255, 0); // Y green
477 driver->draw3DLine(irr::core::vector3dfCH(p0), irr::core::vector3dfCH(py), mcol);
478 mcol = irr::video::SColor(70, 0, 0, 255); // Z blue
479 driver->draw3DLine(irr::core::vector3dfCH(p0), irr::core::vector3dfCH(pz), mcol);
480 }
481
482 return 0;
483 }
484
485 // -----------------------------------------------------------------------------
486 // -----------------------------------------------------------------------------
drawHUDviolation(irr::video::IVideoDriver * driver,IrrlichtDevice * mdevice,ChSystem & asystem,int mx,int my,int sx,int sy,double spfact)487 void drawHUDviolation(irr::video::IVideoDriver* driver,
488 IrrlichtDevice* mdevice,
489 ChSystem& asystem,
490 int mx,
491 int my,
492 int sx,
493 int sy,
494 double spfact) {
495 auto msolver_speed = std::dynamic_pointer_cast<ChIterativeSolverVI>(asystem.GetSolver());
496 if (!msolver_speed)
497 return;
498
499 msolver_speed->SetRecordViolation(true);
500
501 irr::core::rect<s32> mrect(mx, my, mx + sx, my + sy);
502 driver->draw2DRectangle(irr::video::SColor(100, 200, 200, 230), mrect);
503 for (unsigned int i = 0; i < msolver_speed->GetViolationHistory().size(); i++) {
504 driver->draw2DRectangle(
505 irr::video::SColor(90, 255, 0, 0),
506 irr::core::rect<s32>(mx + i * 4, sy + my - (int)(spfact * msolver_speed->GetViolationHistory()[i]),
507 mx + (i + 1) * 4 - 1, sy + my),
508 &mrect);
509 }
510 for (unsigned int i = 0; i < msolver_speed->GetDeltalambdaHistory().size(); i++) {
511 driver->draw2DRectangle(
512 irr::video::SColor(100, 255, 255, 0),
513 irr::core::rect<s32>(mx + i * 4, sy + my - (int)(spfact * msolver_speed->GetDeltalambdaHistory()[i]),
514 mx + (i + 1) * 4 - 2, sy + my),
515 &mrect);
516 }
517
518 if (mdevice->getGUIEnvironment()) {
519 gui::IGUIFont* font = mdevice->getGUIEnvironment()->getBuiltInFont();
520 if (font) {
521 char buffer[100];
522 font->draw(L"Solver speed violation", irr::core::rect<s32>(mx + sx / 2 - 100, my, mx + sx, my + 10),
523 irr::video::SColor(200, 100, 0, 0));
524 sprintf(buffer, "%g", sy / spfact);
525 font->draw(irr::core::stringw(buffer).c_str(), irr::core::rect<s32>(mx, my, mx + 30, my + 10),
526 irr::video::SColor(200, 100, 0, 0));
527 }
528 }
529 }
530
531 // -----------------------------------------------------------------------------
532 // -----------------------------------------------------------------------------
drawChFunction(IrrlichtDevice * mdevice,ChFunction * fx,double xmin,double xmax,double ymin,double ymax,int mx,int my,int sx,int sy)533 void drawChFunction(IrrlichtDevice* mdevice,
534 ChFunction* fx,
535 double xmin,
536 double xmax,
537 double ymin,
538 double ymax,
539 int mx,
540 int my,
541 int sx,
542 int sy) {
543 irr::video::IVideoDriver* driver = mdevice->getVideoDriver();
544
545 if (!fx)
546 return;
547
548 irr::core::rect<s32> mrect(mx, my, mx + sx, my + sy);
549 driver->draw2DRectangle(irr::video::SColor(100, 200, 200, 230), mrect);
550
551 if (mdevice->getGUIEnvironment()) {
552 gui::IGUIFont* font = mdevice->getGUIEnvironment()->getBuiltInFont();
553 if (font) {
554 char buffer[100];
555 sprintf(buffer, "%g", ymax);
556 font->draw(irr::core::stringw(buffer).c_str(), irr::core::rect<s32>(mx, my, mx + sx, my + 10),
557 irr::video::SColor(200, 100, 0, 0));
558 sprintf(buffer, "%g", ymin);
559 font->draw(irr::core::stringw(buffer).c_str(), irr::core::rect<s32>(mx, my + sy, mx + sx, my + sy + 10),
560 irr::video::SColor(200, 100, 0, 0));
561
562 if ((ymin < 0) && (ymax > 0)) {
563 int yzero = my + sy - (int)(((-ymin) / (ymax - ymin)) * (double)sy);
564 driver->draw2DLine(irr::core::position2d<s32>(mx, yzero), irr::core::position2d<s32>(mx + sx, yzero),
565 irr::video::SColor(90, 255, 255, 255));
566 font->draw(irr::core::stringw(buffer).c_str(),
567 irr::core::rect<s32>(mx, my + yzero, mx + sx, my + yzero + 10),
568 irr::video::SColor(200, 100, 0, 0));
569 }
570 }
571 }
572
573 int prevx = 0;
574 int prevy = 0;
575
576 for (int ix = 0; ix < sx; ix++) {
577 double x = xmin + (xmax - xmin) * ((double)(ix)) / (double)(sx);
578 double y = fx->Get_y(x);
579 int py = my + sy - (int)(((y - ymin) / (ymax - ymin)) * (double)sy);
580 int px = mx + ix;
581 if (ix > 0)
582 driver->draw2DLine(irr::core::position2d<s32>(px, py), irr::core::position2d<s32>(prevx, prevy),
583 irr::video::SColor(200, 255, 0, 0));
584 prevx = px;
585 prevy = py;
586 }
587 }
588
589 // -----------------------------------------------------------------------------
590 // Draw segment lines in 3D space, with given color.
591 // -----------------------------------------------------------------------------
drawSegment(irr::video::IVideoDriver * driver,ChVector<> mstart,ChVector<> mend,irr::video::SColor mcol,bool use_Zbuffer)592 void drawSegment(irr::video::IVideoDriver* driver,
593 ChVector<> mstart,
594 ChVector<> mend,
595 irr::video::SColor mcol,
596 bool use_Zbuffer) {
597 driver->setTransform(irr::video::ETS_WORLD, irr::core::matrix4());
598 irr::video::SMaterial mattransp;
599 mattransp.ZBuffer = use_Zbuffer;
600 mattransp.Lighting = false;
601 driver->setMaterial(mattransp);
602 driver->draw3DLine(irr::core::vector3dfCH(mstart), irr::core::vector3dfCH(mend), mcol);
603 }
604
605 // -----------------------------------------------------------------------------
606 // Draw a polyline in 3D space, given the array of points as a std::vector.
607 // -----------------------------------------------------------------------------
drawPolyline(irr::video::IVideoDriver * driver,std::vector<ChVector<>> & mpoints,irr::video::SColor mcol,bool use_Zbuffer)608 void drawPolyline(irr::video::IVideoDriver* driver,
609 std::vector<ChVector<> >& mpoints,
610 irr::video::SColor mcol,
611 bool use_Zbuffer) {
612 // not very efficient, but enough as an example..
613 if (mpoints.size() < 2)
614 return;
615
616 for (unsigned int i = 0; i < mpoints.size() - 1; i++)
617 drawSegment(driver, mpoints[i], mpoints[i + 1], mcol, use_Zbuffer);
618 }
619
620 // -----------------------------------------------------------------------------
621 // Draw a circle line in 3D space, with given color.
622 // -----------------------------------------------------------------------------
drawCircle(irr::video::IVideoDriver * driver,double radius,ChCoordsys<> mpos,irr::video::SColor mcol,int mresolution,bool use_Zbuffer)623 void drawCircle(irr::video::IVideoDriver* driver,
624 double radius,
625 ChCoordsys<> mpos,
626 irr::video::SColor mcol,
627 int mresolution,
628 bool use_Zbuffer) {
629 driver->setTransform(irr::video::ETS_WORLD, irr::core::matrix4());
630 irr::video::SMaterial mattransp;
631 mattransp.ZBuffer = false;
632 mattransp.Lighting = false;
633 driver->setMaterial(mattransp);
634
635 double phaseA = 0;
636 double phaseB = 0;
637
638 for (int iu = 1; iu <= mresolution; iu++) {
639 phaseB = CH_C_2PI * (double)iu / (double)mresolution;
640 ChVector<> V1(radius * cos(phaseA), radius * sin(phaseA), 0);
641 ChVector<> V2(radius * cos(phaseB), radius * sin(phaseB), 0);
642 drawSegment(driver, mpos.TransformLocalToParent(V1), mpos.TransformLocalToParent(V2), mcol, use_Zbuffer);
643 phaseA = phaseB;
644 }
645 }
646
647 // -----------------------------------------------------------------------------
648 // Draw a spring in 3D space, with given color.
649 // -----------------------------------------------------------------------------
drawSpring(irr::video::IVideoDriver * driver,double radius,ChVector<> start,ChVector<> end,irr::video::SColor mcol,int mresolution,double turns,bool use_Zbuffer)650 void drawSpring(irr::video::IVideoDriver* driver,
651 double radius,
652 ChVector<> start,
653 ChVector<> end,
654 irr::video::SColor mcol,
655 int mresolution,
656 double turns,
657 bool use_Zbuffer) {
658 ChMatrix33<> rel_matrix;
659 ChVector<> dist = end - start;
660 ChVector<> Vx, Vy, Vz;
661 double length = dist.Length();
662 ChVector<> dir = Vnorm(dist);
663 XdirToDxDyDz(dir, VECT_Y, Vx, Vy, Vz);
664 rel_matrix.Set_A_axis(Vx, Vy, Vz);
665 ChQuaternion<> Q12 = rel_matrix.Get_A_quaternion();
666 ChCoordsys<> mpos(start, Q12);
667
668 driver->setTransform(irr::video::ETS_WORLD, irr::core::matrix4());
669 irr::video::SMaterial mattransp;
670 mattransp.ZBuffer = false;
671 mattransp.Lighting = false;
672 driver->setMaterial(mattransp);
673
674 double phaseA = 0;
675 double phaseB = 0;
676 double heightA = 0;
677 double heightB = 0;
678
679 for (int iu = 1; iu <= mresolution; iu++) {
680 phaseB = turns * CH_C_2PI * (double)iu / (double)mresolution;
681 heightB = length * ((double)iu / (double)mresolution);
682 ChVector<> V1(heightA, radius * cos(phaseA), radius * sin(phaseA));
683 ChVector<> V2(heightB, radius * cos(phaseB), radius * sin(phaseB));
684 drawSegment(driver, mpos.TransformLocalToParent(V1), mpos.TransformLocalToParent(V2), mcol, use_Zbuffer);
685 phaseA = phaseB;
686 heightA = heightB;
687 }
688 }
689
690 // -----------------------------------------------------------------------------
691 // Draw grids in 3D space, with given orientation, colour and spacing.
692 // -----------------------------------------------------------------------------
drawGrid(irr::video::IVideoDriver * driver,double ustep,double vstep,int nu,int nv,ChCoordsys<> mpos,irr::video::SColor mcol,bool use_Zbuffer)693 void drawGrid(irr::video::IVideoDriver* driver,
694 double ustep,
695 double vstep,
696 int nu,
697 int nv,
698 ChCoordsys<> mpos,
699 irr::video::SColor mcol,
700 bool use_Zbuffer) {
701 driver->setTransform(irr::video::ETS_WORLD, irr::core::matrix4());
702 irr::video::SMaterial mattransp;
703 mattransp.ZBuffer = true;
704 mattransp.Lighting = false;
705 driver->setMaterial(mattransp);
706
707 for (int iu = -nu / 2; iu <= nu / 2; iu++) {
708 ChVector<> V1(iu * ustep, vstep * (nv / 2), 0);
709 ChVector<> V2(iu * ustep, -vstep * (nv / 2), 0);
710 drawSegment(driver, mpos.TransformLocalToParent(V1), mpos.TransformLocalToParent(V2), mcol, use_Zbuffer);
711 }
712
713 for (int iv = -nv / 2; iv <= nv / 2; iv++) {
714 ChVector<> V1(ustep * (nu / 2), iv * vstep, 0);
715 ChVector<> V2(-ustep * (nu / 2), iv * vstep, 0);
716 drawSegment(driver, mpos.TransformLocalToParent(V1), mpos.TransformLocalToParent(V2), mcol, use_Zbuffer);
717 }
718 }
719
720 /// Easy-to-use function to draw color map 2D legend
drawColorbar(double vmin,double vmax,const std::string & label,IrrlichtDevice * mdevice,int mx,int my,int sx,int sy)721 void drawColorbar(double vmin,
722 double vmax,
723 const std::string& label,
724 IrrlichtDevice* mdevice,
725 int mx,
726 int my,
727 int sx,
728 int sy) {
729 irr::video::IVideoDriver* driver = mdevice->getVideoDriver();
730
731 gui::IGUIFont* font = 0;
732 if (mdevice->getGUIEnvironment())
733 font = mdevice->getGUIEnvironment()->getSkin()->getFont();
734
735 int steps = 10;
736 double ystep = ((double)sy / (double)steps);
737 for (int i = 0; i < steps; ++i) {
738 double mv_up = vmax - (vmax - vmin) * ((double)(i) / (double)steps);
739 double mv_dw = vmax - (vmax - vmin) * ((double)(i + 1) / (double)steps);
740 core::rect<s32> mrect(mx, my + (s32)(i * ystep), mx + sx, my + (s32)((i + 1) * ystep));
741 ChColor c_up = ChColor::ComputeFalseColor(mv_up, vmin, vmax, false);
742 ChColor c_dw = ChColor::ComputeFalseColor(mv_dw, vmin, vmax, false);
743 video::SColor col_up(255, u32(255 * c_up.R), u32(255 * c_up.G), u32(255 * c_up.B));
744 video::SColor col_dw(255, u32(255 * c_dw.R), u32(255 * c_dw.G), u32(255 * c_dw.B));
745 driver->draw2DRectangle(mrect, col_up, col_up, col_dw, col_dw);
746
747 if (font) {
748 char buffer[100];
749 sprintf(buffer, "%g", mv_up);
750 font->draw(irr::core::stringw(buffer).c_str(),
751 core::rect<s32>(mrect.UpperLeftCorner.X + sx + 6, mrect.UpperLeftCorner.Y - 5,
752 mrect.LowerRightCorner.X + sx + 6, mrect.LowerRightCorner.Y - 5),
753 irr::video::SColor(255, 0, 0, 0));
754 driver->draw2DLine(irr::core::position2d<s32>(mrect.UpperLeftCorner.X + sx - 4, mrect.UpperLeftCorner.Y),
755 irr::core::position2d<s32>(mrect.UpperLeftCorner.X + sx, mrect.UpperLeftCorner.Y),
756 irr::video::SColor(255, 100, 100, 100));
757 }
758 }
759 font->draw(irr::core::stringw(label.c_str()).c_str(), core::rect<s32>(mx, my + sy + 5, mx + 100, my + sy + 20),
760 irr::video::SColor(255, 0, 0, 0));
761 }
762
763 // utility class used for drawing coll shapes using the Bullet machinery (see drawCollisionShapes)
764 class ChDebugDrawer : public btIDebugDraw {
765 public:
ChDebugDrawer(irr::video::IVideoDriver * driver)766 explicit ChDebugDrawer(irr::video::IVideoDriver* driver)
767 : driver_(driver), debugMode_(0), linecolor(255, 255, 0, 0) {}
768
~ChDebugDrawer()769 ~ChDebugDrawer() override {}
770
drawLine(const btVector3 & from,const btVector3 & to,const btVector3 & color)771 void drawLine(const btVector3& from, const btVector3& to, const btVector3& color) override {
772 // Note: I did not use the color here as the visuals with the white box were not very
773 // appealing. But one could simply do a SCColor(255, color.x() * 255, color.y() * 255, color.z() * 255)
774 // to get native bullet colors. Useful as this override also results in drawing the x,y,z axis for
775 // the reference frames for the collision models.
776 driver_->draw3DLine(irr::core::vector3dfCH(ChVector<>(from.x(), from.y(), from.z())),
777 irr::core::vector3dfCH(ChVector<>(to.x(), to.y(), to.z())), linecolor);
778 }
779
drawContactPoint(const btVector3 & PointOnB,const btVector3 & normalOnB,btScalar distance,int lifeTime,const btVector3 & color)780 void drawContactPoint(const btVector3& PointOnB,
781 const btVector3& normalOnB,
782 btScalar distance,
783 int lifeTime,
784 const btVector3& color) override {}
785
reportErrorWarning(const char * warningString)786 void reportErrorWarning(const char* warningString) override {}
draw3dText(const btVector3 & location,const char * textString)787 void draw3dText(const btVector3& location, const char* textString) override {}
788
setDebugMode(int debugMode)789 void setDebugMode(int debugMode) override { debugMode_ |= debugMode; }
790
getDebugMode() const791 int getDebugMode() const override { return debugMode_; }
792
setLineColor(irr::video::SColor & mcolor)793 void setLineColor(irr::video::SColor& mcolor) { linecolor = mcolor; }
794
795 private:
796 irr::video::IVideoDriver* driver_;
797 int debugMode_;
798 irr::video::SColor linecolor;
799 };
800
801 // Draw the collision shapes as wireframe, overlayed to shapes.
802 // Note: this works only for the Bullet collision system (i.e. not working for Chrono::Multicore)
drawCollisionShapes(ChSystem & asystem,irr::IrrlichtDevice * mdevice,irr::video::SColor mcol)803 void drawCollisionShapes(ChSystem& asystem, irr::IrrlichtDevice* mdevice, irr::video::SColor mcol) {
804 const auto& chCollisionSystem =
805 std::dynamic_pointer_cast<chrono::collision::ChCollisionSystemBullet>(asystem.GetCollisionSystem());
806 if (!chCollisionSystem)
807 return;
808
809 auto bulletCollisionWorld = chCollisionSystem->GetBulletCollisionWorld();
810 ChDebugDrawer debugDrawer(mdevice->getVideoDriver());
811 debugDrawer.setDebugMode(btIDebugDraw::DBG_DrawWireframe);
812 debugDrawer.setLineColor(mcol);
813 bulletCollisionWorld->setDebugDrawer(&debugDrawer);
814
815 mdevice->getVideoDriver()->setTransform(irr::video::ETS_WORLD, irr::core::matrix4());
816 irr::video::SMaterial mattransp;
817 mattransp.ZBuffer = true;
818 mattransp.Lighting = false;
819 mdevice->getVideoDriver()->setMaterial(mattransp);
820
821 bulletCollisionWorld->debugDrawWorld();
822 }
823
824 // -----------------------------------------------------------------------------
825 // -----------------------------------------------------------------------------
drawPlot3D(irr::video::IVideoDriver * driver,ChMatrixConstRef X,ChMatrixConstRef Y,ChMatrixConstRef Z,ChCoordsys<> mpos,irr::video::SColor mcol,bool use_Zbuffer)826 void drawPlot3D(irr::video::IVideoDriver* driver,
827 ChMatrixConstRef X,
828 ChMatrixConstRef Y,
829 ChMatrixConstRef Z,
830 ChCoordsys<> mpos,
831 irr::video::SColor mcol,
832 bool use_Zbuffer) {
833 driver->setTransform(irr::video::ETS_WORLD, irr::core::matrix4());
834 irr::video::SMaterial mattransp;
835 mattransp.ZBuffer = true;
836 mattransp.Lighting = false;
837 driver->setMaterial(mattransp);
838
839 if ((X.cols() != Y.cols()) || (X.cols() != Z.cols()) || (X.rows() != Y.rows()) || (X.rows() != Z.rows())) {
840 GetLog() << "drawPlot3D: X Y Z matrices must have the same size, as n.rows and n.columns \n";
841 return;
842 }
843
844 for (int iy = 0; iy < X.cols(); ++iy) {
845 for (int ix = 0; ix < X.rows(); ++ix) {
846 if (ix > 0) {
847 ChVector<> Vx1(X(ix - 1, iy), Y(ix - 1, iy), Z(ix - 1, iy));
848 ChVector<> Vx2(X(ix, iy), Y(ix, iy), Z(ix, iy));
849 drawSegment(driver, mpos.TransformLocalToParent(Vx1), mpos.TransformLocalToParent(Vx2), mcol,
850 use_Zbuffer);
851 }
852
853 if (iy > 0) {
854 ChVector<> Vy1(X(ix, iy - 1), Y(ix, iy - 1), Z(ix, iy - 1));
855 ChVector<> Vy2(X(ix, iy), Y(ix, iy), Z(ix, iy));
856 drawSegment(driver, mpos.TransformLocalToParent(Vy1), mpos.TransformLocalToParent(Vy2), mcol,
857 use_Zbuffer);
858 }
859 }
860 }
861 }
862
drawProfilerRecursive(utils::ChProfileIterator * profileIterator,irr::IrrlichtDevice * device,int mx,int my,int sx,int sy,int xspacing,int & ypos)863 void drawProfilerRecursive(utils::ChProfileIterator* profileIterator,
864 irr::IrrlichtDevice* device,
865 int mx,
866 int my,
867 int sx,
868 int sy,
869 int xspacing,
870 int& ypos) {
871 profileIterator->First();
872 if (profileIterator->Is_Done())
873 return;
874
875 irr::gui::IGUIFont* font = device->getGUIEnvironment()->getSkin()->getFont(); // getBuiltInFont();
876
877 float accumulated_time = 0, parent_time = profileIterator->Is_Root()
878 ? utils::ChProfileManager::Get_Time_Since_Reset()
879 : profileIterator->Get_Current_Parent_Total_Time();
880 int i;
881 int frames_since_reset = utils::ChProfileManager::Get_Frame_Count_Since_Reset();
882
883 float tot_frametime = utils::ChProfileManager::Get_Time_Since_Reset();
884
885 char buffer[300];
886 irr::video::SColor mcol(255, 255, 255, 90);
887
888 int numChildren = 0;
889
890 for (i = 0; !profileIterator->Is_Done(); i++, profileIterator->Next()) {
891 numChildren++;
892 float current_total_time = profileIterator->Get_Current_Total_Time();
893 accumulated_time += current_total_time;
894 float fraction = parent_time > FLT_EPSILON ? (current_total_time / parent_time) * 100 : 0.f;
895 float fraction_tot = tot_frametime > FLT_EPSILON ? (current_total_time / tot_frametime) * 100 : 0.f;
896
897 irr::core::rect<s32> mrect(mx, my + ypos, mx + (int)(sx * (current_total_time / tot_frametime)),
898 my + ypos + 18);
899 device->getVideoDriver()->draw2DRectangle(
900 irr::video::SColor(100, ((xspacing * 200) % 255), ((-xspacing * 151 + 200) % 255), 230), mrect);
901
902 sprintf(buffer, "%d -- %s (%.2f %% parent, %.2f %% tot.) :: %.3f ms / frame (%d calls)\n", i,
903 profileIterator->Get_Current_Name(), fraction, fraction_tot,
904 (current_total_time / (double)frames_since_reset), profileIterator->Get_Current_Total_Calls());
905 irr::core::stringw mstring(buffer);
906 font->draw(mstring, irr::core::rect<irr::s32>(mx + xspacing, my + ypos, mx + sx, my + ypos + 20), mcol);
907 ypos += 20;
908
909 profileIterator->Enter_Child(i);
910 drawProfilerRecursive(profileIterator, device, mx, my, sx, sy, xspacing + 30, ypos);
911 profileIterator->Enter_Parent();
912 for (int j = 0; j < i; ++j)
913 profileIterator->Next();
914 }
915
916 float fraction = parent_time > FLT_EPSILON ? ((parent_time - accumulated_time) / parent_time) * 100 : 0.f;
917 float fraction_tot = tot_frametime > FLT_EPSILON ? ((parent_time - accumulated_time) / tot_frametime) * 100 : 0.f;
918
919 irr::core::rect<s32> mrect(mx, my + ypos, mx + (int)(sx * ((parent_time - accumulated_time) / tot_frametime)),
920 my + ypos + 18);
921 device->getVideoDriver()->draw2DRectangle(
922 irr::video::SColor(100, ((xspacing * 200) % 255), ((-xspacing * 151 + 200) % 255), 230), mrect);
923
924 sprintf(buffer, "n -- %s (%.2f %% parent, %.2f %% tot.) :: %.3f ms\n", "Unaccounted:", fraction, fraction_tot,
925 parent_time - accumulated_time);
926 irr::core::stringw mstringu(buffer);
927 font->draw(mstringu, irr::core::rect<irr::s32>(mx + xspacing, my + ypos, mx + sx, my + ypos + 20), mcol);
928 ypos += 20;
929 }
930
931 /// Draw run-time profiler infos
drawProfiler(irr::IrrlichtDevice * device)932 void drawProfiler(irr::IrrlichtDevice* device) {
933 int mx = 230;
934 int my = 30;
935 int sx = 500;
936 int sy = 400;
937
938 int ypos = 0;
939 utils::ChProfileIterator* profileIterator = 0;
940 profileIterator = chrono::utils::ChProfileManager::Get_Iterator();
941
942 drawProfilerRecursive(profileIterator, device, mx, my, sx, sy, 0, ypos);
943
944 utils::ChProfileManager::Release_Iterator(profileIterator);
945 }
946
947 } // end namespace tools
948 } // end namespace irrlicht
949 } // end namespace chrono
950