Skip to content

Commit f9586a0

Browse files
authored
Add skeleton animation (mesh skinning) (#328)
* Convert update verticies into external animator * Add empty skeleton API calls * Implement Skeleton * Add rgl_mesh_set_bone_weights * Bind skeleton to entity (+empty SkeletonAnimator class) * Rename rgl_entity_set_pose -> rgl_entity_set_transform * Fix GASBuilder * BoneWeights: fix member's order & separate definition to avoid massive includes in cuda * Add rgl_mesh_set_restposes * Remove Skeleton API object * Add rgl_entity_set_pose_world and SkeletionAnimator logic * Validate mesh when creating SkeletonAnimator * Calculate displacement for SkeletonAnimator * Renaming * Remove TODO verification (verified) * Add test to validate velocity resulted from entity's animation * Extract function for animation time update * Delete GAS builders for static meshes if no needed * Update docs * Simplify std::visit * Review changes * Review changes
1 parent 8d7f86c commit f9586a0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1084
-310
lines changed

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,14 @@ set(RGL_SOURCE_FILES
104104
src/gpu/helpersKernels.cu
105105
src/gpu/gaussianNoiseKernels.cu
106106
src/gpu/nodeKernels.cu
107+
src/gpu/sceneKernels.cu
107108
src/scene/Scene.cpp
108109
src/scene/Mesh.cpp
109110
src/scene/Entity.cpp
110111
src/scene/Texture.cpp
111112
src/scene/ASBuildScratchpad.cpp
113+
src/scene/animator/ExternalAnimator.cpp
114+
src/scene/animator/SkeletonAnimator.cpp
112115
src/graph/GraphRunCtx.cpp
113116
src/graph/Node.cpp
114117
src/graph/GaussianNoiseAngularHitpointNode.cpp

docs/Usage.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ rgl_mat3x4f entity_tf = {
9595
{ 0, 1, 0, 0 },
9696
{ 0, 0, 1, 5 }}
9797
};
98-
rgl_entity_set_pose(cube_entity, &entity_tf);
98+
rgl_entity_set_transform(cube_entity, &entity_tf);
9999

100100
// Create a Graph representation of a lidar that sends 1 ray and is situated at (x,y,z) = (0, 0, 0), facing positive Z
101101
rgl_mat3x4f ray_tf = {

include/rgl/api/core.h

Lines changed: 75 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,31 @@ static_assert(std::is_trivial<rgl_radar_scope_t>::value);
151151
static_assert(std::is_standard_layout<rgl_radar_scope_t>::value);
152152
#endif
153153

154+
/**
155+
* Describes 4 bone weights affecting a mesh vertex.
156+
* The sum of all weights for a given vertex should equal 1 (RGL do not normalize them).
157+
* If a vertex is affected by fewer than 4 bones, each of the remaining weight values must be 0.
158+
* bone_indexes for unused bones must still be valid (filled with the existing bone indexes).
159+
*/
160+
typedef struct
161+
{
162+
/**
163+
* Array for skinning weights.
164+
* The weight at each index corresponds to the bone_idx with the same index.
165+
*/
166+
float weights[4];
167+
/**
168+
* Array for 4 bone indexes affecting a vertex.
169+
*/
170+
int32_t bone_indexes[4];
171+
} rgl_bone_weights_t;
172+
173+
#ifdef __cplusplus
174+
static_assert(sizeof(rgl_bone_weights_t) == 4 * sizeof(float) + 4 * sizeof(int32_t));
175+
static_assert(std::is_trivial<rgl_bone_weights_t>::value);
176+
static_assert(std::is_standard_layout<rgl_bone_weights_t>::value);
177+
#endif
178+
154179
/**
155180
* Radar object class used in object tracking.
156181
*/
@@ -369,7 +394,8 @@ typedef enum : int32_t
369394
* - linear velocity
370395
* - angular velocity
371396
* - mesh deformations (e.g. skinning)
372-
* The aforementioned are inferred from calls to `rgl_entity_set_pose`, `rgl_scene_set_time` and `rgl_mesh_update_vertices`.
397+
* The aforementioned are inferred from calls to
398+
* `rgl_entity_set_transform`, `rgl_scene_set_time` and `rgl_entity_apply_external_animation`, `rgl_entity_set_pose_world`.
373399
*/
374400
RGL_FIELD_ABSOLUTE_VELOCITY_VEC3_F32,
375401

@@ -512,23 +538,35 @@ RGL_API rgl_status_t rgl_mesh_create(rgl_mesh_t* out_mesh, const rgl_vec3f* vert
512538
*/
513539
RGL_API rgl_status_t rgl_mesh_set_texture_coords(rgl_mesh_t mesh, const rgl_vec2f* uvs, int32_t uv_count);
514540

541+
/**
542+
* Assign bone weights to given Mesh.
543+
*
544+
* @param mesh Mesh to modify.
545+
* @param bone_weights An array of rgl_bone_weights_t objects that associate vertices with the bones affecting them.
546+
The bone weights object at each index corresponds to the vertex with the same index (`vertices` array of `rgl_mesh_create` API call).
547+
* @param bone_weights_count Number of elements in the bone_weights array. It must be equal to the vertex count of the Mesh!
548+
*/
549+
RGL_API rgl_status_t rgl_mesh_set_bone_weights(rgl_mesh_t mesh, const rgl_bone_weights_t* bone_weights,
550+
int32_t bone_weights_count);
551+
552+
/**
553+
* Assign restposes to given Mesh.
554+
*
555+
* @param mesh Mesh to modify.
556+
* @param restposes An array containing inverse of the transformation matrix of the bone in restpose for each bone.
557+
* Restpose at each index in the array corresponds to the bone with the same index.
558+
* Typically, the restpose is the same as the bindpose.
559+
* @param bones_count Number of elements in the restposes array.
560+
*/
561+
RGL_API rgl_status_t rgl_mesh_set_restposes(rgl_mesh_t mesh, const rgl_mat3x4f* restposes, int32_t bones_count);
562+
515563
/**
516564
* Informs that the given Mesh will be no longer used.
517565
* The Mesh will be destroyed after all referring Entities are destroyed.
518566
* @param mesh Mesh to be marked as no longer needed
519567
*/
520568
RGL_API rgl_status_t rgl_mesh_destroy(rgl_mesh_t mesh);
521569

522-
/**
523-
* Updates Mesh vertex data. The number of vertices must not change.
524-
* This function is intended to update animated Meshes.
525-
* Should be called after rgl_scene_set_time to ensure proper velocity computation.
526-
* @param mesh Mesh to modify
527-
* @param vertices An array of rgl_vec3f or binary-compatible data representing Mesh vertices
528-
* @param vertex_count Number of elements in the vertices array. It must be equal to the original vertex count!
529-
*/
530-
RGL_API rgl_status_t rgl_mesh_update_vertices(rgl_mesh_t mesh, const rgl_vec3f* vertices, int32_t vertex_count);
531-
532570
/**
533571
* Assigns value true to out_alive if the given mesh is known and has not been destroyed,
534572
* assigns value false otherwise.
@@ -561,7 +599,20 @@ RGL_API rgl_status_t rgl_entity_destroy(rgl_entity_t entity);
561599
* @param entity Entity to modify
562600
* @param transform Pointer to rgl_mat3x4f (or binary-compatible data) representing desired (Entity -> world) coordinate system transform.
563601
*/
564-
RGL_API rgl_status_t rgl_entity_set_pose(rgl_entity_t entity, const rgl_mat3x4f* transform);
602+
RGL_API rgl_status_t rgl_entity_set_transform(rgl_entity_t entity, const rgl_mat3x4f* transform);
603+
604+
/**
605+
* Set the current pose of the given Entity in world coordinates.
606+
* The pose stands for bone transforms used in skeleton animation.
607+
* The mesh associated with this entity must have bone weights and restposes assigned.
608+
* Since it is expected the pose is already in world coordinates, the API call `rgl_entity_set_transform` should no longer be called on this entity.
609+
* Should be called after rgl_scene_set_time to ensure proper velocity computation.
610+
* @param entity Entity to modify.
611+
* @param pose An array containing transformation matrices of the bones in world coordinates.
612+
* Bone transform at each index corresponds to the bone with the same index.
613+
* @param bones_count Number of elements in the pose array. It must be equal to restposes count in the associated mesh!
614+
*/
615+
RGL_API rgl_status_t rgl_entity_set_pose_world(rgl_entity_t entity, const rgl_mat3x4f* pose, int32_t bones_count);
565616

566617
/**
567618
* Set instance ID of the given Entity.
@@ -586,6 +637,17 @@ RGL_API rgl_status_t rgl_entity_set_intensity_texture(rgl_entity_t entity, rgl_t
586637
*/
587638
RGL_API rgl_status_t rgl_entity_set_laser_retro(rgl_entity_t entity, float retro);
588639

640+
/**
641+
* Provides updated vertices to the Entity resulted from external animation system.
642+
* It does not modify Mesh API object bound to the Entity.
643+
* The number of vertices must not change.
644+
* Should be called after rgl_scene_set_time to ensure proper velocity computation.
645+
* @param entity Entity to modify
646+
* @param vertices An array of rgl_vec3f or binary-compatible data representing Mesh vertices
647+
* @param vertex_count Number of elements in the vertices array. It must be equal to the original vertex count!
648+
*/
649+
RGL_API rgl_status_t rgl_entity_apply_external_animation(rgl_entity_t entity, const rgl_vec3f* vertices, int32_t vertex_count);
650+
589651
/**
590652
* Assigns value true to out_alive if the given entity is known and has not been destroyed,
591653
* assigns value false otherwise.
@@ -723,7 +785,7 @@ RGL_API rgl_status_t rgl_node_raytrace(rgl_node_t* node, rgl_scene_t scene);
723785
* Necessary for velocity distortion or calculating fields: RGL_FIELD_RELATIVE_VELOCITY_VEC3_F32 and RGL_FIELD_RADIAL_SPEED_F32.
724786
* Relative velocity calculation:
725787
* To calculate relative velocity the pipeline must allow to compute absolute velocities. For more details refer to API calls documentation:
726-
* `rgl_scene_set_time`, `rgl_entity_set_pose`, and `rgl_mesh_update_vertices`
788+
* `rgl_scene_set_time`, `rgl_entity_set_transform`, `rgl_entity_set_pose_world`, and `rgl_entity_apply_external_animation`
727789
* @param node RaytraceNode to modify
728790
* @param linear_velocity 3D vector for linear velocity in units per second.
729791
* @param angular_velocity 3D vector for angular velocity in radians per second (roll, pitch, yaw).

src/api/apiCore.cpp

Lines changed: 85 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -217,43 +217,65 @@ void TapeCore::tape_mesh_set_texture_coords(const YAML::Node& yamlNode, Playback
217217
yamlNode[2].as<int32_t>());
218218
}
219219

220-
RGL_API rgl_status_t rgl_mesh_destroy(rgl_mesh_t mesh)
220+
RGL_API rgl_status_t rgl_mesh_set_bone_weights(rgl_mesh_t mesh, const rgl_bone_weights_t* bone_weights,
221+
int32_t bone_weights_count)
221222
{
222223
auto status = rglSafeCall([&]() {
223-
RGL_API_LOG("rgl_mesh_destroy(mesh={})", (void*) mesh);
224+
RGL_API_LOG("rgl_mesh_set_bone_weights(mesh={}, bone_weights={})", (void*) mesh,
225+
repr(bone_weights, bone_weights_count));
224226
CHECK_ARG(mesh != nullptr);
227+
CHECK_ARG(bone_weights != nullptr);
228+
CHECK_ARG(bone_weights_count > 0);
225229
GraphRunCtx::synchronizeAll(); // Prevent races with graph threads
226-
Mesh::release(mesh);
230+
Mesh::validatePtr(mesh)->setBoneWeights(bone_weights, bone_weights_count);
227231
});
228-
TAPE_HOOK(mesh);
232+
TAPE_HOOK(mesh, TAPE_ARRAY(bone_weights, bone_weights_count), bone_weights_count);
229233
return status;
230234
}
231235

232-
void TapeCore::tape_mesh_destroy(const YAML::Node& yamlNode, PlaybackState& state)
236+
void TapeCore::tape_mesh_set_bone_weights(const YAML::Node& yamlNode, PlaybackState& state)
233237
{
234-
auto meshId = yamlNode[0].as<TapeAPIObjectID>();
235-
rgl_mesh_destroy(state.meshes.at(meshId));
236-
state.meshes.erase(meshId);
238+
rgl_mesh_set_bone_weights(state.meshes.at(yamlNode[0].as<TapeAPIObjectID>()),
239+
state.getPtr<const rgl_bone_weights_t>(yamlNode[1]), yamlNode[2].as<int32_t>());
237240
}
238241

239-
RGL_API rgl_status_t rgl_mesh_update_vertices(rgl_mesh_t mesh, const rgl_vec3f* vertices, int32_t vertex_count)
242+
RGL_API rgl_status_t rgl_mesh_set_restposes(rgl_mesh_t mesh, const rgl_mat3x4f* restposes, int32_t restposes_count)
240243
{
241244
auto status = rglSafeCall([&]() {
242-
RGL_API_LOG("rgl_mesh_update_vertices(mesh={}, vertices={})", (void*) mesh, repr(vertices, vertex_count));
245+
RGL_API_LOG("rgl_mesh_set_restposes(mesh={}, restposes={})", (void*) mesh, repr(restposes, restposes_count));
243246
CHECK_ARG(mesh != nullptr);
244-
CHECK_ARG(vertices != nullptr);
245-
CHECK_ARG(vertex_count > 0);
247+
CHECK_ARG(restposes != nullptr);
248+
CHECK_ARG(restposes_count > 0);
246249
GraphRunCtx::synchronizeAll(); // Prevent races with graph threads
247-
Mesh::validatePtr(mesh)->updateVertices(reinterpret_cast<const Vec3f*>(vertices), vertex_count);
250+
Mesh::validatePtr(mesh)->setRestposes(reinterpret_cast<const Mat3x4f*>(restposes), restposes_count);
248251
});
249-
TAPE_HOOK(mesh, TAPE_ARRAY(vertices, vertex_count), vertex_count);
252+
TAPE_HOOK(mesh, TAPE_ARRAY(restposes, restposes_count), restposes_count);
250253
return status;
251254
}
252255

253-
void TapeCore::tape_mesh_update_vertices(const YAML::Node& yamlNode, PlaybackState& state)
256+
void TapeCore::tape_mesh_set_restposes(const YAML::Node& yamlNode, PlaybackState& state)
254257
{
255-
rgl_mesh_update_vertices(state.meshes.at(yamlNode[0].as<TapeAPIObjectID>()), state.getPtr<const rgl_vec3f>(yamlNode[1]),
256-
yamlNode[2].as<int32_t>());
258+
rgl_mesh_set_restposes(state.meshes.at(yamlNode[0].as<TapeAPIObjectID>()), state.getPtr<const rgl_mat3x4f>(yamlNode[1]),
259+
yamlNode[2].as<int32_t>());
260+
}
261+
262+
RGL_API rgl_status_t rgl_mesh_destroy(rgl_mesh_t mesh)
263+
{
264+
auto status = rglSafeCall([&]() {
265+
RGL_API_LOG("rgl_mesh_destroy(mesh={})", (void*) mesh);
266+
CHECK_ARG(mesh != nullptr);
267+
GraphRunCtx::synchronizeAll(); // Prevent races with graph threads
268+
Mesh::release(mesh);
269+
});
270+
TAPE_HOOK(mesh);
271+
return status;
272+
}
273+
274+
void TapeCore::tape_mesh_destroy(const YAML::Node& yamlNode, PlaybackState& state)
275+
{
276+
auto meshId = yamlNode[0].as<TapeAPIObjectID>();
277+
rgl_mesh_destroy(state.meshes.at(meshId));
278+
state.meshes.erase(meshId);
257279
}
258280

259281
rgl_status_t rgl_mesh_is_alive(rgl_mesh_t mesh, bool* out_alive)
@@ -308,10 +330,10 @@ void TapeCore::tape_entity_destroy(const YAML::Node& yamlNode, PlaybackState& st
308330
state.entities.erase(entityId);
309331
}
310332

311-
RGL_API rgl_status_t rgl_entity_set_pose(rgl_entity_t entity, const rgl_mat3x4f* transform)
333+
RGL_API rgl_status_t rgl_entity_set_transform(rgl_entity_t entity, const rgl_mat3x4f* transform)
312334
{
313335
auto status = rglSafeCall([&]() {
314-
RGL_API_LOG("rgl_entity_set_pose(entity={}, transform={})", (void*) entity, repr(transform, 1));
336+
RGL_API_LOG("rgl_entity_set_transform(entity={}, transform={})", (void*) entity, repr(transform, 1));
315337
CHECK_ARG(entity != nullptr);
316338
CHECK_ARG(transform != nullptr);
317339
GraphRunCtx::synchronizeAll(); // Prevent races with graph threads
@@ -322,9 +344,30 @@ RGL_API rgl_status_t rgl_entity_set_pose(rgl_entity_t entity, const rgl_mat3x4f*
322344
return status;
323345
}
324346

325-
void TapeCore::tape_entity_set_pose(const YAML::Node& yamlNode, PlaybackState& state)
347+
void TapeCore::tape_entity_set_transform(const YAML::Node& yamlNode, PlaybackState& state)
348+
{
349+
rgl_entity_set_transform(state.entities.at(yamlNode[0].as<TapeAPIObjectID>()),
350+
state.getPtr<const rgl_mat3x4f>(yamlNode[1]));
351+
}
352+
353+
RGL_API rgl_status_t rgl_entity_set_pose_world(rgl_entity_t entity, const rgl_mat3x4f* pose, int32_t bones_count)
354+
{
355+
auto status = rglSafeCall([&]() {
356+
RGL_API_LOG("rgl_entity_set_pose_world(entity={}, pose={})", (void*) entity, repr(pose, bones_count));
357+
CHECK_ARG(entity != nullptr);
358+
CHECK_ARG(pose != nullptr);
359+
CHECK_ARG(bones_count > 0);
360+
GraphRunCtx::synchronizeAll(); // Prevent races with graph threads
361+
Entity::validatePtr(entity)->setPoseAndAnimate(reinterpret_cast<const Mat3x4f*>(pose), bones_count);
362+
});
363+
TAPE_HOOK(entity, TAPE_ARRAY(pose, bones_count), bones_count);
364+
return status;
365+
}
366+
367+
void TapeCore::tape_entity_set_pose_world(const YAML::Node& yamlNode, PlaybackState& state)
326368
{
327-
rgl_entity_set_pose(state.entities.at(yamlNode[0].as<TapeAPIObjectID>()), state.getPtr<const rgl_mat3x4f>(yamlNode[1]));
369+
rgl_entity_set_pose_world(state.entities.at(yamlNode[0].as<TapeAPIObjectID>()),
370+
state.getPtr<const rgl_mat3x4f>(yamlNode[1]), yamlNode[2].as<int32_t>());
328371
}
329372

330373
RGL_API rgl_status_t rgl_entity_set_id(rgl_entity_t entity, int32_t id)
@@ -382,6 +425,27 @@ void TapeCore::tape_entity_set_laser_retro(const YAML::Node& yamlNode, PlaybackS
382425
yamlNode[1].as<Field<LASER_RETRO_F32>::type>());
383426
}
384427

428+
RGL_API rgl_status_t rgl_entity_apply_external_animation(rgl_entity_t entity, const rgl_vec3f* vertices, int32_t vertex_count)
429+
{
430+
auto status = rglSafeCall([&]() {
431+
RGL_API_LOG("rgl_entity_apply_external_animation(entity={}, vertices={})", (void*) entity,
432+
repr(vertices, vertex_count));
433+
CHECK_ARG(entity != nullptr);
434+
CHECK_ARG(vertices != nullptr);
435+
CHECK_ARG(vertex_count > 0);
436+
GraphRunCtx::synchronizeAll(); // Prevent races with graph threads
437+
Entity::validatePtr(entity)->applyExternalAnimation(reinterpret_cast<const Vec3f*>(vertices), vertex_count);
438+
});
439+
TAPE_HOOK(entity, TAPE_ARRAY(vertices, vertex_count), vertex_count);
440+
return status;
441+
}
442+
443+
void TapeCore::tape_entity_apply_external_animation(const YAML::Node& yamlNode, PlaybackState& state)
444+
{
445+
rgl_entity_apply_external_animation(state.entities.at(yamlNode[0].as<TapeAPIObjectID>()),
446+
state.getPtr<const rgl_vec3f>(yamlNode[1]), yamlNode[2].as<int32_t>());
447+
}
448+
385449
rgl_status_t rgl_entity_is_alive(rgl_entity_t entity, bool* out_alive)
386450
{
387451
auto status = rglSafeCall([&]() {

src/gpu/helpersKernels.cu

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,3 @@ void gpuSetupRandomNumberGenerator(cudaStream_t stream, size_t elementsCount, un
3131
{
3232
run(kSetupRandomNumberGenerator, stream, elementsCount, seed, outPHILOXStates);
3333
}
34-
// Updates vertices and calculates their displacement.
35-
// Input: newVertices and oldVertices
36-
// Output: verticesDisplacement and newVertices
37-
__global__ void kUpdateVertices(size_t vertexCount, Vec3f* newVerticesToDisplacement, Vec3f* oldToNewVertices)
38-
{
39-
LIMIT(vertexCount);
40-
// See Mesh::updateVertices to understand the logic here.
41-
Vec3f newVertex = newVerticesToDisplacement[tid];
42-
newVerticesToDisplacement[tid] -= oldToNewVertices[tid];
43-
oldToNewVertices[tid] = newVertex;
44-
}
45-
46-
void gpuUpdateVertices(cudaStream_t stream, size_t vertexCount, Vec3f* newVerticesToDisplacement, Vec3f* oldToNewVertices)
47-
{
48-
run(kUpdateVertices, stream, vertexCount, newVerticesToDisplacement, oldToNewVertices);
49-
}

src/gpu/helpersKernels.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,3 @@
2020

2121
void gpuSetupRandomNumberGenerator(cudaStream_t stream, size_t elementsCount, unsigned int seed,
2222
curandStatePhilox4_32_10_t* outPHILOXStates);
23-
24-
void gpuUpdateVertices(cudaStream_t stream, size_t vertexCount, Vec3f* newVerticesToDisplacement, Vec3f* oldToNewVertices);

src/gpu/optixPrograms.cu

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,6 @@ extern "C" __global__ void __closesthit__()
239239
if (wasSkinned) {
240240
Mat3x4f objectToWorld;
241241
optixGetObjectToWorldTransformMatrix(reinterpret_cast<float*>(objectToWorld.rc));
242-
// TODO(msz-rai): To verify if rotation is needed (in some tests it produces more realistic results)
243242
const Vec3f& vA = objectToWorld.rotation() * entityData.vertexDisplacementSincePrevFrame[triangleIndices.x()];
244243
const Vec3f& vB = objectToWorld.rotation() * entityData.vertexDisplacementSincePrevFrame[triangleIndices.y()];
245244
const Vec3f& vC = objectToWorld.rotation() * entityData.vertexDisplacementSincePrevFrame[triangleIndices.z()];

0 commit comments

Comments
 (0)