1 // =============================================================================
2 // PROJECT CHRONO - http://projectchrono.org
3 //
4 // Copyright (c) 2019 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: Asher Elmquist, Han Wang
13 // =============================================================================
14 //
15 // OptiX rendering engine for processing jobs for sensing. Jobs are defined on
16 // each sensor as a graph.
17 //
18 // =============================================================================
19 
20 #define PROFILE false
21 
22 #include "chrono_sensor/optix/ChOptixEngine.h"
23 
24 #include <cuda.h>
25 #include <cuda_runtime.h>
26 #include <optix_stubs.h>
27 #include <optix_function_table_definition.h>
28 
29 #include "chrono_sensor/sensors/ChCameraSensor.h"
30 #include "chrono_sensor/sensors/ChLidarSensor.h"
31 #include "chrono_sensor/sensors/ChRadarSensor.h"
32 #include "chrono_sensor/optix/ChOptixUtils.h"
33 #include "chrono_sensor/utils/ChVisualMaterialUtils.h"
34 
35 #include "chrono/assets/ChAsset.h"
36 #include "chrono/assets/ChBoxShape.h"
37 #include "chrono/assets/ChCapsuleShape.h"
38 #include "chrono/assets/ChColorAsset.h"
39 #include "chrono/assets/ChConeShape.h"
40 #include "chrono/assets/ChCylinderShape.h"
41 #include "chrono/assets/ChEllipsoidShape.h"
42 #include "chrono/assets/ChLineShape.h"
43 #include "chrono/assets/ChPathShape.h"
44 #include "chrono/assets/ChRoundedBoxShape.h"
45 #include "chrono/assets/ChSphereShape.h"
46 #include "chrono/assets/ChTexture.h"
47 #include "chrono/assets/ChTriangleMeshShape.h"
48 #include "chrono/assets/ChVisualization.h"
49 #include "chrono/physics/ChSystem.h"
50 
51 #include <random>
52 
53 namespace chrono {
54 namespace sensor {
55 
56 // using namespace optix;
57 
ChOptixEngine(ChSystem * sys,int device_id,int max_scene_reflections,bool verbose)58 ChOptixEngine::ChOptixEngine(ChSystem* sys, int device_id, int max_scene_reflections, bool verbose)
59     : m_verbose(verbose), m_deviceId(device_id), m_recursions(max_scene_reflections), m_sceneThread() {
60     m_sceneThread.start = false;
61     m_sceneThread.terminate = false;
62     m_sceneThread.done = true;  // thread is done to begin with (no work to complete)
63     m_system = sys;
64     Initialize();
65 }
~ChOptixEngine()66 ChOptixEngine::~ChOptixEngine() {
67     StopAllThreads();  // if it hasn't been stopped yet, stop it ourselves
68     // cleanup ChOptixGeometry and ChOptixPipeline before destroying the context
69     cudaDeviceSynchronize();
70     m_geometry->Cleanup();
71     m_pipeline->Cleanup();
72     // cleanup lights
73     cudaFree(reinterpret_cast<void*>(md_params));
74     // cleanup device context parameters
75     cudaFree(reinterpret_cast<void*>(m_params.lights));
76     optixDeviceContextDestroy(m_context);
77 }
78 
Initialize()79 void ChOptixEngine::Initialize() {
80     cudaFree(0);
81     OptixDeviceContext context;
82     CUcontext cuCtx = 0;  // zero means take the current context, TODO: enable multigpu
83     OPTIX_ERROR_CHECK(optixInit());
84     OptixDeviceContextOptions options = {};
85     options.logCallbackFunction = &optix_log_callback;
86 
87     if (m_verbose) {
88         options.validationMode = OPTIX_DEVICE_CONTEXT_VALIDATION_MODE_ALL;
89         options.logCallbackLevel = 4;
90     } else {
91         options.validationMode = OPTIX_DEVICE_CONTEXT_VALIDATION_MODE_OFF;
92         options.logCallbackLevel = 2;
93     }
94 
95     OPTIX_ERROR_CHECK(optixDeviceContextCreate(cuCtx, &options, &context));
96     m_context = context;
97 
98     // defaults to no lights
99     m_params.lights = {};
100     m_params.num_lights = 0;
101     m_params.ambient_light_color = make_float3(0.0f, 0.0f, 0.0f);  // make_float3(0.1f, 0.1f, 0.1f);  // default value
102     m_params.max_depth = m_recursions;
103     m_params.scene_epsilon = 1.e-3f;    // TODO: determine a good value for this
104     m_params.importance_cutoff = .01f;  /// TODO: determine a good value for this
105 
106     CUDA_ERROR_CHECK(cudaMalloc(reinterpret_cast<void**>(&md_params), sizeof(ContextParameters)));
107     m_params.root = {};
108 
109     m_geometry = chrono_types::make_shared<ChOptixGeometry>(m_context);
110     m_pipeline = chrono_types::make_shared<ChOptixPipeline>(m_context, m_recursions, m_verbose);
111 
112     // TODO: enable multigpu
113 }
114 
AssignSensor(std::shared_ptr<ChOptixSensor> sensor)115 void ChOptixEngine::AssignSensor(std::shared_ptr<ChOptixSensor> sensor) {
116     // all optix sensors should be treated the same. Up to the sensor to specify a difference by using a unique launch
117     // kernel and set of filters
118     {
119         // std::cout << "Assigning a sensor\n";
120         std::unique_lock<std::mutex> lck(m_sceneThread.mutex);
121         // std::cout << "going to wait for lock. done=" << m_sceneThread.done << "\n";
122         while (!m_sceneThread.done) {
123             m_sceneThread.cv.wait(lck);
124         }
125         // std::cout << "Done waiting for lock\n";
126 
127         if (std::find(m_assignedSensor.begin(), m_assignedSensor.end(), sensor) != m_assignedSensor.end()) {
128             std::cerr << "WARNING: This sensor already exists in manager. Ignoring this addition\n";
129             return;
130         }
131 
132         m_assignedSensor.push_back(sensor);
133         m_cameraStartFrames.push_back(sensor->GetParent()->GetAssetsFrame());
134         m_cameraStartFrames_set.push_back(false);
135         m_pipeline->SpawnPipeline(sensor->GetPipelineType());
136 
137         // create a ChFilterOptixRender and push to front of filter list
138         auto opx_filter = chrono_types::make_shared<ChFilterOptixRender>();
139         unsigned int id = static_cast<unsigned int>(m_assignedSensor.size() - 1);
140         opx_filter->m_optix_pipeline = m_pipeline->GetPipeline(id);
141         opx_filter->m_optix_params = md_params;
142         opx_filter->m_optix_sbt = m_pipeline->GetSBT(id);
143         opx_filter->m_raygen_record = m_pipeline->GetRayGenRecord(id);
144 
145         // add a denoiser to the optix render filter if its a camera and global illumination is enabled
146         if (auto cam = std::dynamic_pointer_cast<ChCameraSensor>(sensor)) {
147             if (cam->GetUseGI()) {
148                 std::cout << "Sensor: " << cam->GetName() << " requested global illumination\n";
149                 opx_filter->m_denoiser = chrono_types::make_shared<ChOptixDenoiser>(m_context);
150             }
151         }
152 
153         m_assignedRenderers.push_back(opx_filter);
154         sensor->PushFilterFront(opx_filter);
155         sensor->LockFilterList();
156 
157         std::shared_ptr<SensorBuffer> buffer;
158         for (auto f : sensor->GetFilterList()) {
159             f->Initialize(sensor, buffer);  // master thread should always be the one to initialize
160         }
161         // create the thread that will be in charge of this sensor (must be consistent thread for visualization reasons)
162         m_renderThreads.emplace_back();
163         id = static_cast<unsigned int>(m_renderThreads.size() - 1);
164         m_renderThreads[id].done = true;
165         m_renderThreads[id].start = false;
166         m_renderThreads[id].terminate = false;
167         m_renderThreads[id].thread =
168             std::move(std::thread(&ChOptixEngine::RenderProcess, this, std::ref(m_renderThreads[id]), sensor));
169     }
170     if (!m_started) {
171         Start();
172     }
173 }
174 
UpdateSensors(std::shared_ptr<ChScene> scene)175 void ChOptixEngine::UpdateSensors(std::shared_ptr<ChScene> scene) {
176     if (!m_params.root) {
177         ConstructScene();
178     }
179     std::vector<int> to_be_updated;
180     std::vector<int> to_be_waited_on;
181 
182     // check if any of the sensors would be collecting data right now, if so, pack a tmp start keyframe
183     for (int i = 0; i < m_assignedSensor.size(); i++) {
184         auto sensor = m_assignedSensor[i];
185         if (m_system->GetChTime() > sensor->GetNumLaunches() / sensor->GetUpdateRate() - 1e-7 &&
186             !m_cameraStartFrames_set[i]) {
187             // do this once per sensor because we don't know if they will be updated at the same time
188             m_geometry->UpdateBodyTransformsStart((float)m_system->GetChTime(),
189                                                   (float)m_system->GetChTime() + sensor->GetCollectionWindow());
190             // std::cout << "Loaded a set of body start transforms time=" << m_system->GetChTime() << std::endl;
191 
192             m_cameraStartFrames[i] = sensor->GetParent()->GetAssetsFrame();
193             m_cameraStartFrames_set[i] = true;
194             // std::cout << "Set camera " << i << " start frame at time=" << m_system->GetChTime() << std::endl;
195         }
196     }
197 
198     // check which sensors need to be updated this step
199     for (int i = 0; i < m_assignedSensor.size(); i++) {
200         auto sensor = m_assignedSensor[i];
201         if (m_system->GetChTime() >
202             sensor->GetNumLaunches() / sensor->GetUpdateRate() + sensor->GetCollectionWindow() - 1e-7) {
203             to_be_updated.push_back(i);
204         }
205     }
206 
207     if (to_be_updated.size() > 0) {
208         {
209             // lock the render queue to make sure previous rendering has completed
210             std::unique_lock<std::mutex> lck(m_sceneThread.mutex);
211             while (!m_sceneThread.done) {
212                 m_sceneThread.cv.wait(lck);
213             }
214             // m_sceneThread.cv.wait(lck);  // wait for the scene thread to tell us it is done
215             cudaDeviceSynchronize();  // TODO: do we need to synchronize here?
216 
217             // std::cout << "Starting optix scene update\n";
218             // update the scene for the optix context
219             UpdateCameraTransforms();
220 
221             m_geometry->UpdateBodyTransformsEnd((float)m_system->GetChTime());
222 
223             // std::cout<<"Num render threads: "<<m_renderThreads.size()<<std::endl;
224             // m_renderThreads
225             // cudaDeviceSynchronize();
226             UpdateSceneDescription(scene);
227             UpdateDeformableMeshes();
228             // UpdateDynamicMeshes();
229             // std::cout << "Updated optix scene\n";
230             // std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();
231             // std::chrono::duration<double> wall_time =
232             //     std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1);
233             // std::cout << "Synchronizing scene time: " << wall_time.count() << std::endl;
234             float t = (float)m_system->GetChTime();
235             // push the sensors that need updating to the render queue
236             for (auto i : to_be_updated) {
237                 m_renderQueue.push_back(i);
238                 m_assignedSensor[i]->IncrementNumLaunches();
239                 m_assignedRenderers[i]->m_time_stamp = t;
240                 m_renderThreads[i].done =
241                     false;  // this render thread must not be done now given we have prepped some data for it
242             }
243             // for (int i = 0; i < to_be_updated.size(); i++) {
244             //     m_renderQueue.push_back(m_assignedSensor[to_be_updated[i]]);
245             //     m_assignedSensor[to_be_updated[i]]->IncrementNumLaunches();
246             //     m_assignedRenderers[to_be_updated[i]]->m_time_stamp = t;
247             // }
248         }
249         // m_mainLock.unlock();
250         // we only notify the worker thread when there is a sensor to launch and filters to process
251         // std::cout << "== Preprocessing complete, notifying scene thread" << std::endl;
252         m_sceneThread.done = false;  // tell the thread it is not done
253         // std::cout << "Telling the scene thread it is not done" << std::endl;
254         m_sceneThread.start = true;     // tell the thread it should start
255         m_sceneThread.cv.notify_all();  // tell the scene thread it should proceed
256     }
257 
258     // wait for any sensors whose lag times would mean the data should be available before the next ones start rendering
259     // bool data_complete = false;
260 
261     for (int i = 0; i < m_assignedSensor.size(); i++) {
262         auto sensor = m_assignedSensor[i];
263         if (m_system->GetChTime() > (sensor->GetNumLaunches() - 1) / sensor->GetUpdateRate() +
264                                         sensor->GetCollectionWindow() + sensor->GetLag() - 1e-7) {
265             // wait for the sensor thread i which will notify everyone when done
266 
267             // if (!m_mainLock.owns_lock())
268             //     m_mainLock.lock();  // will wait for lock to come back from scene thread - TODO: make this check the
269             // render threads instead
270             // m_renderThreads.cv.wait()
271             // if any sensors need to have their data returned, we must wait until optix is done rendering their data
272             // TODO: allow waiting for the specific sensor rather than all of them
273             // bool data_complete = false;
274             // while (!data_complete) {
275             //     std::lock_guard<std::mutex> lck(m_sceneThread.mutex);
276             //     if (m_renderQueue.empty())
277             //         data_complete = true;
278             // }
279 
280             // see if this specific thread is done
281             std::unique_lock<std::mutex> lck(m_renderThreads[i].mutex);
282             while (!m_renderThreads[i].done) {
283                 m_renderThreads[i].cv.wait(lck);
284             }
285         }
286     }
287 }  // namespace sensor
288 
StopAllThreads()289 void ChOptixEngine::StopAllThreads() {
290     // stop the scene building thread
291     {
292         // wait for last processing to be done
293         std::unique_lock<std::mutex> lck(m_sceneThread.mutex);
294         while (!m_sceneThread.done) {
295             m_sceneThread.cv.wait(lck);
296         }
297         m_sceneThread.terminate = true;
298         m_sceneThread.start = true;
299         m_sceneThread.done = false;
300     }
301     m_sceneThread.cv.notify_all();
302     // wait for it to finish the terminate proces
303     std::unique_lock<std::mutex> lck(m_sceneThread.mutex);
304     while (!m_sceneThread.done) {
305         m_sceneThread.cv.wait(lck);
306     }
307 
308     if (m_sceneThread.thread.joinable()) {
309         m_sceneThread.thread.join();
310     }
311 
312     m_started = false;
313 
314     // stop all the render threads
315     for (int i = 0; i < m_renderThreads.size(); i++) {
316         {
317             // wait for previous processing to be done
318             std::unique_lock<std::mutex> lck(m_renderThreads[i].mutex);
319             while (!m_renderThreads[i].done) {
320                 m_renderThreads[i].cv.wait(lck);
321             }
322             m_renderThreads[i].terminate = true;
323             m_renderThreads[i].start = true;
324             m_renderThreads[i].done = false;
325         }
326         m_renderThreads[i].cv.notify_all();
327 
328         // wait for it to finish the terminate proces
329         std::unique_lock<std::mutex> lck(m_renderThreads[i].mutex);
330         while (!m_renderThreads[i].done) {
331             m_renderThreads[i].cv.wait(lck);
332         }
333         if (m_renderThreads[i].thread.joinable()) {
334             m_renderThreads[i].thread.join();
335         }
336     }
337 
338     // clear the assigned sensors as their corresponding threads have been stopped
339     m_assignedSensor.clear();
340     m_assignedRenderers.clear();
341     m_renderThreads.clear();
342 }
343 
Start()344 void ChOptixEngine::Start() {
345     if (!m_started) {
346         m_sceneThread.start = false;
347         m_sceneThread.terminate = false;
348         m_sceneThread.done = true;  // thread is done to begin with (no work to complete)
349         m_sceneThread.thread = std::move(std::thread(&ChOptixEngine::SceneProcess, this, std::ref(m_sceneThread)));
350         m_started = true;
351     }
352 }
353 
RenderProcess(RenderThread & tself,std::shared_ptr<ChOptixSensor> sensor)354 void ChOptixEngine::RenderProcess(RenderThread& tself, std::shared_ptr<ChOptixSensor> sensor) {
355     bool terminate = false;
356 
357     // keep the thread running until we submit a terminate job or equivalent
358     while (!terminate) {
359         std::unique_lock<std::mutex> tmp_lock(tself.mutex);
360         while (!tself.start) {
361             tself.cv.wait(tmp_lock);
362         }
363         tself.start = false;
364         terminate = tself.terminate;
365 
366 #if PROFILE
367         auto start = std::chrono::high_resolution_clock::now();
368 #endif
369         if (!terminate) {
370             // run through the filter graph of our sensor
371             for (auto f : sensor->GetFilterList()) {
372                 f->Apply();
373             }
374         }
375 
376         // wait for stream to synchronize
377         cudaStreamSynchronize(sensor->GetCudaStream());
378 #if PROFILE
379         auto elapsed = std::chrono::high_resolution_clock::now() - start;
380         auto milli = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
381         std::cout << "Sensor = " << sensor->GetName() << ", Process time = " << milli << "ms" << std::endl;
382 #endif
383         tself.done = true;
384         tmp_lock.unlock();  // explicitely release the lock on this render thread
385         tself.cv.notify_all();
386     }
387 }
388 
SceneProcess(RenderThread & tself)389 void ChOptixEngine::SceneProcess(RenderThread& tself) {
390     // continually loop and perform two functions: add filters from sensor to job queue, empty the job queue
391 
392     bool terminate = false;
393 
394     // keep the thread running until we submit a terminate job or equivalent
395     while (!terminate) {
396         std::unique_lock<std::mutex> tmp_lock(tself.mutex);
397         // wait for a notification from the main thread
398         while (!tself.start) {
399             tself.cv.wait(tmp_lock);
400         }
401         tself.start = false;  // reset start variable
402         terminate = tself.terminate;
403 
404         if (!terminate) {
405             // wait for the previous render threads before starting this rebuild
406 
407             std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
408             m_geometry->RebuildRootStructure();
409             std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();
410             std::chrono::duration<double> wall_time =
411                 std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1);
412             // launch the render threads
413             for (auto i : m_renderQueue) {
414                 m_renderThreads[i].done = false;
415                 m_renderThreads[i].start = true;
416                 m_renderThreads[i].cv.notify_all();  // notify render thread it should proceed
417 
418                 // for (auto f : m_assignedSensor[i]->GetFilterList()) {
419                 //     f->Apply();
420                 // }
421                 // cudaStreamSynchronize(m_assignedSensor[i]->GetCudaStream());
422             }
423 
424             // wait for each of the thread to be done before we say we are done
425             for (auto i : m_renderQueue) {
426                 std::unique_lock<std::mutex> lck(m_renderThreads[i].mutex);
427                 while (!m_renderThreads[i].done) {
428                     m_renderThreads[i].cv.wait(lck);
429                 }
430             }
431         }
432         m_renderQueue.clear();  // empty list of sensor when everything is processed
433         tself.done = true;
434         tmp_lock.unlock();      // explicitely release the lock on the job queue
435         tself.cv.notify_all();  // wake up anyone waiting for us
436     }
437 }
438 
boxVisualization(std::shared_ptr<ChBody> body,std::shared_ptr<ChBoxShape> box_shape,std::shared_ptr<ChVisualization> visual_asset)439 void ChOptixEngine::boxVisualization(std::shared_ptr<ChBody> body,
440                                      std::shared_ptr<ChBoxShape> box_shape,
441                                      std::shared_ptr<ChVisualization> visual_asset) {
442     ChVector<double> size = box_shape->GetBoxGeometry().GetLengths();
443     ChFrame<double> asset_frame = ChFrame<double>(visual_asset->Pos, visual_asset->Rot);
444 
445     unsigned int mat_id;
446     if (box_shape->material_list.size() == 0) {
447         mat_id = m_pipeline->GetBoxMaterial();
448     } else {
449         mat_id = m_pipeline->GetBoxMaterial(box_shape->material_list[0]);
450     }
451     m_geometry->AddBox(body, asset_frame, size, mat_id);
452     m_pipeline->AddBody(body);
453 }
454 
sphereVisualization(std::shared_ptr<ChBody> body,std::shared_ptr<ChSphereShape> sphere_shape,std::shared_ptr<ChVisualization> visual_asset)455 void ChOptixEngine::sphereVisualization(std::shared_ptr<ChBody> body,
456                                         std::shared_ptr<ChSphereShape> sphere_shape,
457                                         std::shared_ptr<ChVisualization> visual_asset) {
458     ChVector<double> size = {sphere_shape->GetSphereGeometry().rad, sphere_shape->GetSphereGeometry().rad,
459                              sphere_shape->GetSphereGeometry().rad};
460     ChFrame<double> asset_frame =
461         ChFrame<double>(visual_asset->Pos + sphere_shape->GetSphereGeometry().center, visual_asset->Rot);
462 
463     unsigned int mat_id;
464     if (sphere_shape->material_list.size() == 0) {
465         mat_id = m_pipeline->GetSphereMaterial();
466     } else {
467         mat_id = m_pipeline->GetSphereMaterial(sphere_shape->material_list[0]);
468     }
469     m_geometry->AddSphere(body, asset_frame, size, mat_id);
470     m_pipeline->AddBody(body);
471 }
472 
cylinderVisualization(std::shared_ptr<ChBody> body,std::shared_ptr<ChCylinderShape> cyl_shape,std::shared_ptr<ChVisualization> visual_asset)473 void ChOptixEngine::cylinderVisualization(std::shared_ptr<ChBody> body,
474                                           std::shared_ptr<ChCylinderShape> cyl_shape,
475                                           std::shared_ptr<ChVisualization> visual_asset) {
476     double radius = cyl_shape->GetCylinderGeometry().rad;
477     double height = (cyl_shape->GetCylinderGeometry().p1 - cyl_shape->GetCylinderGeometry().p2).Length();
478     ChVector<double> center = (cyl_shape->GetCylinderGeometry().p1 + cyl_shape->GetCylinderGeometry().p2) / 2;
479 
480     ChVector<double> size = {radius, height, radius};
481     ChFrame<double> asset_frame = ChFrame<double>(visual_asset->Pos + center, visual_asset->Rot);
482 
483     unsigned int mat_id;
484     if (cyl_shape->material_list.size() == 0) {
485         mat_id = m_pipeline->GetCylinderMaterial();
486     } else {
487         mat_id = m_pipeline->GetCylinderMaterial(cyl_shape->material_list[0]);
488     }
489     m_geometry->AddCylinder(body, asset_frame, size, mat_id);
490     m_pipeline->AddBody(body);
491 }
492 
rigidMeshVisualization(std::shared_ptr<ChBody> body,std::shared_ptr<ChTriangleMeshShape> mesh_shape,std::shared_ptr<ChVisualization> visual_asset)493 void ChOptixEngine::rigidMeshVisualization(std::shared_ptr<ChBody> body,
494                                            std::shared_ptr<ChTriangleMeshShape> mesh_shape,
495                                            std::shared_ptr<ChVisualization> visual_asset) {
496     if (mesh_shape->IsWireframe()) {
497         std::cerr << "WARNING: Chrono::Sensor does not support wireframe meshes. Defaulting back to solid mesh, please "
498                      "check for visual issues.\n";
499     }
500     ChVector<double> size = mesh_shape->GetScale();
501     ChFrame<double> asset_frame = ChFrame<double>(visual_asset->Pos, visual_asset->Rot);
502 
503     unsigned int mat_id;
504 
505     if (mesh_shape->material_list.size() == 0) {
506         // Create a "proper" mesh if one doesn't already exist for it
507         CreateModernMeshAssets(mesh_shape);
508     }
509 
510     CUdeviceptr d_vertex_buffer;  // handle will go to m_geometry
511     CUdeviceptr d_index_buffer;   // handle will go to m_geometry
512     // still possible there are no materials, but the pipeline will make a default if none exist
513     mat_id = m_pipeline->GetRigidMeshMaterial(d_vertex_buffer, d_index_buffer, mesh_shape, mesh_shape->material_list);
514     m_geometry->AddRigidMesh(d_vertex_buffer, d_index_buffer, mesh_shape, body, asset_frame, size, mat_id);
515     m_pipeline->AddBody(body);
516 }
517 
deformableMeshVisualization(std::shared_ptr<ChBody> body,std::shared_ptr<ChTriangleMeshShape> mesh_shape,std::shared_ptr<ChVisualization> visual_asset)518 void ChOptixEngine::deformableMeshVisualization(std::shared_ptr<ChBody> body,
519                                                 std::shared_ptr<ChTriangleMeshShape> mesh_shape,
520                                                 std::shared_ptr<ChVisualization> visual_asset) {
521     if (mesh_shape->IsWireframe()) {
522         std::cerr << "WARNING: Chrono::Sensor does not support wireframe meshes. Defaulting back to solid mesh, please "
523                      "check for visual issues.\n";
524     }
525     ChVector<double> size = mesh_shape->GetScale();
526     ChFrame<double> asset_frame = ChFrame<double>(visual_asset->Pos, visual_asset->Rot);
527 
528     unsigned int mat_id;
529 
530     if (mesh_shape->material_list.size() == 0) {
531         // Create a "proper" mesh if one doesn't already exist for it
532         CreateModernMeshAssets(mesh_shape);
533     }
534 
535     CUdeviceptr d_vertex_buffer;  // handle will go to m_geometry
536     CUdeviceptr d_index_buffer;   // handle will go to m_geometry
537     // still possible there are no materials, but the pipeline will make a default if none exist
538     mat_id =
539         m_pipeline->GetDeformableMeshMaterial(d_vertex_buffer, d_index_buffer, mesh_shape, mesh_shape->material_list);
540     m_geometry->AddDeformableMesh(d_vertex_buffer, d_index_buffer, mesh_shape, body, asset_frame, size, mat_id);
541     m_pipeline->AddBody(body);
542 }
543 
ConstructScene()544 void ChOptixEngine::ConstructScene() {
545     // need to lock before touching any optix stuff
546     // std::lock_guard<std::mutex> lck(
547     //     m_sceneThread.mutex);  /// here we should not wait for a notify, it is good enough to get a lock
548     std::unique_lock<std::mutex> lck(m_sceneThread.mutex);
549     while (!m_sceneThread.done) {
550         m_sceneThread.cv.wait(lck);
551     }
552     cudaDeviceSynchronize();
553     // wipeout all of old scene
554     m_geometry->Cleanup();         // remove all geometry
555     m_pipeline->CleanMaterials();  // remove all programs and materials
556 
557     // iterate through all bodies in Chrono and add a subnode for each body in Chrono
558     for (auto body : m_system->Get_bodylist()) {
559         // check that the body list is not empty
560         if (body->GetAssets().size() > 0) {
561             // iterate through all assets in the body
562             for (auto asset : body->GetAssets()) {
563                 // check if the asset is a ChVisualization
564 
565                 if (std::shared_ptr<ChVisualization> visual_asset = std::dynamic_pointer_cast<ChVisualization>(asset)) {
566                     // collect relative position and orientation of the asset
567                     // ChVector<double> asset_pos = visual_asset->Pos;
568                     // ChMatrix33<double> asset_rot_mat = visual_asset->Rot;
569 
570                     // const ChFrame<float> asset_frame = ChFrame<float>(asset_pos,asset_rot_mat);
571 
572                     if (!visual_asset->IsVisible()) {
573                         std::cout << "Ignoring an asset that is set to invisible\n";
574                     } else if (std::shared_ptr<ChBoxShape> box_shape = std::dynamic_pointer_cast<ChBoxShape>(asset)) {
575                         boxVisualization(body, box_shape, visual_asset);
576 
577                     } else if (std::shared_ptr<ChSphereShape> sphere_shape =
578                                    std::dynamic_pointer_cast<ChSphereShape>(asset)) {
579                         sphereVisualization(body, sphere_shape, visual_asset);
580 
581                     } else if (std::shared_ptr<ChCylinderShape> cylinder_shape =
582                                    std::dynamic_pointer_cast<ChCylinderShape>(asset)) {
583                         cylinderVisualization(body, cylinder_shape, visual_asset);
584 
585                     } else if (std::shared_ptr<ChTriangleMeshShape> trimesh_shape =
586                                    std::dynamic_pointer_cast<ChTriangleMeshShape>(asset)) {
587                         if (trimesh_shape->IsStatic()) {
588                             rigidMeshVisualization(body, trimesh_shape, visual_asset);
589 
590                             // added_asset_for_body = true;
591                         } else {
592                             deformableMeshVisualization(body, trimesh_shape, visual_asset);
593                         }
594 
595                     } else if (std::shared_ptr<ChEllipsoidShape> ellipsoid_shape =
596                                    std::dynamic_pointer_cast<ChEllipsoidShape>(asset)) {
597                     } else if (std::shared_ptr<ChConeShape> cone_shape =
598                                    std::dynamic_pointer_cast<ChConeShape>(asset)) {
599                     } else if (std::shared_ptr<ChRoundedBoxShape> shape =
600                                    std::dynamic_pointer_cast<ChRoundedBoxShape>(asset)) {
601                     } else if (std::shared_ptr<ChCapsuleShape> capsule_shape =
602                                    std::dynamic_pointer_cast<ChCapsuleShape>(asset)) {
603                     } else if (std::shared_ptr<ChPathShape> path_shape =
604                                    std::dynamic_pointer_cast<ChPathShape>(asset)) {
605                     } else if (std::shared_ptr<ChLineShape> line_shape =
606                                    std::dynamic_pointer_cast<ChLineShape>(asset)) {
607                     }
608 
609                     // check if the asset is a ChColorAsset
610                     else if (std::shared_ptr<ChColorAsset> color_asset =
611                                  std::dynamic_pointer_cast<ChColorAsset>(asset)) {
612                         // std::cout << "Asset was color\n";
613                     }
614 
615                     // check if the asset is a ChTexture
616                     else if (std::shared_ptr<ChTexture> texture_asset = std::dynamic_pointer_cast<ChTexture>(asset)) {
617                         // std::cout << "Asset was texture\n";
618                     }
619                 }
620             }
621         }
622     }
623 
624     // Assumption made here that other physics items don't have a transform -> not always true!!!
625     for (auto item : m_system->Get_otherphysicslist()) {
626         // add items one by one
627 
628         if (item->GetAssets().size() > 0) {
629             for (auto asset : item->GetAssets()) {
630                 if (std::shared_ptr<ChVisualization> visual_asset = std::dynamic_pointer_cast<ChVisualization>(asset)) {
631                     // ChVector<double> asset_pos = visual_asset->Pos;
632                     // ChMatrix33<double> asset_rot_mat = visual_asset->Rot;
633 
634                     auto dummy_body = chrono_types::make_shared<ChBody>();
635 
636                     if (!visual_asset->IsVisible()) {
637                         std::cout << "Ignoring an asset that is set to invisible in otherphysicslist\n";
638                     } else if (std::shared_ptr<ChBoxShape> box_shape = std::dynamic_pointer_cast<ChBoxShape>(asset)) {
639                         boxVisualization(dummy_body, box_shape, visual_asset);
640                     } else if (std::shared_ptr<ChCylinderShape> cylinder_shape =
641                                    std::dynamic_pointer_cast<ChCylinderShape>(asset)) {
642                         cylinderVisualization(dummy_body, cylinder_shape, visual_asset);
643                     } else if (std::shared_ptr<ChSphereShape> sphere_shape =
644                                    std::dynamic_pointer_cast<ChSphereShape>(asset)) {
645                         sphereVisualization(dummy_body, sphere_shape, visual_asset);
646                     } else if (std::shared_ptr<ChTriangleMeshShape> trimesh_shape =
647                                    std::dynamic_pointer_cast<ChTriangleMeshShape>(asset)) {
648                         if (trimesh_shape->IsStatic()) {
649                             rigidMeshVisualization(dummy_body, trimesh_shape, visual_asset);
650                         } else {
651                             deformableMeshVisualization(dummy_body, trimesh_shape, visual_asset);
652                         }
653                     }
654                 }
655             }
656         }
657     }
658 
659     m_params.root = m_geometry->CreateRootStructure();
660     m_pipeline->UpdateAllSBTs();
661     m_pipeline->UpdateAllPipelines();
662     m_params.mesh_pool = reinterpret_cast<MeshParameters*>(m_pipeline->GetMeshPool());
663     m_params.material_pool = reinterpret_cast<MaterialParameters*>(m_pipeline->GetMaterialPool());
664     cudaMemcpy(reinterpret_cast<void*>(md_params), &m_params, sizeof(ContextParameters), cudaMemcpyHostToDevice);
665 }
666 
UpdateCameraTransforms()667 void ChOptixEngine::UpdateCameraTransforms() {
668     // go through all of the bodies
669     for (int i = 0; i < m_assignedSensor.size(); i++) {
670         auto sensor = m_assignedSensor[i];
671 
672         // update radar velocity
673         if (auto radar = std::dynamic_pointer_cast<ChRadarSensor>(sensor)) {
674             ChVector<float> origin(0, 0, 0);
675             auto r = radar->GetOffsetPose().GetPos() - origin;
676             auto ang_vel = radar->GetAngularVelocity() % r;
677             auto vel_abs =
678                 radar->GetOffsetPose().TransformDirectionLocalToParent(ang_vel) + radar->GetTranslationalVelocity();
679             m_assignedRenderers[i]->m_raygen_record->data.specific.radar.velocity.x = vel_abs.x();
680             m_assignedRenderers[i]->m_raygen_record->data.specific.radar.velocity.y = vel_abs.y();
681             m_assignedRenderers[i]->m_raygen_record->data.specific.radar.velocity.z = vel_abs.z();
682             m_pipeline->UpdateObjectVelocity();
683         }
684 
685         ChFrame<double> f_offset = sensor->GetOffsetPose();
686         ChFrame<double> f_body_0 = m_cameraStartFrames[i];
687         m_cameraStartFrames_set[i] = false;  // reset this camera frame so that we know it should be packed again
688         ChFrame<double> f_body_1 = sensor->GetParent()->GetAssetsFrame();
689         ChFrame<double> global_loc_0 = f_body_0 * f_offset;
690         ChFrame<double> global_loc_1 = f_body_1 * f_offset;
691         m_assignedRenderers[i]->m_raygen_record->data.t0 =
692             (float)(m_system->GetChTime() - sensor->GetCollectionWindow());
693         m_assignedRenderers[i]->m_raygen_record->data.t1 = (float)(m_system->GetChTime());
694         m_assignedRenderers[i]->m_raygen_record->data.pos0 = make_float3(
695             (float)global_loc_0.GetPos().x(), (float)global_loc_0.GetPos().y(), (float)global_loc_0.GetPos().z());
696         m_assignedRenderers[i]->m_raygen_record->data.rot0 =
697             make_float4((float)global_loc_0.GetRot().e0(), (float)global_loc_0.GetRot().e1(),
698                         (float)global_loc_0.GetRot().e2(), (float)global_loc_0.GetRot().e3());
699         m_assignedRenderers[i]->m_raygen_record->data.pos1 = make_float3(
700             (float)global_loc_1.GetPos().x(), (float)global_loc_1.GetPos().y(), (float)global_loc_1.GetPos().z());
701         m_assignedRenderers[i]->m_raygen_record->data.rot1 =
702             make_float4((float)global_loc_1.GetRot().e0(), (float)global_loc_1.GetRot().e1(),
703                         (float)global_loc_1.GetRot().e2(), (float)global_loc_1.GetRot().e3());
704         m_assignedRenderers[i]->m_time_stamp = (float)m_system->GetChTime();
705     }
706 }
707 
UpdateDeformableMeshes()708 void ChOptixEngine::UpdateDeformableMeshes() {
709     // update the mesh in the pipeline
710     m_pipeline->UpdateDeformableMeshes();
711     // update the meshes in the geometric scene
712     m_geometry->UpdateDeformableMeshes();
713 }
714 
UpdateSceneDescription(std::shared_ptr<ChScene> scene)715 void ChOptixEngine::UpdateSceneDescription(std::shared_ptr<ChScene> scene) {
716     if (scene->GetBackgroundChanged()) {
717         m_pipeline->UpdateBackground(scene->GetBackground());
718 
719         m_params.scene_epsilon = scene->GetSceneEpsilon();
720         cudaMemcpy(reinterpret_cast<void*>(md_params), &m_params, sizeof(ContextParameters), cudaMemcpyHostToDevice);
721         scene->ResetBackgroundChanged();
722     }
723 
724     if (scene->GetLightsChanged()) {
725         std::vector<PointLight> l = scene->GetPointLights();
726         if (l.size() != m_params.num_lights) {  // need new memory in this case
727             if (m_params.lights)
728                 CUDA_ERROR_CHECK(cudaFree(reinterpret_cast<void*>(m_params.lights)));
729 
730             cudaMalloc(reinterpret_cast<void**>(&m_params.lights), l.size() * sizeof(PointLight));
731         }
732 
733         cudaMemcpy(reinterpret_cast<void*>(m_params.lights), l.data(), l.size() * sizeof(PointLight),
734                    cudaMemcpyHostToDevice);
735         m_params.num_lights = static_cast<int>(l.size());
736         m_params.ambient_light_color = {scene->GetAmbientLight().x(), scene->GetAmbientLight().y(),
737                                         scene->GetAmbientLight().z()};
738         cudaMemcpy(reinterpret_cast<void*>(md_params), &m_params, sizeof(ContextParameters), cudaMemcpyHostToDevice);
739         scene->ResetLightsChanged();
740     }
741 }
742 }  // namespace sensor
743 }  // namespace chrono
744