Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to pad skinned mesh buffers for the Skinning sample #635

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 50 additions & 20 deletions Gem/Code/Source/ProceduralSkinnedMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <ProceduralSkinnedMesh.h>

#include <AzCore/Math/MathUtils.h>
#include <Atom/RPI.Reflect/Model/ModelAssetHelpers.h>

const uint32_t maxInfluencesPerVertex = 4;

Expand All @@ -19,6 +20,11 @@ namespace AtomSampleViewer
m_verticesPerSegment = skinnedMeshConfig.m_verticesPerSegment;
m_segmentCount = static_cast<uint32_t>(skinnedMeshConfig.m_segmentCount);
m_vertexCount = m_segmentCount * m_verticesPerSegment;

m_alignedVertCountForRGBStream = aznumeric_cast<uint32_t>(RPI::ModelAssetHelpers::GetAlignedCount<float>(m_vertexCount, RHI::Format::R32G32B32_FLOAT, RPI::SkinnedMeshBufferAlignment));

m_alignedVertCountForRGBAStream = aznumeric_cast<uint32_t>(RPI::ModelAssetHelpers::GetAlignedCount<float>(m_vertexCount, RHI::Format::R32G32B32A32_FLOAT, RPI::SkinnedMeshBufferAlignment));

m_boneCount = AZ::GetMax(1u, static_cast<uint32_t>(skinnedMeshConfig.m_boneCount));
m_influencesPerVertex = AZ::GetMax(0u, AZ::GetMin(static_cast<uint32_t>(skinnedMeshConfig.m_influencesPerVertex), AZ::GetMin(m_boneCount, maxInfluencesPerVertex)));
m_subMeshCount = skinnedMeshConfig.m_subMeshCount;
Expand Down Expand Up @@ -130,13 +136,21 @@ namespace AtomSampleViewer
}
}

m_positions.resize(m_vertexCount);
m_normals.resize(m_vertexCount);
m_tangents.resize(m_vertexCount);
m_bitangents.resize(m_vertexCount);
// We pack 16 bit joint id's into 32 bit uints, so use half the number of joints for the uint count
m_blendIndices.resize(m_vertexCount * m_influencesPerVertex / 2);
m_blendWeights.resize(m_vertexCount * m_influencesPerVertex);
m_positions.resize(m_alignedVertCountForRGBStream);
m_normals.resize(m_alignedVertCountForRGBStream);
m_bitangents.resize(m_alignedVertCountForRGBStream);

size_t alignedTangentVertCount = RPI::ModelAssetHelpers::GetAlignedCount<float>(m_vertexCount, RPI::TangentFormat, RPI::SkinnedMeshBufferAlignment);
m_tangents.resize(alignedTangentVertCount);

// We pack 16 bit joint id's into 32 bit uints.
uint32_t numVertInfluences = m_vertexCount * m_influencesPerVertex;
size_t alignedIndicesVertCount = RPI::ModelAssetHelpers::GetAlignedCount<uint32_t>(numVertInfluences, RPI::SkinIndicesFormat, RPI::SkinnedMeshBufferAlignment);
m_blendIndices.resize(alignedIndicesVertCount);

size_t alignedWeightsVertCount = RPI::ModelAssetHelpers::GetAlignedCount<float>(numVertInfluences, RPI::SkinWeightFormat, RPI::SkinnedMeshBufferAlignment);
m_blendWeights.resize(alignedWeightsVertCount);

m_uvs.resize(m_vertexCount);

for (uint32_t vertexIndex = 0; vertexIndex < m_vertexCount; ++vertexIndex)
Expand All @@ -147,19 +161,19 @@ namespace AtomSampleViewer

// Get the x and y positions from a unit circle
float vertexAngle = (AZ::Constants::TwoPi / static_cast<float>(m_verticesPerSegment - 1)) * static_cast<float>(indexWithinTheCurrentSegment);
m_positions[vertexIndex][0] = cosf(vertexAngle) * m_radius;
m_positions[vertexIndex][1] = sinf(vertexAngle) * m_radius;
m_positions[vertexIndex][2] = m_segmentHeightOffsets[segmentIndex];
m_positions[(vertexIndex * RPI::PositionFloatsPerVert) + 0] = cosf(vertexAngle) * m_radius;
m_positions[(vertexIndex * RPI::PositionFloatsPerVert) + 1] = sinf(vertexAngle) * m_radius;
m_positions[(vertexIndex * RPI::PositionFloatsPerVert) + 2] = m_segmentHeightOffsets[segmentIndex];

