Skip to content

Commit

Permalink
trackgen: improve skinning and ditch splines
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryp committed Mar 29, 2024
1 parent a1a44cf commit 9a38ff8
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 124 deletions.
15 changes: 5 additions & 10 deletions src/GameLoop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,8 @@ namespace Neptune
{
struct Track
{
std::vector<TrackSkeletonNode> skeleton_nodes;
std::vector<Reaper::Math::Spline> splines_ms;
std::vector<TrackSkinning> skinning;
std::vector<TrackSkeletonNode> skeleton_nodes;
std::vector<TrackSkinning> skinning;

std::vector<StaticMeshColliderHandle> sim_handles;
std::vector<Reaper::SceneMesh> scene_meshes;
Expand All @@ -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<glm::fmat4x3> chunk_transforms(gen_info.chunk_count);
std::vector<Reaper::Mesh> track_meshes;
Expand Down Expand Up @@ -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{
Expand Down
130 changes: 62 additions & 68 deletions src/neptune/trackgen/Track.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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<const TrackSkeletonNode> nodes, const TrackSkeletonNode& current_node,
Expand Down Expand Up @@ -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<glm::fmat4>(), forward_offset_ms);
const glm::fmat4 translation_b =
Expand Down Expand Up @@ -172,93 +189,60 @@ void generate_track_skeleton(const GenerationInfo& gen_info, std::span<TrackSkel
Assert(tryCount < MaxTryCount, "something is majorly FUBAR");
}

void generate_track_splines(std::span<const TrackSkeletonNode> skeleton_nodes, std::span<Reaper::Math::Spline> 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<glm::fvec4, 4> 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<glm::vec3, BoneCountPerChunk + 1> 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<float>(bone_index) / static_cast<float>(BoneCountPerChunk);
const float t = static_cast<float>(bone_index) / static_cast<float>(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<glm::vec3, BoneCountPerChunk> 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<float>(bone_index) / static_cast<float>(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>() : glm::fmat3(node.end_transform);
const glm::fmat4x3 translation = glm::translate(glm::identity<glm::fmat4>(), bone.root);
glm::mat4x3 transform = glm::translate(glm::identity<glm::fmat4>(), 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<const TrackSkeletonNode> skeleton_nodes,
std::span<const Reaper::Math::Spline>
splines,
std::span<TrackSkinning>
skinning)
void generate_track_skinning(std::span<const TrackSkeletonNode> skeleton_nodes, std::span<TrackSkinning> 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]);
}
Expand All @@ -268,13 +252,23 @@ namespace
{
std::array<float, BoneCountPerChunk> compute_bone_weights(glm::vec3 position, float radius)
{
const float t = (position.x / radius) * 0.5f + 0.5f; // FIXME
std::array<float, BoneCountPerChunk> weights = {1.f - t, t};
float t = (position.x / radius) * 0.5f + 0.5f;
float debug_sum = 0.f;

std::array<float, BoneCountPerChunk> 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
Expand All @@ -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<u32>(vertices.size());
Expand Down
33 changes: 8 additions & 25 deletions src/neptune/trackgen/Track.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,6 @@

#include <glm/glm.hpp>

namespace Reaper
{
namespace Math
{
struct Spline;
}
} // namespace Reaper

namespace Neptune
{
constexpr float DefaultRadiusMinMeter = 100.0f;
Expand All @@ -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;
Expand All @@ -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<Bone> bones;
std::vector<glm::fmat4x3> invBindTransforms;
std::vector<glm::fmat4x3> poseTransforms;
std::vector<glm::fmat4x3> bind_pose_inv_transforms;
std::vector<glm::fmat4x3> pose_transforms;
};

NEPTUNE_TRACKGEN_API
void generate_track_skeleton(const GenerationInfo& genInfo, std::span<TrackSkeletonNode> skeleton_nodes);

NEPTUNE_TRACKGEN_API
void generate_track_splines(std::span<const TrackSkeletonNode> skeleton_nodes, std::span<Reaper::Math::Spline> splines);

NEPTUNE_TRACKGEN_API
void generate_track_skinning(std::span<const TrackSkeletonNode> skeleton_nodes,
std::span<const Reaper::Math::Spline>
splines,
std::span<TrackSkinning>
skinning);
void generate_track_skinning(
std::span<const TrackSkeletonNode> skeleton_nodes, std::span<TrackSkinning> skinning_array);

NEPTUNE_TRACKGEN_API
void skin_track_chunk_mesh(const TrackSkeletonNode& node,
Expand Down
29 changes: 8 additions & 21 deletions src/neptune/trackgen/test/gen_simple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ using namespace Neptune;

TEST_CASE("Track generation")
{
std::vector<TrackSkeletonNode> skeletonNodes;
std::vector<Reaper::Math::Spline> splinesMS;
std::vector<TrackSkinning> skinning;
std::vector<TrackSkeletonNode> skeletonNodes;
std::vector<TrackSkinning> skinning;

GenerationInfo gen_info;

Expand All @@ -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);
}
}
}
Expand All @@ -56,19 +46,16 @@ TEST_CASE("Track generation")

TEST_CASE("Track mesh generation")
{
std::vector<TrackSkeletonNode> skeletonNodes;
std::vector<Reaper::Math::Spline> splinesMS;
std::vector<TrackSkinning> skinning;
std::vector<TrackSkeletonNode> skeletonNodes;
std::vector<TrackSkinning> 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");

Expand Down

0 comments on commit 9a38ff8

Please sign in to comment.