Add support for GPU skinning (WIP)
This commit is contained in:
committed by
Jérôme Leclercq
parent
5d8ecd11df
commit
104f60f3e7
@@ -151,6 +151,16 @@ namespace Nz
|
||||
options.defaultValues
|
||||
});
|
||||
|
||||
FieldOffsets skeletalOffsets(StructLayout::Std140);
|
||||
skeletalOffsets.AddMatrixArray(StructFieldType::Float1, 4, 4, true, 100);
|
||||
|
||||
settings.sharedUniformBlocks.push_back({
|
||||
6,
|
||||
"SkeletalData",
|
||||
{},
|
||||
ShaderStageType::Vertex
|
||||
});
|
||||
|
||||
// Common data
|
||||
settings.textures.push_back({
|
||||
3,
|
||||
@@ -160,9 +170,11 @@ namespace Nz
|
||||
|
||||
settings.sharedUniformBlocks.push_back(PredefinedInstanceData::GetUniformBlock(4, nzsl::ShaderStageType::Vertex));
|
||||
settings.sharedUniformBlocks.push_back(PredefinedViewerData::GetUniformBlock(5, nzsl::ShaderStageType_All));
|
||||
//settings.sharedUniformBlocks.push_back(PredefinedInstanceData::GetUniformBlock(6, nzsl::ShaderStageType::Vertex));
|
||||
|
||||
settings.predefinedBindings[UnderlyingCast(PredefinedShaderBinding::InstanceDataUbo)] = 4;
|
||||
settings.predefinedBindings[UnderlyingCast(PredefinedShaderBinding::OverlayTexture)] = 3;
|
||||
//settings.predefinedBindings[UnderlyingCast(PredefinedShaderBinding::SkeletalDataUbo)] = 6;
|
||||
settings.predefinedBindings[UnderlyingCast(PredefinedShaderBinding::ViewerDataUbo)] = 5;
|
||||
|
||||
settings.shaders = options.shaders;
|
||||
@@ -194,6 +206,14 @@ namespace Nz
|
||||
config.optionValues[CRC32("UvLocation")] = locationIndex;
|
||||
break;
|
||||
|
||||
case VertexComponent::JointIndices:
|
||||
config.optionValues[CRC32("JointIndicesLocation")] = locationIndex;
|
||||
break;
|
||||
|
||||
case VertexComponent::JointWeights:
|
||||
config.optionValues[CRC32("JointWeightsLocation")] = locationIndex;
|
||||
break;
|
||||
|
||||
case VertexComponent::Unused:
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -13,8 +13,6 @@ namespace Nz
|
||||
{
|
||||
std::shared_ptr<GraphicalMesh> GraphicalMesh::BuildFromMesh(const Mesh& mesh)
|
||||
{
|
||||
assert(mesh.GetAnimationType() == AnimationType::Static);
|
||||
|
||||
const std::shared_ptr<RenderDevice>& renderDevice = Graphics::Instance()->GetRenderDevice();
|
||||
|
||||
std::shared_ptr<GraphicalMesh> gfxMesh = std::make_shared<GraphicalMesh>();
|
||||
|
||||
@@ -47,9 +47,11 @@ namespace Nz
|
||||
}
|
||||
|
||||
const auto& textureSettings = m_settings->GetTextures();
|
||||
const auto& sharedUboSettings = m_settings->GetSharedUniformBlocks();
|
||||
const auto& uboSettings = m_settings->GetUniformBlocks();
|
||||
|
||||
m_textures.resize(textureSettings.size());
|
||||
m_sharedUniformBuffers.resize(sharedUboSettings.size());
|
||||
|
||||
m_uniformBuffers.reserve(uboSettings.size());
|
||||
for (const auto& uniformBufferInfo : uboSettings)
|
||||
@@ -68,6 +70,7 @@ namespace Nz
|
||||
void MaterialPass::FillShaderBinding(std::vector<ShaderBinding::Binding>& bindings) const
|
||||
{
|
||||
const auto& textureSettings = m_settings->GetTextures();
|
||||
const auto& sharedUboSettings = m_settings->GetSharedUniformBlocks();
|
||||
const auto& uboSettings = m_settings->GetUniformBlocks();
|
||||
|
||||
// Textures
|
||||
@@ -98,7 +101,22 @@ namespace Nz
|
||||
});
|
||||
}
|
||||
|
||||
// Shared UBO (TODO)
|
||||
// Shared UBO
|
||||
for (std::size_t i = 0; i < m_sharedUniformBuffers.size(); ++i)
|
||||
{
|
||||
const auto& sharedUboSlot = m_sharedUniformBuffers[i];
|
||||
if (!sharedUboSlot.bufferView)
|
||||
continue;
|
||||
|
||||
const auto& sharedUboSetting = sharedUboSettings[i];
|
||||
|
||||
bindings.push_back({
|
||||
sharedUboSetting.bindingIndex,
|
||||
ShaderBinding::UniformBufferBinding {
|
||||
sharedUboSlot.bufferView.GetBuffer(), sharedUboSlot.bufferView.GetOffset(), sharedUboSlot.bufferView.GetSize()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Owned UBO
|
||||
for (std::size_t i = 0; i < m_uniformBuffers.size(); ++i)
|
||||
|
||||
@@ -186,7 +186,7 @@ namespace Nz
|
||||
options.phongTextureIndexes->emissive = settings.textures.size();
|
||||
|
||||
settings.textures.push_back({
|
||||
7,
|
||||
8,
|
||||
"Emissive",
|
||||
ImageType::E2D
|
||||
});
|
||||
@@ -195,7 +195,7 @@ namespace Nz
|
||||
options.phongTextureIndexes->height = settings.textures.size();
|
||||
|
||||
settings.textures.push_back({
|
||||
8,
|
||||
9,
|
||||
"Height",
|
||||
ImageType::E2D
|
||||
});
|
||||
@@ -204,7 +204,7 @@ namespace Nz
|
||||
options.phongTextureIndexes->normal = settings.textures.size();
|
||||
|
||||
settings.textures.push_back({
|
||||
9,
|
||||
10,
|
||||
"Normal",
|
||||
ImageType::E2D
|
||||
});
|
||||
@@ -213,7 +213,7 @@ namespace Nz
|
||||
options.phongTextureIndexes->specular = settings.textures.size();
|
||||
|
||||
settings.textures.push_back({
|
||||
10,
|
||||
11,
|
||||
"Specular",
|
||||
ImageType::E2D
|
||||
});
|
||||
@@ -229,8 +229,8 @@ namespace Nz
|
||||
options.defaultValues
|
||||
});
|
||||
|
||||
settings.sharedUniformBlocks.push_back(PredefinedLightData::GetUniformBlock(6, nzsl::ShaderStageType::Fragment));
|
||||
settings.predefinedBindings[UnderlyingCast(PredefinedShaderBinding::LightDataUbo)] = 6;
|
||||
settings.sharedUniformBlocks.push_back(PredefinedLightData::GetUniformBlock(7, nzsl::ShaderStageType::Fragment));
|
||||
settings.predefinedBindings[UnderlyingCast(PredefinedShaderBinding::LightDataUbo)] = 7;
|
||||
|
||||
settings.shaders = options.shaders;
|
||||
|
||||
|
||||
@@ -19,9 +19,13 @@ option ColorLocation: i32 = -1;
|
||||
option PosLocation: i32;
|
||||
option UvLocation: i32 = -1;
|
||||
|
||||
option JointIndicesLocation: i32 = -1;
|
||||
option JointWeightsLocation: i32 = -1;
|
||||
|
||||
const HasVertexColor = (ColorLocation >= 0);
|
||||
const HasColor = (HasVertexColor || Billboard);
|
||||
const HasUV = (UvLocation >= 0);
|
||||
const HasSkinning = (JointIndicesLocation >= 0 && JointWeightsLocation >= 0);
|
||||
|
||||
[layout(std140)]
|
||||
struct MaterialSettings
|
||||
@@ -30,6 +34,14 @@ struct MaterialSettings
|
||||
BaseColor: vec4[f32]
|
||||
}
|
||||
|
||||
const MaxJointCount: u32 = u32(200); //< FIXME: Fix integral value types
|
||||
|
||||
[layout(std140)]
|
||||
struct SkeletalData
|
||||
{
|
||||
JointMatrices: array[mat4[f32], MaxJointCount]
|
||||
}
|
||||
|
||||
external
|
||||
{
|
||||
[binding(0)] settings: uniform[MaterialSettings],
|
||||
@@ -38,6 +50,7 @@ external
|
||||
[binding(3)] TextureOverlay: sampler2D[f32],
|
||||
[binding(4)] instanceData: uniform[InstanceData],
|
||||
[binding(5)] viewerData: uniform[ViewerData],
|
||||
[binding(6)] skeletalData: uniform[SkeletalData]
|
||||
}
|
||||
|
||||
// Fragment stage
|
||||
@@ -92,6 +105,12 @@ struct VertIn
|
||||
[cond(HasUV), location(UvLocation)]
|
||||
uv: vec2[f32],
|
||||
|
||||
[cond(HasSkinning), location(JointIndicesLocation)]
|
||||
jointIndices: vec4[i32],
|
||||
|
||||
[cond(HasSkinning), location(JointWeightsLocation)]
|
||||
jointWeights: vec4[f32],
|
||||
|
||||
[cond(Billboard), location(BillboardCenterLocation)]
|
||||
billboardCenter: vec3[f32],
|
||||
|
||||
@@ -143,8 +162,26 @@ fn billboardMain(input: VertIn) -> VertOut
|
||||
[entry(vert), cond(!Billboard)]
|
||||
fn main(input: VertIn) -> VertOut
|
||||
{
|
||||
let pos: vec3[f32];
|
||||
const if (HasSkinning)
|
||||
{
|
||||
pos = vec3[f32](0.0, 0.0, 0.0);
|
||||
|
||||
[unroll]
|
||||
for i in 0 -> 4
|
||||
{
|
||||
let jointIndex = input.jointIndices[i];
|
||||
let jointWeight = input.jointWeights[i];
|
||||
|
||||
let jointMatrix = skeletalData.JointMatrices[jointIndex];
|
||||
pos += (jointMatrix * vec4[f32](input.pos, 1.0)).xyz * jointWeight;
|
||||
}
|
||||
}
|
||||
else
|
||||
pos = input.pos;
|
||||
|
||||
let output: VertOut;
|
||||
output.position = viewerData.viewProjMatrix * instanceData.worldMatrix * vec4[f32](input.pos, 1.0);
|
||||
output.position = viewerData.viewProjMatrix * instanceData.worldMatrix * vec4[f32](pos, 1.0);
|
||||
|
||||
const if (HasColor)
|
||||
output.color = input.color;
|
||||
|
||||
@@ -62,11 +62,11 @@ external
|
||||
[binding(3)] TextureOverlay: sampler2D[f32],
|
||||
[binding(4)] instanceData: uniform[InstanceData],
|
||||
[binding(5)] viewerData: uniform[ViewerData],
|
||||
[binding(6)] lightData: uniform[LightData],
|
||||
[binding(7)] MaterialEmissiveMap: sampler2D[f32],
|
||||
[binding(8)] MaterialHeightMap: sampler2D[f32],
|
||||
[binding(9)] MaterialNormalMap: sampler2D[f32],
|
||||
[binding(10)] MaterialSpecularMap: sampler2D[f32],
|
||||
[binding(7)] lightData: uniform[LightData],
|
||||
[binding(8)] MaterialEmissiveMap: sampler2D[f32],
|
||||
[binding(9)] MaterialHeightMap: sampler2D[f32],
|
||||
[binding(10)] MaterialNormalMap: sampler2D[f32],
|
||||
[binding(11)] MaterialSpecularMap: sampler2D[f32],
|
||||
}
|
||||
|
||||
struct VertToFrag
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace Nz
|
||||
const WorldInstance* currentWorldInstance = nullptr;
|
||||
Recti currentScissorBox = invalidScissorBox;
|
||||
RenderBufferView currentLightData;
|
||||
RenderBufferView currentSkeletalData;
|
||||
|
||||
auto FlushDrawCall = [&]()
|
||||
{
|
||||
@@ -95,6 +96,12 @@ namespace Nz
|
||||
currentLightData = renderState.lightData;
|
||||
}
|
||||
|
||||
if (currentSkeletalData != renderState.skeletalData)
|
||||
{
|
||||
FlushDrawData();
|
||||
currentSkeletalData = renderState.skeletalData;
|
||||
}
|
||||
|
||||
const Recti& scissorBox = submesh.GetScissorBox();
|
||||
const Recti& targetScissorBox = (scissorBox.width >= 0) ? scissorBox : invalidScissorBox;
|
||||
if (currentScissorBox != targetScissorBox)
|
||||
@@ -135,6 +142,16 @@ namespace Nz
|
||||
};
|
||||
}
|
||||
|
||||
if (std::size_t bindingIndex = matSettings->GetPredefinedBinding(PredefinedShaderBinding::SkeletalDataUbo); bindingIndex != MaterialSettings::InvalidIndex && currentSkeletalData)
|
||||
{
|
||||
auto& bindingEntry = m_bindingCache.emplace_back();
|
||||
bindingEntry.bindingIndex = bindingIndex;
|
||||
bindingEntry.content = ShaderBinding::UniformBufferBinding{
|
||||
currentSkeletalData.GetBuffer(),
|
||||
currentSkeletalData.GetOffset(), currentSkeletalData.GetSize()
|
||||
};
|
||||
}
|
||||
|
||||
if (std::size_t bindingIndex = matSettings->GetPredefinedBinding(PredefinedShaderBinding::ViewerDataUbo); bindingIndex != MaterialSettings::InvalidIndex)
|
||||
{
|
||||
const auto& viewerBuffer = viewerInstance.GetViewerBuffer();
|
||||
|
||||
@@ -1067,33 +1067,43 @@ namespace Nz
|
||||
|
||||
void SkinPosition(const SkinningData& skinningInfos, UInt64 startVertex, UInt64 vertexCount)
|
||||
{
|
||||
const SkeletalMeshVertex* inputVertex = &skinningInfos.inputVertex[startVertex];
|
||||
MeshVertex* outputVertex = &skinningInfos.outputVertex[startVertex];
|
||||
NazaraAssert(skinningInfos.inputJointIndices, "missing input joint indices");
|
||||
NazaraAssert(skinningInfos.inputJointWeights, "missing input joint weights");
|
||||
|
||||
UInt64 endVertex = startVertex + vertexCount - 1;
|
||||
for (UInt64 i = startVertex; i <= endVertex; ++i)
|
||||
if (skinningInfos.outputPositions)
|
||||
{
|
||||
Vector3f finalPosition(Vector3f::Zero());
|
||||
NazaraAssert(skinningInfos.inputPositions, "missing input positions");
|
||||
NazaraAssert(skinningInfos.joints, "missing skeleton joints");
|
||||
|
||||
for (Int32 j = 0; j < inputVertex->weightCount; ++j)
|
||||
for (UInt64 i = startVertex; i <= endVertex; ++i)
|
||||
{
|
||||
Matrix4f mat(skinningInfos.joints[inputVertex->jointIndexes[j]].GetSkinningMatrix());
|
||||
mat *= inputVertex->weights[j];
|
||||
Vector3f finalPosition(Vector3f::Zero());
|
||||
|
||||
finalPosition += mat.Transform(inputVertex->position);
|
||||
for (Int32 j = 0; j < 4; ++j)
|
||||
{
|
||||
Matrix4f mat(skinningInfos.joints[skinningInfos.inputJointIndices[i][j]].GetSkinningMatrix());
|
||||
mat *= skinningInfos.inputJointWeights[i][j];
|
||||
|
||||
finalPosition += mat.Transform(skinningInfos.inputPositions[i]);
|
||||
}
|
||||
|
||||
skinningInfos.outputPositions[i] = finalPosition;
|
||||
}
|
||||
}
|
||||
|
||||
outputVertex->position = finalPosition;
|
||||
outputVertex->uv = inputVertex->uv;
|
||||
if (skinningInfos.outputUv)
|
||||
{
|
||||
NazaraAssert(skinningInfos.inputUv, "missing input uv");
|
||||
|
||||
inputVertex++;
|
||||
outputVertex++;
|
||||
for (UInt64 i = startVertex; i <= endVertex; ++i)
|
||||
skinningInfos.outputUv[i] = skinningInfos.inputUv[i];
|
||||
}
|
||||
}
|
||||
|
||||
void SkinPositionNormal(const SkinningData& skinningInfos, UInt64 startVertex, UInt64 vertexCount)
|
||||
{
|
||||
const SkeletalMeshVertex* inputVertex = &skinningInfos.inputVertex[startVertex];
|
||||
/*const SkeletalMeshVertex* inputVertex = &skinningInfos.inputVertex[startVertex];
|
||||
MeshVertex* outputVertex = &skinningInfos.outputVertex[startVertex];
|
||||
|
||||
UInt64 endVertex = startVertex + vertexCount - 1;
|
||||
@@ -1102,7 +1112,7 @@ namespace Nz
|
||||
Vector3f finalPosition(Vector3f::Zero());
|
||||
Vector3f finalNormal(Vector3f::Zero());
|
||||
|
||||
for (Int32 j = 0; j < inputVertex->weightCount; ++j)
|
||||
for (Int32 j = 0; j < 4; ++j)
|
||||
{
|
||||
Matrix4f mat(skinningInfos.joints[inputVertex->jointIndexes[j]].GetSkinningMatrix());
|
||||
mat *= inputVertex->weights[j];
|
||||
@@ -1119,12 +1129,12 @@ namespace Nz
|
||||
|
||||
inputVertex++;
|
||||
outputVertex++;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
void SkinPositionNormalTangent(const SkinningData& skinningInfos, UInt64 startVertex, UInt64 vertexCount)
|
||||
{
|
||||
const SkeletalMeshVertex* inputVertex = &skinningInfos.inputVertex[startVertex];
|
||||
/*const SkeletalMeshVertex* inputVertex = &skinningInfos.inputVertex[startVertex];
|
||||
MeshVertex* outputVertex = &skinningInfos.outputVertex[startVertex];
|
||||
|
||||
UInt64 endVertex = startVertex + vertexCount - 1;
|
||||
@@ -1134,7 +1144,7 @@ namespace Nz
|
||||
Vector3f finalNormal(Vector3f::Zero());
|
||||
Vector3f finalTangent(Vector3f::Zero());
|
||||
|
||||
for (int j = 0; j < inputVertex->weightCount; ++j)
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
Matrix4f mat(skinningInfos.joints[inputVertex->jointIndexes[j]].GetSkinningMatrix());
|
||||
mat *= inputVertex->weights[j];
|
||||
@@ -1154,6 +1164,6 @@ namespace Nz
|
||||
|
||||
inputVertex++;
|
||||
outputVertex++;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,22 +62,27 @@ namespace Nz
|
||||
Quaternionf rotationQuat = Quaternionf::RotationBetween(Vector3f::UnitX(), Vector3f::Forward()) *
|
||||
Quaternionf::RotationBetween(Vector3f::UnitZ(), Vector3f::Up());
|
||||
|
||||
for (UInt32 i = 0; i < jointCount; ++i)
|
||||
{
|
||||
int parent = joints[i].parent;
|
||||
for (UInt32 j = 0; j < frameCount; ++j)
|
||||
{
|
||||
SequenceJoint& sequenceJoint = sequenceJoints[j*jointCount + i];
|
||||
//Matrix4f matrix = Matrix4f::Transform(Nz::Vector3f::Zero(), rotationQuat, Vector3f(1.f / 40.f));
|
||||
//matrix *= parameters.matrix;
|
||||
|
||||
if (parent >= 0)
|
||||
rotationQuat = Quaternionf::Identity();
|
||||
|
||||
for (UInt32 frameIndex = 0; frameIndex < frameCount; ++frameIndex)
|
||||
{
|
||||
for (UInt32 jointIndex = 0; jointIndex < jointCount; ++jointIndex)
|
||||
{
|
||||
SequenceJoint& sequenceJoint = sequenceJoints[frameIndex * jointCount + jointIndex];
|
||||
|
||||
Int32 parentId = joints[jointIndex].parent;
|
||||
if (parentId >= 0)
|
||||
{
|
||||
sequenceJoint.position = frames[j].joints[i].pos;
|
||||
sequenceJoint.rotation = frames[j].joints[i].orient;
|
||||
sequenceJoint.position = frames[frameIndex].joints[jointIndex].pos;
|
||||
sequenceJoint.rotation = frames[frameIndex].joints[jointIndex].orient;
|
||||
}
|
||||
else
|
||||
{
|
||||
sequenceJoint.position = rotationQuat * frames[j].joints[i].pos;
|
||||
sequenceJoint.rotation = rotationQuat * frames[j].joints[i].orient;
|
||||
sequenceJoint.position = rotationQuat * frames[frameIndex].joints[jointIndex].pos;
|
||||
sequenceJoint.rotation = rotationQuat * frames[frameIndex].joints[jointIndex].orient;
|
||||
}
|
||||
|
||||
sequenceJoint.scale.Set(1.f);
|
||||
|
||||
@@ -44,6 +44,19 @@ namespace Nz
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UInt32 maxWeightCount = 4;
|
||||
long long customMaxWeightCount;
|
||||
if (parameters.custom.GetIntegerParameter("MaxWeightCount", &customMaxWeightCount))
|
||||
{
|
||||
maxWeightCount = SafeCast<UInt32>(customMaxWeightCount);
|
||||
if (maxWeightCount > 4)
|
||||
{
|
||||
NazaraWarning("MaxWeightCount cannot be over 4");
|
||||
maxWeightCount = 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Pour que le squelette soit correctement aligné, il faut appliquer un quaternion "de correction" aux joints à la base du squelette
|
||||
Quaternionf rotationQuat = Quaternionf::RotationBetween(Vector3f::UnitX(), Vector3f::Forward()) *
|
||||
Quaternionf::RotationBetween(Vector3f::UnitZ(), Vector3f::Up());
|
||||
@@ -55,6 +68,8 @@ namespace Nz
|
||||
Matrix4f matrix = Matrix4f::Transform(Nz::Vector3f::Zero(), rotationQuat, Vector3f(1.f / 40.f));
|
||||
matrix *= parameters.matrix;
|
||||
|
||||
rotationQuat = Quaternionf::Identity();
|
||||
|
||||
const MD5MeshParser::Joint* joints = parser.GetJoints();
|
||||
const MD5MeshParser::Mesh* meshes = parser.GetMeshes();
|
||||
UInt32 jointCount = parser.GetJointCount();
|
||||
@@ -120,8 +135,12 @@ namespace Nz
|
||||
|
||||
std::vector<Weight> tempWeights;
|
||||
|
||||
BufferMapper<VertexBuffer> vertexMapper(*vertexBuffer, 0, vertexBuffer->GetVertexCount());
|
||||
SkeletalMeshVertex* vertices = static_cast<SkeletalMeshVertex*>(vertexMapper.GetPointer());
|
||||
VertexMapper vertexMapper(*vertexBuffer);
|
||||
|
||||
auto posPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent::Position);
|
||||
auto jointIndicesPtr = vertexMapper.GetComponentPtr<Vector4i32>(VertexComponent::JointIndices);
|
||||
auto jointWeightPtr = vertexMapper.GetComponentPtr<Vector4f>(VertexComponent::JointWeights);
|
||||
auto uvPtr = vertexMapper.GetComponentPtr<Vector2f>(VertexComponent::TexCoord);
|
||||
|
||||
for (const MD5MeshParser::Vertex& vertex : md5Mesh.vertices)
|
||||
{
|
||||
@@ -130,21 +149,21 @@ namespace Nz
|
||||
|
||||
// On stocke tous les poids dans le tableau temporaire en même temps qu'on calcule la position finale du sommet.
|
||||
tempWeights.resize(vertex.weightCount);
|
||||
for (unsigned int j = 0; j < vertex.weightCount; ++j)
|
||||
for (unsigned int weightIndex = 0; weightIndex < vertex.weightCount; ++weightIndex)
|
||||
{
|
||||
const MD5MeshParser::Weight& weight = md5Mesh.weights[vertex.startWeight + j];
|
||||
const MD5MeshParser::Weight& weight = md5Mesh.weights[vertex.startWeight + weightIndex];
|
||||
const MD5MeshParser::Joint& joint = joints[weight.joint];
|
||||
|
||||
finalPos += (joint.bindPos + joint.bindOrient*weight.pos) * weight.bias;
|
||||
finalPos += (joint.bindPos + joint.bindOrient * weight.pos) * weight.bias;
|
||||
|
||||
// Avant d'ajouter les poids, il faut s'assurer qu'il n'y en ait pas plus que le maximum supporté
|
||||
// et dans le cas contraire, garder les poids les plus importants et les renormaliser
|
||||
tempWeights[j] = {weight.bias, weight.joint};
|
||||
tempWeights[weightIndex] = {weight.bias, weight.joint};
|
||||
}
|
||||
|
||||
// Avons nous plus de poids que le moteur ne peut en supporter ?
|
||||
unsigned int weightCount = vertex.weightCount;
|
||||
if (weightCount > NAZARA_UTILITY_SKINNING_MAX_WEIGHTS)
|
||||
UInt32 weightCount = vertex.weightCount;
|
||||
if (weightCount > maxWeightCount)
|
||||
{
|
||||
// Pour augmenter la qualité du skinning tout en ne gardant que X poids, on ne garde que les poids
|
||||
// les plus importants, ayant le plus d'impact sur le sommet final
|
||||
@@ -154,36 +173,43 @@ namespace Nz
|
||||
|
||||
// Sans oublier bien sûr de renormaliser les poids (que leur somme soit 1)
|
||||
float weightSum = 0.f;
|
||||
for (unsigned int j = 0; j < NAZARA_UTILITY_SKINNING_MAX_WEIGHTS; ++j)
|
||||
for (UInt32 j = 0; j < maxWeightCount; ++j)
|
||||
weightSum += tempWeights[j].bias;
|
||||
|
||||
for (unsigned int j = 0; j < NAZARA_UTILITY_SKINNING_MAX_WEIGHTS; ++j)
|
||||
for (UInt32 j = 0; j < maxWeightCount; ++j)
|
||||
tempWeights[j].bias /= weightSum;
|
||||
|
||||
weightCount = NAZARA_UTILITY_SKINNING_MAX_WEIGHTS;
|
||||
weightCount = maxWeightCount;
|
||||
}
|
||||
|
||||
vertices->weightCount = weightCount;
|
||||
for (unsigned int j = 0; j < NAZARA_UTILITY_SKINNING_MAX_WEIGHTS; ++j)
|
||||
if (posPtr)
|
||||
*posPtr++ = /*finalPos * */Vector3f(1.f / 40.f);
|
||||
|
||||
if (uvPtr)
|
||||
*uvPtr++ = Vector2f(parameters.texCoordOffset + vertex.uv * parameters.texCoordScale);
|
||||
|
||||
if (jointIndicesPtr)
|
||||
{
|
||||
if (j < weightCount)
|
||||
{
|
||||
// On donne une valeur aux poids présents
|
||||
vertices->weights[j] = tempWeights[j].bias;
|
||||
vertices->jointIndexes[j] = tempWeights[j].jointIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Et un poids de 0 sur le joint 0 pour les autres (nécessaire pour le GPU Skinning)
|
||||
// La raison est que le GPU ne tiendra pas compte du nombre de poids pour des raisons de performances.
|
||||
vertices->weights[j] = 0.f;
|
||||
vertices->jointIndexes[j] = 0;
|
||||
}
|
||||
Vector4i32& jointIndices = *jointIndicesPtr++;
|
||||
|
||||
for (UInt32 j = 0; j < maxWeightCount; ++j)
|
||||
jointIndices[j] = (j < weightCount) ? tempWeights[j].jointIndex : 0;
|
||||
}
|
||||
|
||||
vertices->position = finalPos;
|
||||
vertices->uv.Set(parameters.texCoordOffset + vertex.uv * parameters.texCoordScale);
|
||||
vertices++;
|
||||
if (jointWeightPtr)
|
||||
{
|
||||
Vector4f& jointWeights = *jointWeightPtr++;
|
||||
|
||||
for (UInt32 j = 0; j < maxWeightCount; ++j)
|
||||
jointWeights[j] = (j < weightCount) ? tempWeights[j].bias : 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Vertex colors (.md5mesh files have no vertex color)
|
||||
if (auto colorPtr = vertexMapper.GetComponentPtr<Color>(VertexComponent::Color))
|
||||
{
|
||||
for (std::size_t j = 0; j < md5Mesh.vertices.size(); ++j)
|
||||
*colorPtr++ = Color::White;
|
||||
}
|
||||
|
||||
vertexMapper.Unmap();
|
||||
@@ -196,7 +222,15 @@ namespace Nz
|
||||
|
||||
// Submesh
|
||||
std::shared_ptr<SkeletalMesh> subMesh = std::make_shared<SkeletalMesh>(vertexBuffer, indexBuffer);
|
||||
subMesh->GenerateNormalsAndTangents();
|
||||
|
||||
if (parameters.vertexDeclaration->HasComponentOfType<Vector3f>(VertexComponent::Normal))
|
||||
{
|
||||
if (parameters.vertexDeclaration->HasComponentOfType<Vector3f>(VertexComponent::Tangent))
|
||||
subMesh->GenerateNormalsAndTangents();
|
||||
else
|
||||
subMesh->GenerateNormals();
|
||||
}
|
||||
|
||||
subMesh->SetMaterialIndex(i);
|
||||
|
||||
mesh->AddSubMesh(subMesh);
|
||||
|
||||
@@ -280,19 +280,14 @@ namespace Nz
|
||||
0
|
||||
},
|
||||
{
|
||||
VertexComponent::Userdata,
|
||||
ComponentType::Int1,
|
||||
0 // Weight count
|
||||
},
|
||||
{
|
||||
VertexComponent::Userdata,
|
||||
VertexComponent::JointWeights,
|
||||
ComponentType::Float4,
|
||||
1 // Weights
|
||||
0
|
||||
},
|
||||
{
|
||||
VertexComponent::Userdata,
|
||||
VertexComponent::JointIndices,
|
||||
ComponentType::Int4,
|
||||
2 // Joint indexes
|
||||
0
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user