// Normals are flat on the z-plane and point away from the origin in the direction of the vertex position
m_normals[vertexIndex][0] = m_positions[vertexIndex][0];
m_normals[vertexIndex][1] = m_positions[vertexIndex][1];
m_normals[vertexIndex][2] = 0.0f;
m_normals[(vertexIndex * RPI::PositionFloatsPerVert) + 0] = m_positions[(vertexIndex * RPI::PositionFloatsPerVert) + 0];
m_normals[(vertexIndex * RPI::PositionFloatsPerVert) + 1] = m_positions[(vertexIndex * RPI::PositionFloatsPerVert) + 1];
m_normals[(vertexIndex * RPI::PositionFloatsPerVert) + 2] = 0.0f;

// Bitangent is straight down
m_bitangents[vertexIndex][0] = 0.0f;
m_bitangents[vertexIndex][1] = 0.0f;
m_bitangents[vertexIndex][2] = -1.0f;
m_bitangents[(vertexIndex * RPI::PositionFloatsPerVert)+0] = 0.0f;
m_bitangents[(vertexIndex * RPI::PositionFloatsPerVert)+1] = 0.0f;
m_bitangents[(vertexIndex * RPI::PositionFloatsPerVert)+2] = -1.0f;

for (size_t i = 0; i < m_influencesPerVertex; ++i)
{
Expand Down Expand Up @@ -200,10 +214,11 @@ namespace AtomSampleViewer
uint32_t leftVertex = vertexIndex;
// The last vertex of the segment will have the first vertex of the segment as its neighbor, not just the next vertex (which would be in the next segment)
uint32_t rightVertex = (leftVertex + 1) % m_verticesPerSegment;
m_tangents[vertexIndex][0] = m_positions[leftVertex][0] - m_positions[rightVertex][0];
m_tangents[vertexIndex][1] = m_positions[leftVertex][1] - m_positions[rightVertex][1];
m_tangents[vertexIndex][2] = 0.0f;
m_tangents[vertexIndex][3] = 1.0f;
m_tangents[(vertexIndex * RPI::TangentFloatsPerVert)+0] = m_positions[(leftVertex * RPI::PositionFloatsPerVert) + 0] - m_positions[(rightVertex * RPI::PositionFloatsPerVert)+0];
m_tangents[(vertexIndex * RPI::TangentFloatsPerVert)+1] = m_positions[(leftVertex * RPI::PositionFloatsPerVert) + 1] - m_positions[(rightVertex * RPI::PositionFloatsPerVert)+1];

m_tangents[(vertexIndex * RPI::TangentFloatsPerVert)+2] = 0.0f;
m_tangents[(vertexIndex * RPI::TangentFloatsPerVert)+3] = 1.0f;
}
}

Expand Down Expand Up @@ -337,4 +352,19 @@ namespace AtomSampleViewer
m_segmentHeightOffsets[segmentIndex] = currentSegmentHeight - heightOffset;
}
}

uint32_t ProceduralSkinnedMesh::GetVertexCount() const
{
return m_vertexCount;
}

uint32_t ProceduralSkinnedMesh::GetAlignedVertCountForRGBStream() const
{
return m_alignedVertCountForRGBStream;
}

uint32_t ProceduralSkinnedMesh::GetAlignedVertCountForRGBAStream() const
{
return m_alignedVertCountForRGBAStream;
}
}
13 changes: 9 additions & 4 deletions Gem/Code/Source/ProceduralSkinnedMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,18 @@ namespace AtomSampleViewer
uint32_t GetSubMeshCount() const;
float GetSubMeshYOffset() const;

uint32_t GetVertexCount() const;
uint32_t GetAlignedVertCountForRGBStream() const;
uint32_t GetAlignedVertCountForRGBAStream() const;
static const uint32_t MaxInfluencesPerVertex = 4;

// Mesh data that's used for rendering
AZ::Aabb m_aabb = AZ::Aabb::CreateNull();
AZStd::vector<uint32_t> m_indices;
AZStd::vector< AZStd::array<float, 3>> m_positions;
AZStd::vector< AZStd::array<float, 3>> m_normals;
AZStd::vector< AZStd::array<float, 4>> m_tangents;
AZStd::vector< AZStd::array<float, 3>> m_bitangents;
AZStd::vector<float> m_positions;
AZStd::vector<float> m_normals;
AZStd::vector<float> m_tangents;
AZStd::vector<float> m_bitangents;
AZStd::vector<uint32_t> m_blendIndices;
AZStd::vector<float> m_blendWeights;
AZStd::vector<AZStd::array<float, 2>> m_uvs;
Expand All @@ -64,6 +67,8 @@ namespace AtomSampleViewer

