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