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 // Authors: Alessandro Tasora
13 // =============================================================================
14 //
15 // Demonstration of the conveyor belt primitive.
16 //
17 // =============================================================================
18 
19 #include "chrono/physics/ChSystemNSC.h"
20 #include "chrono/physics/ChBodyEasy.h"
21 #include "chrono/physics/ChConveyor.h"
22 
23 #include "chrono_irrlicht/ChIrrApp.h"
24 
25 // Use the namespaces of Chrono
26 using namespace chrono;
27 using namespace chrono::collision;
28 using namespace chrono::irrlicht;
29 
30 // Use the main namespaces of Irrlicht
31 using namespace irr;
32 using namespace irr::core;
33 using namespace irr::scene;
34 using namespace irr::video;
35 using namespace irr::io;
36 using namespace irr::gui;
37 
38 // Static values valid through the entire program (bad
39 // programming practice, but enough for quick tests)
40 
41 double STATIC_flow = 100;
42 double STATIC_speed = 2;
43 std::vector<std::shared_ptr<ChBody> > particlelist;
44 
45 // Define a MyEventReceiver class which will be used to manage input
46 // from the GUI graphical user interface
47 
48 class MyEventReceiver : public IEventReceiver {
49   public:
MyEventReceiver(ChIrrAppInterface * myapp)50     MyEventReceiver(ChIrrAppInterface* myapp) {
51         // store pointer application
52         application = myapp;
53 
54         // ..add a GUI slider to control particles flow
55         scrollbar_flow = application->GetIGUIEnvironment()->addScrollBar(true, rect<s32>(510, 85, 650, 100), 0, 101);
56         scrollbar_flow->setMax(300);
57         scrollbar_flow->setPos(150);
58         text_flow = application->GetIGUIEnvironment()->addStaticText(L"Flow [particles/s]",
59                                                                      rect<s32>(650, 85, 750, 100), false);
60 
61         // ..add GUI slider to control the speed
62         scrollbar_speed = application->GetIGUIEnvironment()->addScrollBar(true, rect<s32>(510, 125, 650, 140), 0, 102);
63         scrollbar_speed->setMax(100);
64         scrollbar_speed->setPos(100);
65         text_speed = application->GetIGUIEnvironment()->addStaticText(L"Conveyor speed [m/s]:",
66                                                                       rect<s32>(650, 125, 750, 140), false);
67     }
68 
OnEvent(const SEvent & event)69     bool OnEvent(const SEvent& event) {
70         // check if user moved the sliders with mouse..
71         if (event.EventType == EET_GUI_EVENT) {
72             s32 id = event.GUIEvent.Caller->getID();
73 
74             switch (event.GUIEvent.EventType) {
75                 case EGET_SCROLL_BAR_CHANGED:
76                     if (id == 101)  // id of 'flow' slider..
77                     {
78                         s32 pos = ((IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
79                         STATIC_flow = (double)pos;
80                     }
81                     if (id == 102)  // id of 'speed' slider..
82                     {
83                         s32 pos = ((IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
84                         STATIC_speed = (((double)pos) / 100) * 2;
85                     }
86                     break;
87                 default:
88                     break;
89             }
90         }
91 
92         return false;
93     }
94 
95   private:
96     ChIrrAppInterface* application;
97 
98     IGUIScrollBar* scrollbar_flow;
99     IGUIStaticText* text_flow;
100     IGUIScrollBar* scrollbar_speed;
101     IGUIStaticText* text_speed;
102 };
103 
104 // Function that creates debris that fall on the conveyor belt, to be called at each dt.
105 // In this example, all debris particles share the same contact material.
create_debris(ChIrrApp & application,double dt,double particles_second)106 void create_debris(ChIrrApp& application, double dt, double particles_second) {
107     double xnozzlesize = 0.2;
108     double znozzlesize = 0.56;
109     double ynozzle = 0.2;
110 
111     double box_fraction = 0.3;  // 30% cubes
112     double cyl_fraction = 0.4;  // 40% cylinders
113 
114     double sphrad = 0.013;
115 
116     double exact_particles_dt = dt * particles_second;
117     double particles_dt = floor(exact_particles_dt);
118     double remaind = exact_particles_dt - particles_dt;
119     if (remaind > ChRandom())
120         particles_dt += 1;
121 
122     auto sphere_mat = chrono_types::make_shared<ChMaterialSurfaceNSC>();
123     sphere_mat->SetFriction(0.2f);
124     sphere_mat->SetRestitution(0.8f);
125 
126     auto box_mat = chrono_types::make_shared<ChMaterialSurfaceNSC>();
127     box_mat->SetFriction(0.4f);
128 
129     auto cyl_mat = chrono_types::make_shared<ChMaterialSurfaceNSC>();
130     cyl_mat->SetFriction(0.2f);
131 
132     for (int i = 0; i < particles_dt; i++) {
133         double rand_fract = ChRandom();
134 
135         if (rand_fract < box_fraction) {
136             auto mrigidBody = chrono_types::make_shared<ChBodyEasySphere>(sphrad,       // size
137                                                                           1000,         // density
138                                                                           true,         // visualization?
139                                                                           true,         // collision?
140                                                                           sphere_mat);  // contact material
141             mrigidBody->SetPos(ChVector<>(-0.5 * xnozzlesize + ChRandom() * xnozzlesize, ynozzle + i * 0.005,
142                                           -0.5 * znozzlesize + ChRandom() * znozzlesize));
143             mrigidBody->AddAsset(chrono_types::make_shared<ChTexture>(GetChronoDataFile("textures/bluewhite.png")));
144 
145             application.GetSystem()->Add(mrigidBody);
146 
147             // This will make particle's visualization assets visible in Irrlicht:
148             application.AssetBind(mrigidBody);
149             application.AssetUpdate(mrigidBody);
150 
151             particlelist.push_back(mrigidBody);
152         }
153 
154         if ((rand_fract > box_fraction) && (rand_fract < box_fraction + cyl_fraction)) {
155             double xscale = 1.3 * (1 - 0.4 * ChRandom());  // for oddly-shaped boxes..
156             double yscale = 1.3 * (1 - 0.4 * ChRandom());
157             double zscale = 1.3 * (1 - 0.4 * ChRandom());
158 
159             auto mrigidBody =
160                 chrono_types::make_shared<ChBodyEasyBox>(sphrad * 2 * xscale, sphrad * 2 * yscale, sphrad * 2 * zscale,
161                                                          1000,      // density
162                                                          true,      // visualization?
163                                                          true,      // collision?
164                                                          box_mat);  // contact material
165             mrigidBody->SetPos(ChVector<>(-0.5 * xnozzlesize + ChRandom() * xnozzlesize, ynozzle + i * 0.005,
166                                           -0.5 * znozzlesize + ChRandom() * znozzlesize));
167             mrigidBody->AddAsset(chrono_types::make_shared<ChTexture>(GetChronoDataFile("textures/cubetexture_bluewhite.png")));
168 
169             application.GetSystem()->Add(mrigidBody);
170 
171             // This will make particle's visualization assets visible in Irrlicht:
172             application.AssetBind(mrigidBody);
173             application.AssetUpdate(mrigidBody);
174 
175             particlelist.push_back(mrigidBody);
176         }
177 
178         if (rand_fract > box_fraction + cyl_fraction) {
179             auto mrigidBody = chrono_types::make_shared<ChBodyEasyCylinder>(sphrad, sphrad * 2,  // rad, height
180                                                                             1000,                // density
181                                                                             true,                // visualization?
182                                                                             true,                // collision?
183                                                                             cyl_mat);            // contact material
184             mrigidBody->SetPos(ChVector<>(-0.5 * xnozzlesize + ChRandom() * xnozzlesize, ynozzle + i * 0.005,
185                                           -0.5 * znozzlesize + ChRandom() * znozzlesize));
186             mrigidBody->AddAsset(chrono_types::make_shared<ChTexture>(GetChronoDataFile("textures/pinkwhite.png")));
187 
188             application.GetSystem()->Add(mrigidBody);
189 
190             // This will make particle's visualization assets visible in Irrlicht:
191             application.AssetBind(mrigidBody);
192             application.AssetUpdate(mrigidBody);
193 
194             particlelist.push_back(mrigidBody);
195         }
196     }
197 }
198 
199 // Function that deletes old debris (to avoid infinite creation that fills memory)
200 
purge_debris(ChIrrAppInterface & application,int nmaxparticles=100)201 void purge_debris(ChIrrAppInterface& application, int nmaxparticles = 100) {
202     while (particlelist.size() > nmaxparticles) {
203         application.GetSystem()->Remove(particlelist[0]);  // remove from physical simulation
204         particlelist.erase(particlelist.begin());  // remove also from our particle list (will also automatically delete
205                                                    // object thank to shared pointer)
206     }
207 }
208 
main(int argc,char * argv[])209 int main(int argc, char* argv[]) {
210     GetLog() << "Copyright (c) 2017 projectchrono.org\nChrono version: " << CHRONO_VERSION << "\n\n";
211 
212     // Create a ChronoENGINE physical system
213     ChSystemNSC mphysicalSystem;
214 
215     // Create the Irrlicht visualization (open the Irrlicht device,
216     // bind a simple user interface, etc. etc.)
217     ChIrrApp application(&mphysicalSystem, L"Conveyor belt", core::dimension2d<u32>(800, 600));
218     application.AddTypicalLogo();
219     application.AddTypicalSky();
220     application.AddTypicalLights();
221     application.AddTypicalCamera(core::vector3df(1.5f, 0.4f, -1.0f), core::vector3df(0.5f, 0.0f, 0.0f));
222 
223     // This is for GUI tweaking of system parameters..
224     MyEventReceiver receiver(&application);
225     // note how to add the custom event receiver to the default interface:
226     application.SetUserEventReceiver(&receiver);
227 
228     // Set small collision envelopes for objects that will be created from now on..
229     ChCollisionModel::SetDefaultSuggestedEnvelope(0.002);
230     ChCollisionModel::SetDefaultSuggestedMargin(0.002);
231 
232     // Create two conveyor fences
233     auto fence_mat = chrono_types::make_shared<ChMaterialSurfaceNSC>();
234     fence_mat->SetFriction(0.1f);
235 
236     auto mfence1 = chrono_types::make_shared<ChBodyEasyBox>(2, 0.11, 0.04, 1000, true, true, fence_mat);
237     mphysicalSystem.Add(mfence1);
238     mfence1->SetPos(ChVector<>(0, 0, -0.325));
239     mfence1->SetBodyFixed(true);
240 
241     auto mfence2 = chrono_types::make_shared<ChBodyEasyBox>(2, 0.11, 0.04, 1000, true, true, fence_mat);
242     mphysicalSystem.Add(mfence2);
243     mfence2->SetPos(ChVector<>(0, 0, 0.325));
244     mfence2->SetBodyFixed(true);
245 
246     // Create the conveyor belt (this is a pure Chrono::Engine object,
247     // because an Irrlicht 'SceneNode' wrapper is not yet available, so it is invisible - no 3D preview)
248     auto conveyor_mat = chrono_types::make_shared<ChMaterialSurfaceNSC>();
249     conveyor_mat->SetFriction(0.35f);
250 
251     auto mconveyor = chrono_types::make_shared<ChConveyor>(2, 0.05, 0.6);
252     mconveyor->SetBodyFixed(true);
253     mconveyor->SetMaterialSurface(conveyor_mat);
254     mconveyor->SetConveyorSpeed(STATIC_speed);
255     mconveyor->SetPos(ChVector<>(0, 0, 0));
256 
257     mphysicalSystem.Add(mconveyor);
258 
259     // Use this function for adding a ChIrrNodeAsset to all items.
260     // Otherwise use application.AssetBind(myitem); on a per-item basis.
261     application.AssetBindAll();
262 
263     // Use this function for 'converting' assets into Irrlicht meshes
264     application.AssetUpdateAll();
265 
266     //
267     // THE SOFT-REAL-TIME CYCLE
268     //
269 
270     application.SetTimestep(0.005);
271 
272     while (application.GetDevice()->run()) {
273         application.BeginScene(true, true, SColor(255, 140, 161, 192));
274 
275         application.DrawAll();
276 
277         application.DoStep();
278 
279         if (!application.GetPaused()) {
280             // Continuosly create debris that fall on the conveyor belt
281             create_debris(application, application.GetTimestep(), STATIC_flow);
282 
283             // Limit the max number of debris particles on the scene, deleting the oldest ones, for performance
284             purge_debris(application, 300);
285 
286             // Maybe the user played with the slider and changed STATIC_speed...
287             mconveyor->SetConveyorSpeed(STATIC_speed);
288         }
289 
290         application.EndScene();
291     }
292 
293     return 0;
294 }
295