uint32_t m_boneCount = 0;
uint32_t m_vertexCount = 0;
uint32_t m_alignedVertCountForRGBStream = 0;
uint32_t m_alignedVertCountForRGBAStream = 0;
uint32_t m_verticesPerSegment = 0;
uint32_t m_segmentCount = 0;
uint32_t m_influencesPerVertex = 0;
Expand Down
89 changes: 51 additions & 38 deletions Gem/Code/Source/ProceduralSkinnedMeshUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,51 +99,55 @@ namespace AtomSampleViewer
modelCreator.SetName(AZStd::string("ProceduralSkinnedMesh_" + assetId.m_guid.ToString<AZStd::string>()));

uint32_t submeshCount = proceduralMesh.GetSubMeshCount();
uint32_t verticesPerSubmesh = aznumeric_caster(proceduralMesh.m_positions.size());
uint32_t verticesPerSubmesh = proceduralMesh.GetVertexCount();
uint32_t totalVertices = verticesPerSubmesh * submeshCount;

uint32_t jointIdCountPerSubmesh = verticesPerSubmesh * proceduralMesh.GetInfluencesPerVertex();
uint32_t extraJointIdCount = AZ::RPI::CalculateJointIdPaddingCount(jointIdCountPerSubmesh);
uint32_t extraPackedIdCount = extraJointIdCount / 2;

// Copy the original buffer data n-times to create the data for extra sub-meshes
DuplicateVertices(proceduralMesh.m_indices, aznumeric_caster(proceduralMesh.m_indices.size()), submeshCount);
DuplicateVertices(proceduralMesh.m_positions, verticesPerSubmesh, submeshCount);
DuplicateVertices(proceduralMesh.m_normals, verticesPerSubmesh, submeshCount);
DuplicateVertices(proceduralMesh.m_tangents, verticesPerSubmesh, submeshCount);
DuplicateVertices(proceduralMesh.m_bitangents, verticesPerSubmesh, submeshCount);
DuplicateVertices(proceduralMesh.m_uvs, verticesPerSubmesh, submeshCount);
DuplicateVertices(proceduralMesh.m_blendWeights, verticesPerSubmesh * proceduralMesh.GetInfluencesPerVertex(), submeshCount);
DuplicateVertices(proceduralMesh.m_positions, proceduralMesh.GetAlignedVertCountForRGBStream(), submeshCount);
DuplicateVertices(proceduralMesh.m_normals, proceduralMesh.GetAlignedVertCountForRGBStream(), submeshCount);
DuplicateVertices(proceduralMesh.m_bitangents, proceduralMesh.GetAlignedVertCountForRGBStream(), submeshCount);

size_t alignedTangentVertCount = RPI::ModelAssetHelpers::GetAlignedCount<float>(verticesPerSubmesh, RPI::TangentFormat, RPI::SkinnedMeshBufferAlignment);
DuplicateVertices(proceduralMesh.m_tangents, aznumeric_cast<uint32_t>(alignedTangentVertCount), submeshCount);

// Insert the jointId padding first before duplicating
AZStd::vector<uint32_t> extraIds(extraPackedIdCount, 0);
DuplicateVertices(proceduralMesh.m_uvs, verticesPerSubmesh, submeshCount);

// Track the count of 32-byte 'elements' (packed) and offsets for creating sub-mesh views
uint32_t jointIdElementCountPerSubmesh = aznumeric_caster(proceduralMesh.m_blendIndices.size());
uint32_t jointIdOffsetElementsPerSubmesh = jointIdElementCountPerSubmesh + extraPackedIdCount;
uint32_t numBlendWeights = verticesPerSubmesh * proceduralMesh.GetInfluencesPerVertex();
size_t alignedWeightsVertCount = RPI::ModelAssetHelpers::GetAlignedCount<float>(numBlendWeights, RPI::SkinWeightFormat, RPI::SkinnedMeshBufferAlignment);
DuplicateVertices(proceduralMesh.m_blendWeights, aznumeric_cast<uint32_t>(alignedWeightsVertCount), submeshCount);

proceduralMesh.m_blendIndices.insert(proceduralMesh.m_blendIndices.end(), extraIds.begin(), extraIds.end());
DuplicateVertices(
proceduralMesh.m_blendIndices, aznumeric_caster(proceduralMesh.m_blendIndices.size()), submeshCount);
uint32_t numBlendIndices = verticesPerSubmesh * proceduralMesh.GetInfluencesPerVertex();
size_t alignedIndicesVertCount = RPI::ModelAssetHelpers::GetAlignedCount<uint32_t>(numBlendIndices, RPI::SkinIndicesFormat, RPI::SkinnedMeshBufferAlignment);
DuplicateVertices(proceduralMesh.m_blendIndices, aznumeric_cast<uint32_t>(alignedIndicesVertCount), submeshCount);

