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