diff --git a/src/GameLoop.cpp b/src/GameLoop.cpp index bc3d5638..509da483 100644 --- a/src/GameLoop.cpp +++ b/src/GameLoop.cpp @@ -54,9 +54,8 @@ namespace Neptune { struct Track { - std::vector skeleton_nodes; - std::vector splines_ms; - std::vector skinning; + std::vector skeleton_nodes; + std::vector skinning; std::vector sim_handles; std::vector scene_meshes; @@ -77,13 +76,9 @@ namespace generate_track_skeleton(gen_info, track.skeleton_nodes); - track.splines_ms.resize(gen_info.chunk_count); - - generate_track_splines(track.skeleton_nodes, track.splines_ms); - track.skinning.resize(gen_info.chunk_count); - generate_track_skinning(track.skeleton_nodes, track.splines_ms, track.skinning); + generate_track_skinning(track.skeleton_nodes, track.skinning); std::vector chunk_transforms(gen_info.chunk_count); std::vector track_meshes; @@ -466,8 +461,8 @@ void execute_game_loop(ReaperRoot& root) Neptune::GenerationInfo track_gen_info = {}; track_gen_info.chunk_count = 100; track_gen_info.radius_min_meter = 300.f; - track_gen_info.radius_max_meter = 700.f; - track_gen_info.chaos = 0.6f; + track_gen_info.radius_max_meter = 600.f; + track_gen_info.chaos = 0.4f; const SceneMaterialHandle default_material_handle = alloc_scene_material(scene); scene.scene_materials[default_material_handle] = SceneMaterial{ diff --git a/src/neptune/trackgen/Track.cpp b/src/neptune/trackgen/Track.cpp index 3cefb5bd..3ae1a62f 100644 --- a/src/neptune/trackgen/Track.cpp +++ b/src/neptune/trackgen/Track.cpp @@ -53,7 +53,15 @@ using Math::UnitZAxis; namespace { - glm::fmat4x3 GenerateChunkEndLocalSpace(const GenerationInfo& gen_info, RNG& rng) + struct ChunkEnd + { + glm::fmat4x3 transform; + float phi_angle; + float theta_angle; + float roll_angle; + }; + + ChunkEnd generate_chunk_end(const GenerationInfo& gen_info, RNG& rng) { glm::vec2 thetaBounds = glm::vec2(0.0f, ThetaMax) * gen_info.chaos; glm::vec2 phiBounds = glm::vec2(-PhiMax, PhiMax); @@ -70,7 +78,12 @@ namespace glm::quat deviation = glm::angleAxis(phi, UnitXAxis) * glm::angleAxis(theta, UnitZAxis); glm::quat rollFixup = glm::angleAxis(-phi + roll, deviation * UnitXAxis); - return glm::toMat4(rollFixup * deviation); + return ChunkEnd{ + .transform = glm::toMat4(rollFixup * deviation), + .phi_angle = phi, + .theta_angle = theta, + .roll_angle = roll, + }; } bool is_node_self_colliding(std::span nodes, const TrackSkeletonNode& current_node, @@ -118,7 +131,11 @@ namespace node.center_ws = node.in_transform_ms_to_ws * glm::fvec4(forward_offset_ms, 1.f); } - node.end_transform = GenerateChunkEndLocalSpace(gen_info, rng); + const ChunkEnd chunk_end = generate_chunk_end(gen_info, rng); + node.end_transform = chunk_end.transform; + node.phi_angle = chunk_end.phi_angle; + node.theta_angle = chunk_end.theta_angle; + node.roll_angle = chunk_end.roll_angle; const glm::fmat4 translation_a = glm::translate(glm::identity(), forward_offset_ms); const glm::fmat4 translation_b = @@ -172,93 +189,60 @@ void generate_track_skeleton(const GenerationInfo& gen_info, std::span skeleton_nodes, std::span splines) -{ - REAPER_PROFILE_SCOPE_FUNC(); - - Assert(skeleton_nodes.size() == splines.size()); - - for (u32 chunk_index = 0; chunk_index < skeleton_nodes.size(); chunk_index++) - { - const TrackSkeletonNode& node = skeleton_nodes[chunk_index]; - - // Laying down 1 control point at each end and 2 in the center of the sphere - // seems to work pretty well. - std::array controlPoints; - controlPoints[0] = glm::vec4(glm::vec3(0.0f), 1.0f); - controlPoints[1] = glm::vec4(UnitXAxis * node.radius, SplineInnerWeight); - controlPoints[2] = glm::vec4(UnitXAxis * node.radius, SplineInnerWeight); - controlPoints[3] = - glm::vec4(UnitXAxis * node.radius + node.end_transform * glm::fvec4(UnitXAxis * node.radius, 0.f), 1.0f); - - splines[chunk_index] = Math::create_spline(SplineOrder, controlPoints); - } -} - namespace { TrackSkinning generate_track_skinning_for_chunk(const TrackSkeletonNode& node) { - REAPER_PROFILE_SCOPE_FUNC(); - - // NOTE: local transform origin is the 'in' node, not the sphere center - std::array bone_positions_ms; - bone_positions_ms[0] = glm::vec3(0.0f); - bone_positions_ms[1] = UnitXAxis * node.radius; - bone_positions_ms[2] = UnitXAxis * node.radius + node.end_transform * glm::fvec4(UnitXAxis * node.radius, 0.f); + TrackSkinning skinning; - // Fill bone positions - TrackSkinning skinningInfo; - - skinningInfo.bones.resize(BoneCountPerChunk); - - for (u32 bone_index = 0; bone_index < BoneCountPerChunk; bone_index++) - { - skinningInfo.bones[bone_index].root = bone_positions_ms[bone_index]; - skinningInfo.bones[bone_index].end = bone_positions_ms[bone_index + 1]; - } - - skinningInfo.invBindTransforms.resize(BoneCountPerChunk); + skinning.bind_pose_inv_transforms.resize(BoneCountPerChunk); // Compute inverse bind pose matrix for (u32 bone_index = 0; bone_index < BoneCountPerChunk; bone_index++) { - const float t = static_cast(bone_index) / static_cast(BoneCountPerChunk); + const float t = static_cast(bone_index) / static_cast(BoneCountPerChunk - 1); const float param = t * 2.0f - 1.0f; const glm::vec3 offset = UnitXAxis * (param * node.radius); // Inverse of a translation is the translation by the opposite vector - skinningInfo.invBindTransforms[bone_index] = glm::translate(glm::mat4(1.0f), -offset); + skinning.bind_pose_inv_transforms[bone_index] = glm::translate(glm::mat4(1.0f), -offset); } + // NOTE: local transform origin is the 'in' node, not the sphere center + std::array bone_root_positions_ms; + + bone_root_positions_ms[0] = glm::vec3(0.0f); + // FIXME We need to fix positions to be placed on the arc of the trajectory. + // bone_root_positions_ms[1] = UnitXAxis * node.radius; + bone_root_positions_ms[1] = + UnitXAxis * node.radius + node.end_transform * glm::fvec4(UnitXAxis * node.radius, 0.f); // FIXME + // Compute static pose matrix - skinningInfo.poseTransforms.resize(BoneCountPerChunk); + skinning.pose_transforms.resize(BoneCountPerChunk); for (u32 bone_index = 0; bone_index < BoneCountPerChunk; bone_index++) { - const Bone& bone = skinningInfo.bones[bone_index]; + const float t = static_cast(bone_index) / static_cast(BoneCountPerChunk - 1); + const float theta_angle = t * node.theta_angle; + const float roll_angle = t * node.roll_angle; + + glm::quat deviation = glm::angleAxis(node.phi_angle, UnitXAxis) * glm::angleAxis(theta_angle, UnitZAxis); + glm::quat rollFixup = glm::angleAxis(-node.phi_angle + roll_angle, deviation * UnitXAxis); - // Convert to a matrix - const glm::fmat3 rotation = bone_index == 0 ? glm::identity() : glm::fmat3(node.end_transform); - const glm::fmat4x3 translation = glm::translate(glm::identity(), bone.root); + glm::mat4x3 transform = glm::translate(glm::identity(), bone_root_positions_ms[bone_index]); - skinningInfo.poseTransforms[bone_index] = translation * glm::mat4(rotation); + skinning.pose_transforms[bone_index] = transform * glm::toMat4(rollFixup * deviation); } - return skinningInfo; + return skinning; } } // namespace -void generate_track_skinning(std::span skeleton_nodes, - std::span - splines, - std::span - skinning) +void generate_track_skinning(std::span skeleton_nodes, std::span skinning) { - Assert(skeleton_nodes.size() == splines.size()); - Assert(splines.size() == skinning.size()); + Assert(skeleton_nodes.size() == skinning.size()); - for (u32 chunk_index = 0; chunk_index < splines.size(); chunk_index++) + for (u32 chunk_index = 0; chunk_index < skinning.size(); chunk_index++) { skinning[chunk_index] = generate_track_skinning_for_chunk(skeleton_nodes[chunk_index]); } @@ -268,13 +252,23 @@ namespace { std::array compute_bone_weights(glm::vec3 position, float radius) { - const float t = (position.x / radius) * 0.5f + 0.5f; // FIXME - std::array weights = {1.f - t, t}; + float t = (position.x / radius) * 0.5f + 0.5f; + float debug_sum = 0.f; + + std::array weights; + + for (u32 bone_index = 0; bone_index < BoneCountPerChunk; bone_index++) + { + float weight = std::max(0.f, 1.f - glm::abs(t * float(BoneCountPerChunk - 1) - float(bone_index))); + weights[bone_index] = weight; + + debug_sum += weight; + } #if 1 - const float debugSum = weights[0] + weights[1]; - Assert(Math::IsEqualWithEpsilon(debugSum, 1.0f)); + Assert(Math::IsEqualWithEpsilon(debug_sum, 1.0f)); #endif + return weights; } } // namespace @@ -289,8 +283,8 @@ void skin_track_chunk_mesh(const TrackSkeletonNode& node, const TrackSkinning& t for (u32 bone_index = 0; bone_index < bone_count; bone_index++) { - bone_transforms[bone_index] = - track_skinning.poseTransforms[bone_index] * glm::fmat4(track_skinning.invBindTransforms[bone_index]); + bone_transforms[bone_index] = track_skinning.pose_transforms[bone_index] + * glm::fmat4(track_skinning.bind_pose_inv_transforms[bone_index]); } const u32 vertex_count = static_cast(vertices.size()); diff --git a/src/neptune/trackgen/Track.h b/src/neptune/trackgen/Track.h index 799cccac..4f5245a5 100644 --- a/src/neptune/trackgen/Track.h +++ b/src/neptune/trackgen/Track.h @@ -17,14 +17,6 @@ #include -namespace Reaper -{ -namespace Math -{ - struct Spline; -} -} // namespace Reaper - namespace Neptune { constexpr float DefaultRadiusMinMeter = 100.0f; @@ -43,6 +35,10 @@ struct TrackSkeletonNode glm::fmat4x3 in_transform_ms_to_ws; // Transform of the input frame placed on the tangent of the bounding sphere glm::fmat4x3 end_transform; + float phi_angle; + float theta_angle; + float roll_angle; + float radius; float in_width; float out_width; @@ -56,31 +52,18 @@ struct TrackSkeletonNode glm::fmat4x3 out_transform_ws_to_ms; }; -struct Bone -{ - glm::vec3 root; - glm::vec3 end; -}; - struct TrackSkinning { - std::vector bones; - std::vector invBindTransforms; - std::vector poseTransforms; + std::vector bind_pose_inv_transforms; + std::vector pose_transforms; }; NEPTUNE_TRACKGEN_API void generate_track_skeleton(const GenerationInfo& genInfo, std::span skeleton_nodes); NEPTUNE_TRACKGEN_API -void generate_track_splines(std::span skeleton_nodes, std::span splines); - -NEPTUNE_TRACKGEN_API -void generate_track_skinning(std::span skeleton_nodes, - std::span - splines, - std::span - skinning); +void generate_track_skinning( + std::span skeleton_nodes, std::span skinning_array); NEPTUNE_TRACKGEN_API void skin_track_chunk_mesh(const TrackSkeletonNode& node, diff --git a/src/neptune/trackgen/test/gen_simple.cpp b/src/neptune/trackgen/test/gen_simple.cpp index 1b9e22b8..5e92e773 100644 --- a/src/neptune/trackgen/test/gen_simple.cpp +++ b/src/neptune/trackgen/test/gen_simple.cpp @@ -18,9 +18,8 @@ using namespace Neptune; TEST_CASE("Track generation") { - std::vector skeletonNodes; - std::vector splinesMS; - std::vector skinning; + std::vector skeletonNodes; + std::vector skinning; GenerationInfo gen_info; @@ -34,20 +33,11 @@ TEST_CASE("Track generation") SUBCASE("") {} - SUBCASE("Generate splines") + SUBCASE("Generate bones") { - splinesMS.resize(gen_info.chunk_count); + skinning.resize(gen_info.chunk_count); - generate_track_splines(skeletonNodes, splinesMS); - - SUBCASE("") - {} - SUBCASE("Generate bones") - { - skinning.resize(gen_info.chunk_count); - - generate_track_skinning(skeletonNodes, splinesMS, skinning); - } + generate_track_skinning(skeletonNodes, skinning); } } } @@ -56,19 +46,16 @@ TEST_CASE("Track generation") TEST_CASE("Track mesh generation") { - std::vector skeletonNodes; - std::vector splinesMS; - std::vector skinning; + std::vector skeletonNodes; + std::vector skinning; GenerationInfo gen_info; skeletonNodes.resize(gen_info.chunk_count); - splinesMS.resize(gen_info.chunk_count); skinning.resize(gen_info.chunk_count); generate_track_skeleton(gen_info, skeletonNodes); - generate_track_splines(skeletonNodes, splinesMS); - generate_track_skinning(skeletonNodes, splinesMS, skinning); + generate_track_skinning(skeletonNodes, skinning); const std::string assetFile("res/model/track_chunk_simple.obj");