// Offset duplicate positions in the +y direction, so each sub-mesh ends up in a unique position
for (uint32_t subMeshIndex = 1; subMeshIndex < submeshCount; ++subMeshIndex)
{
for (uint32_t i = 0; i < verticesPerSubmesh; ++i)
for (uint32_t i = 0; i < proceduralMesh.GetAlignedVertCountForRGBStream(); i+=3)
{
proceduralMesh.m_positions[subMeshIndex*verticesPerSubmesh + i][1] +=
aznumeric_cast<float>(subMeshIndex) * proceduralMesh.GetSubMeshYOffset();
if ((proceduralMesh.GetAlignedVertCountForRGBStream() * subMeshIndex) + i + 1 < proceduralMesh.m_positions.size())
moudgils marked this conversation as resolved.
Show resolved Hide resolved
{
proceduralMesh.m_positions[(proceduralMesh.GetAlignedVertCountForRGBStream() * subMeshIndex) + i + 1] +=
aznumeric_cast<float>(subMeshIndex) * proceduralMesh.GetSubMeshYOffset();
}
}
}

size_t positionStreamSize = proceduralMesh.m_positions.size() / RHI::GetFormatComponentCount(RPI::PositionFormat);
size_t normalStreamSize = proceduralMesh.m_normals.size() / RHI::GetFormatComponentCount(RPI::NormalFormat);
size_t tangentStreamSize = proceduralMesh.m_tangents.size() / RHI::GetFormatComponentCount(RPI::TangentFormat);
size_t bitangentStreamSize = proceduralMesh.m_bitangents.size() / RHI::GetFormatComponentCount(RPI::BitangentFormat);

auto indexBuffer = CreateTypedBufferAsset(proceduralMesh.m_indices.data(), proceduralMesh.m_indices.size(), AZ::RHI::Format::R32_FLOAT);
auto positionBuffer = CreateTypedBufferAsset(proceduralMesh.m_positions.data(), proceduralMesh.m_positions.size(), AZ::RHI::Format::R32G32B32_FLOAT);
auto normalBuffer = CreateTypedBufferAsset(proceduralMesh.m_normals.data(), proceduralMesh.m_normals.size(), AZ::RHI::Format::R32G32B32_FLOAT);
auto tangentBuffer = CreateTypedBufferAsset(proceduralMesh.m_tangents.data(), proceduralMesh.m_tangents.size(), AZ::RHI::Format::R32G32B32A32_FLOAT);
auto bitangentBuffer = CreateTypedBufferAsset(proceduralMesh.m_bitangents.data(), proceduralMesh.m_bitangents.size(), AZ::RHI::Format::R32G32B32_FLOAT);
auto uvBuffer = CreateTypedBufferAsset(proceduralMesh.m_uvs.data(), proceduralMesh.m_uvs.size(), AZ::RHI::Format::R32G32_FLOAT);

auto positionBuffer = CreateTypedBufferAsset(proceduralMesh.m_positions.data(), positionStreamSize, RPI::PositionFormat);
auto normalBuffer = CreateTypedBufferAsset(proceduralMesh.m_normals.data(), normalStreamSize, RPI::NormalFormat);
auto tangentBuffer = CreateTypedBufferAsset(proceduralMesh.m_tangents.data(), tangentStreamSize, RPI::TangentFormat);
auto bitangentBuffer = CreateTypedBufferAsset(proceduralMesh.m_bitangents.data(), bitangentStreamSize, RPI::BitangentFormat);
auto uvBuffer = CreateTypedBufferAsset(proceduralMesh.m_uvs.data(), proceduralMesh.m_uvs.size(), RPI::UVFormat);
auto skinJointIdBuffer = CreateRawBufferAsset(proceduralMesh.m_blendIndices.data(), proceduralMesh.m_blendIndices.size(), sizeof(proceduralMesh.m_blendIndices[0]));
auto skinJointWeightBuffer = CreateTypedBufferAsset(proceduralMesh.m_blendWeights.data(), proceduralMesh.m_blendWeights.size(), AZ::RHI::Format::R32_FLOAT);
auto skinJointWeightBuffer = CreateTypedBufferAsset(proceduralMesh.m_blendWeights.data(), proceduralMesh.m_blendWeights.size(), RPI::SkinWeightFormat);

//
// Lod
Expand Down Expand Up @@ -183,24 +187,33 @@ namespace AtomSampleViewer
// Get the element count and offset for this sub-mesh
uint32_t elementCount = verticesPerSubmesh;
uint32_t elementOffset = verticesPerSubmesh * submeshIndex;
uint32_t alignedRGBElementOffset = (proceduralMesh.GetAlignedVertCountForRGBStream()/RHI::GetFormatComponentCount(RHI::Format::R32G32B32_FLOAT)) * submeshIndex;
uint32_t alignedRGBAElementOffset = (proceduralMesh.GetAlignedVertCountForRGBAStream()/RHI::GetFormatComponentCount(RHI::Format::R32G32B32A32_FLOAT)) * submeshIndex;

// Include any truncated vertices if this is the last mesh
if (submeshIndex == submeshCount - 1)
{
elementCount += totalVertices % verticesPerSubmesh;
}

modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "POSITION" }, AZ::Name(), AZ::RPI::BufferAssetView{ positionBuffer, CreateSubmeshBufferViewDescriptor(positionBuffer, elementCount, elementOffset) });
modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "NORMAL" }, AZ::Name(), AZ::RPI::BufferAssetView{ normalBuffer, CreateSubmeshBufferViewDescriptor(normalBuffer, elementCount, elementOffset) });
modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "TANGENT" }, AZ::Name(), AZ::RPI::BufferAssetView{ tangentBuffer, CreateSubmeshBufferViewDescriptor(tangentBuffer, elementCount, elementOffset) });
modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "BITANGENT" }, AZ::Name(), AZ::RPI::BufferAssetView{ bitangentBuffer, CreateSubmeshBufferViewDescriptor(bitangentBuffer, elementCount, elementOffset) });
modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "UV" }, AZ::Name(), AZ::RPI::BufferAssetView{ uvBuffer, CreateSubmeshBufferViewDescriptor(uvBuffer, elementCount, elementOffset) });
modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "SKIN_JOINTINDICES" }, AZ::Name(), AZ::RPI::BufferAssetView{ skinJointIdBuffer, CreateSubmeshBufferViewDescriptor(skinJointIdBuffer, jointIdElementCountPerSubmesh, jointIdOffsetElementsPerSubmesh * submeshIndex) });

uint32_t jointWeightElementCount = elementCount * proceduralMesh.GetInfluencesPerVertex();
uint32_t jointWeightOffset = elementOffset * proceduralMesh.GetInfluencesPerVertex();
modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "SKIN_WEIGHTS" }, AZ::Name(), AZ::RPI::BufferAssetView{ skinJointWeightBuffer, CreateSubmeshBufferViewDescriptor(skinJointWeightBuffer, jointWeightElementCount, jointWeightOffset) });
modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "POSITION" }, AZ::Name(), AZ::RPI::BufferAssetView{ positionBuffer, CreateSubmeshBufferViewDescriptor(positionBuffer, elementCount, alignedRGBElementOffset) });

modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "NORMAL" }, AZ::Name(), AZ::RPI::BufferAssetView{ normalBuffer, CreateSubmeshBufferViewDescriptor(normalBuffer, elementCount, alignedRGBElementOffset) });


modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "TANGENT" }, AZ::Name(), AZ::RPI::BufferAssetView{ tangentBuffer, CreateSubmeshBufferViewDescriptor(tangentBuffer, elementCount, alignedRGBAElementOffset) });

modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "BITANGENT" }, AZ::Name(), AZ::RPI::BufferAssetView{ bitangentBuffer, CreateSubmeshBufferViewDescriptor(bitangentBuffer, elementCount, alignedRGBElementOffset) });

modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "UV" }, AZ::Name(), AZ::RPI::BufferAssetView{ uvBuffer, CreateSubmeshBufferViewDescriptor(uvBuffer, elementCount, elementOffset) });

//Divide by 2 as we are storing 16bit data into 32bit integers
uint32_t numIndices = numBlendIndices/2;
modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "SKIN_JOINTINDICES" }, AZ::Name(), AZ::RPI::BufferAssetView{ skinJointIdBuffer, CreateSubmeshBufferViewDescriptor(skinJointIdBuffer, numIndices, aznumeric_cast<uint32_t>(alignedIndicesVertCount) * submeshIndex) });

uint32_t jointWeightOffset = aznumeric_cast<uint32_t>(alignedWeightsVertCount) * submeshIndex;
modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "SKIN_WEIGHTS" }, AZ::Name(), AZ::RPI::BufferAssetView{ skinJointWeightBuffer, CreateSubmeshBufferViewDescriptor(skinJointWeightBuffer, numBlendWeights, jointWeightOffset) });

AZ::Aabb localAabb = proceduralMesh.m_aabb;
modelLodCreator.SetMeshAabb(AZStd::move(localAabb));

Expand Down