Add support for GPU skinning (WIP)
This commit is contained in:
parent
5d8ecd11df
commit
104f60f3e7
|
|
@ -0,0 +1,343 @@
|
||||||
|
#include <Nazara/Core.hpp>
|
||||||
|
#include <Nazara/Platform.hpp>
|
||||||
|
#include <Nazara/Graphics.hpp>
|
||||||
|
#include <Nazara/Graphics/TextSprite.hpp>
|
||||||
|
#include <Nazara/Graphics/Components.hpp>
|
||||||
|
#include <Nazara/Graphics/Systems.hpp>
|
||||||
|
#include <Nazara/Math/PidController.hpp>
|
||||||
|
#include <Nazara/Physics3D.hpp>
|
||||||
|
#include <Nazara/Physics3D/Components.hpp>
|
||||||
|
#include <Nazara/Physics3D/Systems.hpp>
|
||||||
|
#include <Nazara/Renderer.hpp>
|
||||||
|
#include <Nazara/Shader.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvConstantCache.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvPrinter.hpp>
|
||||||
|
#include <Nazara/Utility.hpp>
|
||||||
|
#include <Nazara/Utility/Components.hpp>
|
||||||
|
#include <entt/entt.hpp>
|
||||||
|
#include <array>
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
NAZARA_REQUEST_DEDICATED_GPU()
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
std::filesystem::path resourceDir = "resources";
|
||||||
|
if (!std::filesystem::is_directory(resourceDir) && std::filesystem::is_directory(".." / resourceDir))
|
||||||
|
resourceDir = ".." / resourceDir;
|
||||||
|
|
||||||
|
Nz::Renderer::Config rendererConfig;
|
||||||
|
//rendererConfig.preferredAPI = Nz::RenderAPI::OpenGL;
|
||||||
|
|
||||||
|
Nz::Modules<Nz::Graphics> nazara(rendererConfig);
|
||||||
|
|
||||||
|
Nz::PluginManager::Mount(Nz::Plugin::Assimp);
|
||||||
|
|
||||||
|
std::shared_ptr<Nz::RenderDevice> device = Nz::Graphics::Instance()->GetRenderDevice();
|
||||||
|
|
||||||
|
std::string windowTitle = "Skinning test";
|
||||||
|
Nz::RenderWindow window;
|
||||||
|
if (!window.Create(device, Nz::VideoMode(1280, 720, 32), windowTitle))
|
||||||
|
{
|
||||||
|
std::cout << "Failed to create Window" << std::endl;
|
||||||
|
return __LINE__;
|
||||||
|
}
|
||||||
|
|
||||||
|
entt::registry registry;
|
||||||
|
|
||||||
|
Nz::Physics3DSystem physSytem(registry);
|
||||||
|
Nz::RenderSystem renderSystem(registry);
|
||||||
|
|
||||||
|
physSytem.GetPhysWorld().SetGravity({ 0.f, -9.81f, 0.f });
|
||||||
|
|
||||||
|
Nz::TextureParams texParams;
|
||||||
|
texParams.renderDevice = device;
|
||||||
|
|
||||||
|
entt::entity playerEntity = registry.create();
|
||||||
|
{
|
||||||
|
auto& playerNode = registry.emplace<Nz::NodeComponent>(playerEntity);
|
||||||
|
playerNode.SetPosition(0.f, 1.8f, 1.f);
|
||||||
|
playerNode.SetRotation(Nz::EulerAnglesf(-30.f, 0.f, 0.f));
|
||||||
|
|
||||||
|
auto& playerBody = registry.emplace<Nz::RigidBody3DComponent>(playerEntity, &physSytem.GetPhysWorld());
|
||||||
|
playerBody.SetMass(0.f);
|
||||||
|
playerBody.SetGeom(std::make_shared<Nz::CapsuleCollider3D>(1.8f, 0.5f));
|
||||||
|
|
||||||
|
auto& playerComponent = registry.emplace<Nz::CameraComponent>(playerEntity, window.GetRenderTarget());
|
||||||
|
playerComponent.UpdateZNear(0.2f);
|
||||||
|
playerComponent.UpdateRenderMask(1);
|
||||||
|
playerComponent.UpdateClearColor(Nz::Color(127, 127, 127));
|
||||||
|
}
|
||||||
|
|
||||||
|
Nz::FieldOffsets skeletalOffsets(Nz::StructLayout::Std140);
|
||||||
|
std::size_t arrayOffset = skeletalOffsets.AddMatrixArray(Nz::StructFieldType::Float1, 4, 4, true, 200);
|
||||||
|
|
||||||
|
std::vector<Nz::UInt8> skeletalBufferMem(skeletalOffsets.GetAlignedSize());
|
||||||
|
Nz::Matrix4f* matrices = Nz::AccessByOffset<Nz::Matrix4f*>(skeletalBufferMem.data(), arrayOffset);
|
||||||
|
|
||||||
|
std::shared_ptr<Nz::Animation> bobAnim = Nz::Animation::LoadFromFile(resourceDir / "hellknight/idle2.md5anim");
|
||||||
|
if (!bobAnim)
|
||||||
|
{
|
||||||
|
NazaraError("Failed to load bob anim");
|
||||||
|
return __LINE__;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nz::MeshParams meshParams;
|
||||||
|
meshParams.animated = true;
|
||||||
|
//meshParams.center = true;
|
||||||
|
//meshParams.matrix = Nz::Matrix4f::Scale(Nz::Vector3f(10.f));
|
||||||
|
meshParams.vertexDeclaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ_Normal_UV_Tangent_Skinning);
|
||||||
|
|
||||||
|
std::shared_ptr<Nz::Mesh> bobMesh = Nz::Mesh::LoadFromFile(resourceDir / "hellknight/hellknight.md5mesh", meshParams);
|
||||||
|
if (!bobMesh)
|
||||||
|
{
|
||||||
|
NazaraError("Failed to load bob mesh");
|
||||||
|
return __LINE__;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nz::Skeleton skeleton = *bobMesh->GetSkeleton();
|
||||||
|
|
||||||
|
bobAnim->AnimateSkeleton(&skeleton, 0, 1, 0.5f);
|
||||||
|
|
||||||
|
/*for (std::size_t i = 0; i < bobMesh->GetSubMeshCount(); ++i)
|
||||||
|
{
|
||||||
|
Nz::VertexMapper mapper(*bobMesh->GetSubMesh(i));
|
||||||
|
|
||||||
|
Nz::SkinningData skinningData;
|
||||||
|
skinningData.joints = skeleton.GetJoints();
|
||||||
|
skinningData.inputJointIndices = mapper.GetComponentPtr<Nz::Vector4i32>(Nz::VertexComponent::JointIndices);
|
||||||
|
skinningData.inputJointWeights = mapper.GetComponentPtr<Nz::Vector4f>(Nz::VertexComponent::JointWeights);
|
||||||
|
skinningData.outputPositions = mapper.GetComponentPtr<Nz::Vector3f>(Nz::VertexComponent::Position);
|
||||||
|
skinningData.inputPositions = skinningData.outputPositions;
|
||||||
|
|
||||||
|
Nz::SkinPosition(skinningData, 0, mapper.GetVertexCount());
|
||||||
|
}*/
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < skeleton.GetJointCount(); ++i)
|
||||||
|
matrices[i] = skeleton.GetJoint(i)->GetSkinningMatrix();
|
||||||
|
|
||||||
|
std::shared_ptr<Nz::RenderBuffer> renderBuffer = device->InstantiateBuffer(Nz::BufferType::Uniform, skeletalBufferMem.size(), Nz::BufferUsage::Write, skeletalBufferMem.data());
|
||||||
|
|
||||||
|
entt::entity bobEntity = registry.create();
|
||||||
|
{
|
||||||
|
const Nz::Boxf& bobAABB = bobMesh->GetAABB();
|
||||||
|
std::shared_ptr<Nz::GraphicalMesh> bobGfxMesh = std::make_shared<Nz::GraphicalMesh>(*bobMesh);
|
||||||
|
|
||||||
|
std::shared_ptr<Nz::Model> bobModel = std::make_shared<Nz::Model>(std::move(bobGfxMesh), bobAABB);
|
||||||
|
for (std::size_t i = 0; i < bobMesh->GetMaterialCount(); ++i)
|
||||||
|
{
|
||||||
|
std::string matPath;
|
||||||
|
bobMesh->GetMaterialData(i).GetStringParameter(Nz::MaterialData::FilePath, &matPath);
|
||||||
|
|
||||||
|
std::shared_ptr<Nz::Material> bobMat = std::make_shared<Nz::Material>();
|
||||||
|
|
||||||
|
std::shared_ptr<Nz::MaterialPass> bobMatPass = std::make_shared<Nz::MaterialPass>(Nz::BasicMaterial::GetSettings());
|
||||||
|
bobMatPass->SetSharedUniformBuffer(0, renderBuffer);
|
||||||
|
|
||||||
|
bobMatPass->EnableDepthBuffer(true);
|
||||||
|
{
|
||||||
|
Nz::BasicMaterial basicMat(*bobMatPass);
|
||||||
|
basicMat.SetDiffuseMap(Nz::Texture::LoadFromFile(matPath + ".tga", texParams));
|
||||||
|
}
|
||||||
|
if (i == 0 || i == 3)
|
||||||
|
bobMat->AddPass("ForwardPass", bobMatPass);
|
||||||
|
|
||||||
|
bobModel->SetMaterial(i, bobMat);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& bobNode = registry.emplace<Nz::NodeComponent>(bobEntity);
|
||||||
|
bobNode.SetPosition(Nz::Vector3f(0.f, bobAABB.height / 2.f, 0.f));
|
||||||
|
bobNode.SetRotation(Nz::EulerAnglesf(-90.f, -90.f, 0.f));
|
||||||
|
bobNode.SetScale(1.f / 40.f * 0.5f);
|
||||||
|
|
||||||
|
auto& bobGfx = registry.emplace<Nz::GraphicsComponent>(bobEntity);
|
||||||
|
bobGfx.AttachRenderable(bobModel, 0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
entt::entity planeEntity = registry.create();
|
||||||
|
{
|
||||||
|
Nz::MeshParams meshPrimitiveParams;
|
||||||
|
meshPrimitiveParams.vertexDeclaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ_Normal_UV);
|
||||||
|
|
||||||
|
Nz::Vector2f planeSize(25.f, 25.f);
|
||||||
|
|
||||||
|
Nz::Mesh planeMesh;
|
||||||
|
planeMesh.CreateStatic();
|
||||||
|
planeMesh.BuildSubMesh(Nz::Primitive::Plane(planeSize, Nz::Vector2ui(0u), Nz::Matrix4f::Identity(), Nz::Rectf(0.f, 0.f, 10.f, 10.f)), meshPrimitiveParams);
|
||||||
|
planeMesh.SetMaterialCount(1);
|
||||||
|
|
||||||
|
std::shared_ptr<Nz::GraphicalMesh> planeMeshGfx = std::make_shared<Nz::GraphicalMesh>(planeMesh);
|
||||||
|
|
||||||
|
std::shared_ptr<Nz::Material> planeMat = std::make_shared<Nz::Material>();
|
||||||
|
|
||||||
|
std::shared_ptr<Nz::MaterialPass> planeMatPass = std::make_shared<Nz::MaterialPass>(Nz::BasicMaterial::GetSettings());
|
||||||
|
planeMatPass->EnableDepthBuffer(true);
|
||||||
|
{
|
||||||
|
Nz::BasicMaterial basicMat(*planeMatPass);
|
||||||
|
basicMat.SetDiffuseMap(Nz::Texture::LoadFromFile(resourceDir / "dev_grey.png", texParams));
|
||||||
|
|
||||||
|
Nz::TextureSamplerInfo planeSampler;
|
||||||
|
planeSampler.anisotropyLevel = 16;
|
||||||
|
planeSampler.wrapModeU = Nz::SamplerWrap::Repeat;
|
||||||
|
planeSampler.wrapModeV = Nz::SamplerWrap::Repeat;
|
||||||
|
basicMat.SetDiffuseSampler(planeSampler);
|
||||||
|
}
|
||||||
|
planeMat->AddPass("ForwardPass", planeMatPass);
|
||||||
|
|
||||||
|
std::shared_ptr<Nz::Model> planeModel = std::make_shared<Nz::Model>(std::move(planeMeshGfx), planeMesh.GetAABB());
|
||||||
|
planeModel->SetMaterial(0, planeMat);
|
||||||
|
|
||||||
|
auto& planeNode = registry.emplace<Nz::NodeComponent>(planeEntity);
|
||||||
|
|
||||||
|
auto& planeBody = registry.emplace<Nz::RigidBody3DComponent>(planeEntity, &physSytem.GetPhysWorld());
|
||||||
|
planeBody.SetGeom(std::make_shared<Nz::BoxCollider3D>(Nz::Vector3f(planeSize.x, 0.5f, planeSize.y), Nz::Vector3f(0.f, -0.25f, 0.f)));
|
||||||
|
|
||||||
|
auto& planeGfx = registry.emplace<Nz::GraphicsComponent>(planeEntity);
|
||||||
|
planeGfx.AttachRenderable(planeModel, 0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
Nz::Clock fpsClock, updateClock;
|
||||||
|
float incr = 0.f;
|
||||||
|
unsigned int currentFrame = 0;
|
||||||
|
unsigned int nextFrame = 1;
|
||||||
|
Nz::UInt64 lastTime = Nz::GetElapsedMicroseconds();
|
||||||
|
Nz::UInt64 fps = 0;
|
||||||
|
while (window.IsOpen())
|
||||||
|
{
|
||||||
|
Nz::UInt64 now = Nz::GetElapsedMicroseconds();
|
||||||
|
Nz::UInt64 elapsedTime = (now - lastTime) / 1'000'000.f;
|
||||||
|
lastTime = now;
|
||||||
|
|
||||||
|
Nz::WindowEvent event;
|
||||||
|
while (window.PollEvent(&event))
|
||||||
|
{
|
||||||
|
switch (event.type)
|
||||||
|
{
|
||||||
|
case Nz::WindowEventType::Quit:
|
||||||
|
window.Close();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Nz::WindowEventType::KeyPressed:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Nz::WindowEventType::MouseMoved:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateClock.GetMilliseconds() > 1000 / 60)
|
||||||
|
{
|
||||||
|
float updateTime = updateClock.Restart() / 1'000'000.f;
|
||||||
|
|
||||||
|
physSytem.Update(registry, 1.f / 60.f);
|
||||||
|
|
||||||
|
auto& playerBody = registry.get<Nz::RigidBody3DComponent>(playerEntity);
|
||||||
|
|
||||||
|
if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Space))
|
||||||
|
playerBody.AddForce(Nz::Vector3f(0.f, playerBody.GetMass() * 50.f, 0.f));
|
||||||
|
|
||||||
|
incr += 1.f / 60.f * 30.f;
|
||||||
|
if (incr >= 1.f)
|
||||||
|
{
|
||||||
|
incr -= 1.f;
|
||||||
|
|
||||||
|
currentFrame = nextFrame;
|
||||||
|
nextFrame++;
|
||||||
|
if (nextFrame >= bobAnim->GetFrameCount())
|
||||||
|
nextFrame = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bobAnim->AnimateSkeleton(&skeleton, currentFrame, nextFrame, incr);
|
||||||
|
for (std::size_t i = 0; i < skeleton.GetJointCount(); ++i)
|
||||||
|
matrices[i] = skeleton.GetJoint(i)->GetSkinningMatrix();
|
||||||
|
|
||||||
|
//renderBuffer->Fill(skeletalBufferMem.data(), 0, skeletalBufferMem.size());
|
||||||
|
|
||||||
|
/*auto spaceshipView = registry.view<Nz::NodeComponent, Nz::RigidBody3DComponent>();
|
||||||
|
for (auto&& [entity, node, _] : spaceshipView.each())
|
||||||
|
{
|
||||||
|
if (entity == playerEntity)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Nz::Vector3f spaceshipPos = node.GetPosition(Nz::CoordSys::Global);
|
||||||
|
if (spaceshipPos.GetSquaredLength() > Nz::IntegralPow(20.f, 2))
|
||||||
|
registry.destroy(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
Nz::RigidBody3DComponent& playerShipBody = registry.get<Nz::RigidBody3DComponent>(playerEntity);
|
||||||
|
Nz::Quaternionf currentRotation = playerShipBody.GetRotation();
|
||||||
|
|
||||||
|
Nz::Vector3f desiredHeading = registry.get<Nz::NodeComponent>(headingEntity).GetForward();
|
||||||
|
Nz::Vector3f currentHeading = currentRotation * Nz::Vector3f::Forward();
|
||||||
|
Nz::Vector3f headingError = currentHeading.CrossProduct(desiredHeading);
|
||||||
|
|
||||||
|
Nz::Vector3f desiredUp = registry.get<Nz::NodeComponent>(headingEntity).GetUp();
|
||||||
|
Nz::Vector3f currentUp = currentRotation * Nz::Vector3f::Up();
|
||||||
|
Nz::Vector3f upError = currentUp.CrossProduct(desiredUp);
|
||||||
|
|
||||||
|
playerShipBody.AddTorque(headingController.Update(headingError, elapsedTime) * 10.f);
|
||||||
|
playerShipBody.AddTorque(upController.Update(upError, elapsedTime) * 10.f);
|
||||||
|
|
||||||
|
float mass = playerShipBody.GetMass();
|
||||||
|
|
||||||
|
if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Up) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Z))
|
||||||
|
playerShipBody.AddForce(Nz::Vector3f::Forward() * 2.5f * mass, Nz::CoordSys::Local);
|
||||||
|
|
||||||
|
if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Down) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::S))
|
||||||
|
playerShipBody.AddForce(Nz::Vector3f::Backward() * 2.5f * mass, Nz::CoordSys::Local);
|
||||||
|
|
||||||
|
if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Left) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Q))
|
||||||
|
playerShipBody.AddForce(Nz::Vector3f::Left() * 2.5f * mass, Nz::CoordSys::Local);
|
||||||
|
|
||||||
|
if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Right) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::D))
|
||||||
|
playerShipBody.AddForce(Nz::Vector3f::Right() * 2.5f * mass, Nz::CoordSys::Local);
|
||||||
|
|
||||||
|
if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::LShift) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::RShift))
|
||||||
|
playerShipBody.AddForce(Nz::Vector3f::Up() * 3.f * mass, Nz::CoordSys::Local);
|
||||||
|
|
||||||
|
if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::LControl) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::RControl))
|
||||||
|
playerShipBody.AddForce(Nz::Vector3f::Down() * 3.f * mass, Nz::CoordSys::Local);*/
|
||||||
|
}
|
||||||
|
|
||||||
|
Nz::RenderFrame frame = window.AcquireFrame();
|
||||||
|
if (!frame)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
frame.Execute([&](Nz::CommandBufferBuilder& builder)
|
||||||
|
{
|
||||||
|
builder.BeginDebugRegion("Skeletal UBO Update", Nz::Color::Yellow);
|
||||||
|
{
|
||||||
|
builder.PreTransferBarrier();
|
||||||
|
|
||||||
|
auto& skeletalAllocation = frame.GetUploadPool().Allocate(skeletalBufferMem.size());
|
||||||
|
std::memcpy(skeletalAllocation.mappedPtr, skeletalBufferMem.data(), skeletalBufferMem.size());
|
||||||
|
|
||||||
|
builder.CopyBuffer(skeletalAllocation, Nz::RenderBufferView(renderBuffer.get()));
|
||||||
|
|
||||||
|
builder.PostTransferBarrier();
|
||||||
|
}
|
||||||
|
builder.EndDebugRegion();
|
||||||
|
}, Nz::QueueType::Graphics);
|
||||||
|
|
||||||
|
renderSystem.Render(registry, frame);
|
||||||
|
|
||||||
|
frame.Present();
|
||||||
|
|
||||||
|
fps++;
|
||||||
|
|
||||||
|
if (fpsClock.GetMilliseconds() >= 1000)
|
||||||
|
{
|
||||||
|
fpsClock.Restart();
|
||||||
|
|
||||||
|
window.SetTitle(windowTitle + " - " + Nz::NumberToString(fps) + " FPS" + " - " + Nz::NumberToString(registry.alive()) + " entities");
|
||||||
|
|
||||||
|
fps = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
target("Showcase")
|
||||||
|
set_group("Examples")
|
||||||
|
set_kind("binary")
|
||||||
|
add_deps("NazaraAudio", "NazaraGraphics", "NazaraPhysics2D", "NazaraPhysics3D", "NazaraWidgets")
|
||||||
|
add_packages("entt")
|
||||||
|
add_files("main.cpp")
|
||||||
|
|
@ -41,6 +41,7 @@ namespace Nz
|
||||||
struct RenderStates
|
struct RenderStates
|
||||||
{
|
{
|
||||||
RenderBufferView lightData;
|
RenderBufferView lightData;
|
||||||
|
RenderBufferView skeletalData;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ namespace Nz
|
||||||
InstanceDataUbo,
|
InstanceDataUbo,
|
||||||
LightDataUbo,
|
LightDataUbo,
|
||||||
OverlayTexture,
|
OverlayTexture,
|
||||||
|
SkeletalDataUbo,
|
||||||
ViewerDataUbo,
|
ViewerDataUbo,
|
||||||
|
|
||||||
Max = ViewerDataUbo
|
Max = ViewerDataUbo
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
#include <Nazara/Graphics/Config.hpp>
|
#include <Nazara/Graphics/Config.hpp>
|
||||||
#include <Nazara/Graphics/Enums.hpp>
|
#include <Nazara/Graphics/Enums.hpp>
|
||||||
#include <Nazara/Graphics/MaterialPipeline.hpp>
|
#include <Nazara/Graphics/MaterialPipeline.hpp>
|
||||||
|
#include <Nazara/Renderer/RenderBufferView.hpp>
|
||||||
#include <Nazara/Renderer/Texture.hpp>
|
#include <Nazara/Renderer/Texture.hpp>
|
||||||
#include <Nazara/Renderer/TextureSampler.hpp>
|
#include <Nazara/Renderer/TextureSampler.hpp>
|
||||||
#include <Nazara/Utility/UniformBuffer.hpp>
|
#include <Nazara/Utility/UniformBuffer.hpp>
|
||||||
|
|
@ -101,9 +102,10 @@ namespace Nz
|
||||||
inline void SetOptionValue(std::size_t optionIndex, nzsl::Ast::ConstantSingleValue value);
|
inline void SetOptionValue(std::size_t optionIndex, nzsl::Ast::ConstantSingleValue value);
|
||||||
inline void SetPointSize(float pointSize);
|
inline void SetPointSize(float pointSize);
|
||||||
inline void SetPrimitiveMode(PrimitiveMode mode);
|
inline void SetPrimitiveMode(PrimitiveMode mode);
|
||||||
|
inline void SetSharedUniformBuffer(std::size_t sharedUboIndex, std::shared_ptr<RenderBuffer> uniformBuffer);
|
||||||
|
inline void SetSharedUniformBuffer(std::size_t sharedUboIndex, std::shared_ptr<RenderBuffer> uniformBuffer, UInt64 offset, UInt64 size);
|
||||||
inline void SetTexture(std::size_t textureIndex, std::shared_ptr<Texture> texture);
|
inline void SetTexture(std::size_t textureIndex, std::shared_ptr<Texture> texture);
|
||||||
inline void SetTextureSampler(std::size_t textureIndex, TextureSamplerInfo samplerInfo);
|
inline void SetTextureSampler(std::size_t textureIndex, TextureSamplerInfo samplerInfo);
|
||||||
inline void SetUniformBuffer(std::size_t bufferIndex, std::shared_ptr<RenderBuffer> uniformBuffer);
|
|
||||||
|
|
||||||
void Update(RenderFrame& renderFrame, CommandBufferBuilder& builder);
|
void Update(RenderFrame& renderFrame, CommandBufferBuilder& builder);
|
||||||
|
|
||||||
|
|
@ -135,6 +137,12 @@ namespace Nz
|
||||||
NazaraSlot(UberShader, OnShaderUpdated, onShaderUpdated);
|
NazaraSlot(UberShader, OnShaderUpdated, onShaderUpdated);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ShaderUniformBuffer
|
||||||
|
{
|
||||||
|
std::shared_ptr<RenderBuffer> buffer; //< kept for ownership
|
||||||
|
RenderBufferView bufferView;
|
||||||
|
};
|
||||||
|
|
||||||
struct UniformBuffer
|
struct UniformBuffer
|
||||||
{
|
{
|
||||||
std::shared_ptr<RenderBuffer> buffer;
|
std::shared_ptr<RenderBuffer> buffer;
|
||||||
|
|
@ -146,6 +154,7 @@ namespace Nz
|
||||||
std::shared_ptr<const MaterialSettings> m_settings;
|
std::shared_ptr<const MaterialSettings> m_settings;
|
||||||
std::vector<MaterialTexture> m_textures;
|
std::vector<MaterialTexture> m_textures;
|
||||||
std::vector<ShaderEntry> m_shaders;
|
std::vector<ShaderEntry> m_shaders;
|
||||||
|
std::vector<ShaderUniformBuffer> m_sharedUniformBuffers;
|
||||||
std::vector<UniformBuffer> m_uniformBuffers;
|
std::vector<UniformBuffer> m_uniformBuffers;
|
||||||
mutable std::shared_ptr<MaterialPipeline> m_pipeline;
|
mutable std::shared_ptr<MaterialPipeline> m_pipeline;
|
||||||
mutable MaterialPipelineInfo m_pipelineInfo;
|
mutable MaterialPipelineInfo m_pipelineInfo;
|
||||||
|
|
|
||||||
|
|
@ -602,6 +602,32 @@ namespace Nz
|
||||||
InvalidatePipeline();
|
InvalidatePipeline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void MaterialPass::SetSharedUniformBuffer(std::size_t sharedUboIndex, std::shared_ptr<RenderBuffer> uniformBuffer)
|
||||||
|
{
|
||||||
|
if (uniformBuffer)
|
||||||
|
{
|
||||||
|
UInt64 size = uniformBuffer->GetSize();
|
||||||
|
return SetSharedUniformBuffer(sharedUboIndex, std::move(uniformBuffer), 0, size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return SetSharedUniformBuffer(sharedUboIndex, std::move(uniformBuffer), 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void MaterialPass::SetSharedUniformBuffer(std::size_t sharedUboIndex, std::shared_ptr<RenderBuffer> uniformBuffer, UInt64 offset, UInt64 size)
|
||||||
|
{
|
||||||
|
NazaraAssert(sharedUboIndex < m_sharedUniformBuffers.size(), "Invalid shared uniform buffer index");
|
||||||
|
|
||||||
|
RenderBufferView bufferView(uniformBuffer.get(), offset, size);
|
||||||
|
|
||||||
|
if (m_sharedUniformBuffers[sharedUboIndex].bufferView != bufferView)
|
||||||
|
{
|
||||||
|
m_sharedUniformBuffers[sharedUboIndex].bufferView = bufferView;
|
||||||
|
m_sharedUniformBuffers[sharedUboIndex].buffer = std::move(uniformBuffer);
|
||||||
|
|
||||||
|
InvalidateShaderBinding();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline void MaterialPass::SetTexture(std::size_t textureIndex, std::shared_ptr<Texture> texture)
|
inline void MaterialPass::SetTexture(std::size_t textureIndex, std::shared_ptr<Texture> texture)
|
||||||
{
|
{
|
||||||
NazaraAssert(textureIndex < m_textures.size(), "Invalid texture index");
|
NazaraAssert(textureIndex < m_textures.size(), "Invalid texture index");
|
||||||
|
|
@ -624,18 +650,6 @@ namespace Nz
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void MaterialPass::SetUniformBuffer(std::size_t bufferIndex, std::shared_ptr<RenderBuffer> uniformBuffer)
|
|
||||||
{
|
|
||||||
NazaraAssert(bufferIndex < m_uniformBuffers.size(), "Invalid shared uniform buffer index");
|
|
||||||
if (m_uniformBuffers[bufferIndex].buffer != uniformBuffer)
|
|
||||||
{
|
|
||||||
m_uniformBuffers[bufferIndex].buffer = std::move(uniformBuffer);
|
|
||||||
m_uniformBuffers[bufferIndex].dataInvalidated = true;
|
|
||||||
|
|
||||||
InvalidateShaderBinding();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void MaterialPass::InvalidatePipeline()
|
inline void MaterialPass::InvalidatePipeline()
|
||||||
{
|
{
|
||||||
m_pipelineUpdated = false;
|
m_pipelineUpdated = false;
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,12 @@ namespace Nz
|
||||||
struct SkinningData
|
struct SkinningData
|
||||||
{
|
{
|
||||||
const Joint* joints;
|
const Joint* joints;
|
||||||
const SkeletalMeshVertex* inputVertex;
|
SparsePtr<const Vector3f> inputPositions;
|
||||||
MeshVertex* outputVertex;
|
SparsePtr<const Vector4i32> inputJointIndices;
|
||||||
|
SparsePtr<const Vector4f> inputJointWeights;
|
||||||
|
SparsePtr<const Vector2f> inputUv;
|
||||||
|
SparsePtr<Vector3f> outputPositions;
|
||||||
|
SparsePtr<Vector2f> outputUv;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexPointers
|
struct VertexPointers
|
||||||
|
|
|
||||||
|
|
@ -385,6 +385,8 @@ namespace Nz
|
||||||
Unused = -1,
|
Unused = -1,
|
||||||
|
|
||||||
Color,
|
Color,
|
||||||
|
JointIndices,
|
||||||
|
JointWeights,
|
||||||
Normal,
|
Normal,
|
||||||
Position,
|
Position,
|
||||||
Tangent,
|
Tangent,
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,9 @@ namespace Nz
|
||||||
|
|
||||||
struct Joint
|
struct Joint
|
||||||
{
|
{
|
||||||
|
std::string name;
|
||||||
Int32 parent;
|
Int32 parent;
|
||||||
Quaternionf bindOrient;
|
Quaternionf bindOrient;
|
||||||
std::string name;
|
|
||||||
Vector3f bindPos;
|
Vector3f bindPos;
|
||||||
UInt32 flags;
|
UInt32 flags;
|
||||||
UInt32 index;
|
UInt32 index;
|
||||||
|
|
|
||||||
|
|
@ -76,8 +76,6 @@ namespace Nz
|
||||||
|
|
||||||
struct VertexStruct_XYZ_Normal_UV_Tangent_Skinning : VertexStruct_XYZ_Normal_UV_Tangent
|
struct VertexStruct_XYZ_Normal_UV_Tangent_Skinning : VertexStruct_XYZ_Normal_UV_Tangent
|
||||||
{
|
{
|
||||||
Int32 weightCount;
|
|
||||||
|
|
||||||
Vector4f weights;
|
Vector4f weights;
|
||||||
Vector4i32 jointIndexes;
|
Vector4i32 jointIndexes;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -43,32 +43,29 @@ SOFTWARE.
|
||||||
#include <assimp/mesh.h>
|
#include <assimp/mesh.h>
|
||||||
#include <assimp/postprocess.h>
|
#include <assimp/postprocess.h>
|
||||||
#include <assimp/scene.h>
|
#include <assimp/scene.h>
|
||||||
#include <set>
|
#include <unordered_set>
|
||||||
|
|
||||||
using namespace Nz;
|
using namespace Nz;
|
||||||
|
|
||||||
void ProcessJoints(aiNode* node, Skeleton* skeleton, const std::set<std::string>& joints)
|
void ProcessJoints(aiNode* node, Skeleton* skeleton, const std::unordered_set<std::string_view>& joints)
|
||||||
{
|
{
|
||||||
std::string jointName(node->mName.data, node->mName.length);
|
std::string_view jointName(node->mName.data, node->mName.length);
|
||||||
if (joints.count(jointName))
|
if (joints.count(jointName))
|
||||||
{
|
{
|
||||||
Joint* joint = skeleton->GetJoint(jointName);
|
Joint* joint = skeleton->GetJoint(jointName);
|
||||||
if (joint)
|
|
||||||
{
|
|
||||||
if (node->mParent)
|
|
||||||
joint->SetParent(skeleton->GetJoint(node->mParent->mName.C_Str()));
|
|
||||||
|
|
||||||
Matrix4f transformMatrix(node->mTransformation.a1, node->mTransformation.a2, node->mTransformation.a3, node->mTransformation.a4,
|
if (node->mParent)
|
||||||
node->mTransformation.b1, node->mTransformation.b2, node->mTransformation.b3, node->mTransformation.b4,
|
joint->SetParent(skeleton->GetJoint(node->mParent->mName.C_Str()));
|
||||||
node->mTransformation.c1, node->mTransformation.c2, node->mTransformation.c3, node->mTransformation.c4,
|
|
||||||
node->mTransformation.d1, node->mTransformation.d2, node->mTransformation.d3, node->mTransformation.d4);
|
|
||||||
|
|
||||||
transformMatrix.Transpose();
|
Matrix4f transformMatrix(node->mTransformation.a1, node->mTransformation.a2, node->mTransformation.a3, node->mTransformation.a4,
|
||||||
|
node->mTransformation.b1, node->mTransformation.b2, node->mTransformation.b3, node->mTransformation.b4,
|
||||||
|
node->mTransformation.c1, node->mTransformation.c2, node->mTransformation.c3, node->mTransformation.c4,
|
||||||
|
node->mTransformation.d1, node->mTransformation.d2, node->mTransformation.d3, node->mTransformation.d4);
|
||||||
|
|
||||||
transformMatrix.InverseTransform();
|
transformMatrix.Transpose();
|
||||||
|
transformMatrix.InverseTransform();
|
||||||
|
|
||||||
joint->SetInverseBindMatrix(transformMatrix);
|
joint->SetInverseBindMatrix(transformMatrix);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i < node->mNumChildren; ++i)
|
for (unsigned int i = 0; i < node->mNumChildren; ++i)
|
||||||
|
|
@ -118,8 +115,8 @@ std::shared_ptr<Animation> LoadAnimation(Stream& stream, const AnimationParams&
|
||||||
| aiProcess_TransformUVCoords | aiProcess_Triangulate;
|
| aiProcess_TransformUVCoords | aiProcess_Triangulate;
|
||||||
|
|
||||||
aiPropertyStore* properties = aiCreatePropertyStore();
|
aiPropertyStore* properties = aiCreatePropertyStore();
|
||||||
aiSetImportPropertyInteger(properties, AI_CONFIG_PP_LBW_MAX_WEIGHTS, 4);
|
aiSetImportPropertyInteger(properties, AI_CONFIG_PP_LBW_MAX_WEIGHTS, 4);
|
||||||
aiSetImportPropertyInteger(properties, AI_CONFIG_PP_RVC_FLAGS, ~aiComponent_ANIMATIONS);
|
aiSetImportPropertyInteger(properties, AI_CONFIG_PP_RVC_FLAGS, ~aiComponent_ANIMATIONS);
|
||||||
|
|
||||||
const aiScene* scene = aiImportFileExWithProperties(userdata.originalFilePath, postProcess, &fileIO, properties);
|
const aiScene* scene = aiImportFileExWithProperties(userdata.originalFilePath, postProcess, &fileIO, properties);
|
||||||
aiReleasePropertyStore(properties);
|
aiReleasePropertyStore(properties);
|
||||||
|
|
@ -259,7 +256,7 @@ std::shared_ptr<Mesh> LoadMesh(Stream& stream, const MeshParams& parameters)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<std::string> joints;
|
std::unordered_set<std::string_view> joints;
|
||||||
|
|
||||||
bool animatedMesh = false;
|
bool animatedMesh = false;
|
||||||
if (parameters.animated)
|
if (parameters.animated)
|
||||||
|
|
@ -285,8 +282,8 @@ std::shared_ptr<Mesh> LoadMesh(Stream& stream, const MeshParams& parameters)
|
||||||
|
|
||||||
// First, assign names
|
// First, assign names
|
||||||
unsigned int jointIndex = 0;
|
unsigned int jointIndex = 0;
|
||||||
for (const std::string& jointName : joints)
|
for (std::string_view jointName : joints)
|
||||||
skeleton->GetJoint(jointIndex++)->SetName(jointName);
|
skeleton->GetJoint(jointIndex++)->SetName(std::string(jointName));
|
||||||
|
|
||||||
ProcessJoints(scene->mRootNode, skeleton, joints);
|
ProcessJoints(scene->mRootNode, skeleton, joints);
|
||||||
|
|
||||||
|
|
@ -330,34 +327,51 @@ std::shared_ptr<Mesh> LoadMesh(Stream& stream, const MeshParams& parameters)
|
||||||
normalTangentMatrix.ApplyScale(1.f / normalTangentMatrix.GetScale());
|
normalTangentMatrix.ApplyScale(1.f / normalTangentMatrix.GetScale());
|
||||||
|
|
||||||
std::shared_ptr<VertexBuffer> vertexBuffer = std::make_shared<VertexBuffer>(VertexDeclaration::Get(VertexLayout::XYZ_Normal_UV_Tangent_Skinning), vertexCount, parameters.vertexBufferFlags, parameters.bufferFactory);
|
std::shared_ptr<VertexBuffer> vertexBuffer = std::make_shared<VertexBuffer>(VertexDeclaration::Get(VertexLayout::XYZ_Normal_UV_Tangent_Skinning), vertexCount, parameters.vertexBufferFlags, parameters.bufferFactory);
|
||||||
BufferMapper<VertexBuffer> vertexMapper(*vertexBuffer, 0, vertexBuffer->GetVertexCount());
|
|
||||||
SkeletalMeshVertex* vertices = static_cast<SkeletalMeshVertex*>(vertexMapper.GetPointer());
|
|
||||||
|
|
||||||
for (std::size_t vertexIdx = 0; vertexIdx < vertexCount; ++vertexIdx)
|
VertexMapper vertexMapper(*vertexBuffer);
|
||||||
|
|
||||||
|
auto posPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent::Position);
|
||||||
|
auto normalPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent::Normal);
|
||||||
|
auto tangentPtr = vertexMapper.GetComponentPtr<Vector3f>(VertexComponent::Tangent);
|
||||||
|
auto jointIndicesPtr = vertexMapper.GetComponentPtr<Vector4i32>(VertexComponent::JointIndices);
|
||||||
|
auto jointWeightPtr = vertexMapper.GetComponentPtr<Vector4f>(VertexComponent::JointWeights);
|
||||||
|
auto uvPtr = vertexMapper.GetComponentPtr<Vector2f>(VertexComponent::TexCoord);
|
||||||
|
|
||||||
|
for (std::size_t vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex)
|
||||||
{
|
{
|
||||||
aiVector3D normal = iMesh->mNormals[vertexIdx];
|
aiVector3D normal = iMesh->mNormals[vertexIndex];
|
||||||
aiVector3D position = iMesh->mVertices[vertexIdx];
|
aiVector3D position = iMesh->mVertices[vertexIndex];
|
||||||
aiVector3D tangent = iMesh->mTangents[vertexIdx];
|
aiVector3D tangent = iMesh->mTangents[vertexIndex];
|
||||||
aiVector3D uv = iMesh->mTextureCoords[0][vertexIdx];
|
aiVector3D uv = iMesh->mTextureCoords[0][vertexIndex];
|
||||||
|
|
||||||
vertices[vertexIdx].weightCount = 0;
|
if (posPtr)
|
||||||
vertices[vertexIdx].normal = normalTangentMatrix.Transform({ normal.x, normal.y, normal.z }, 0.f);
|
posPtr[vertexIndex] = parameters.matrix * Vector3f(position.x, position.y, position.z);
|
||||||
vertices[vertexIdx].position = parameters.matrix * Vector3f(position.x, position.y, position.z);
|
|
||||||
vertices[vertexIdx].tangent = normalTangentMatrix.Transform({ tangent.x, tangent.y, tangent.z }, 0.f);
|
if (normalPtr)
|
||||||
vertices[vertexIdx].uv = parameters.texCoordOffset + Vector2f(uv.x, uv.y) * parameters.texCoordScale;
|
normalPtr[vertexIndex] = normalTangentMatrix.Transform({ normal.x, normal.y, normal.z }, 0.f);
|
||||||
|
|
||||||
|
if (tangentPtr)
|
||||||
|
tangentPtr[vertexIndex] = normalTangentMatrix.Transform({ tangent.x, tangent.y, tangent.z }, 0.f);
|
||||||
|
|
||||||
|
if (uvPtr)
|
||||||
|
uvPtr[vertexIndex] = parameters.texCoordOffset + Vector2f(uv.x, uv.y) * parameters.texCoordScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int boneIdx = 0; boneIdx < iMesh->mNumBones; ++boneIdx)
|
if (jointIndicesPtr || jointWeightPtr)
|
||||||
{
|
{
|
||||||
aiBone* bone = iMesh->mBones[boneIdx];
|
for (unsigned int boneIndex = 0; boneIndex < iMesh->mNumBones; ++boneIndex)
|
||||||
for (unsigned int weightIdx = 0; weightIdx < bone->mNumWeights; ++weightIdx)
|
|
||||||
{
|
{
|
||||||
aiVertexWeight& vertexWeight = bone->mWeights[weightIdx];
|
aiBone* bone = iMesh->mBones[boneIndex];
|
||||||
SkeletalMeshVertex& vertex = vertices[vertexWeight.mVertexId];
|
for (unsigned int weightIndex = 0; weightIndex < bone->mNumWeights; ++weightIndex)
|
||||||
|
{
|
||||||
|
aiVertexWeight& vertexWeight = bone->mWeights[weightIndex];
|
||||||
|
|
||||||
std::size_t weightIndex = vertex.weightCount++;
|
if (jointIndicesPtr)
|
||||||
vertex.jointIndexes[weightIndex] = boneIdx;
|
jointIndicesPtr[vertexWeight.mVertexId][weightIndex] = boneIndex;
|
||||||
vertex.weights[weightIndex] = vertexWeight.mWeight;
|
|
||||||
|
if (jointWeightPtr)
|
||||||
|
jointWeightPtr[vertexWeight.mVertexId][weightIndex] = vertexWeight.mWeight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,16 @@ namespace Nz
|
||||||
options.defaultValues
|
options.defaultValues
|
||||||
});
|
});
|
||||||
|
|
||||||
|
FieldOffsets skeletalOffsets(StructLayout::Std140);
|
||||||
|
skeletalOffsets.AddMatrixArray(StructFieldType::Float1, 4, 4, true, 100);
|
||||||
|
|
||||||
|
settings.sharedUniformBlocks.push_back({
|
||||||
|
6,
|
||||||
|
"SkeletalData",
|
||||||
|
{},
|
||||||
|
ShaderStageType::Vertex
|
||||||
|
});
|
||||||
|
|
||||||
// Common data
|
// Common data
|
||||||
settings.textures.push_back({
|
settings.textures.push_back({
|
||||||
3,
|
3,
|
||||||
|
|
@ -160,9 +170,11 @@ namespace Nz
|
||||||
|
|
||||||
settings.sharedUniformBlocks.push_back(PredefinedInstanceData::GetUniformBlock(4, nzsl::ShaderStageType::Vertex));
|
settings.sharedUniformBlocks.push_back(PredefinedInstanceData::GetUniformBlock(4, nzsl::ShaderStageType::Vertex));
|
||||||
settings.sharedUniformBlocks.push_back(PredefinedViewerData::GetUniformBlock(5, nzsl::ShaderStageType_All));
|
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::InstanceDataUbo)] = 4;
|
||||||
settings.predefinedBindings[UnderlyingCast(PredefinedShaderBinding::OverlayTexture)] = 3;
|
settings.predefinedBindings[UnderlyingCast(PredefinedShaderBinding::OverlayTexture)] = 3;
|
||||||
|
//settings.predefinedBindings[UnderlyingCast(PredefinedShaderBinding::SkeletalDataUbo)] = 6;
|
||||||
settings.predefinedBindings[UnderlyingCast(PredefinedShaderBinding::ViewerDataUbo)] = 5;
|
settings.predefinedBindings[UnderlyingCast(PredefinedShaderBinding::ViewerDataUbo)] = 5;
|
||||||
|
|
||||||
settings.shaders = options.shaders;
|
settings.shaders = options.shaders;
|
||||||
|
|
@ -194,6 +206,14 @@ namespace Nz
|
||||||
config.optionValues[CRC32("UvLocation")] = locationIndex;
|
config.optionValues[CRC32("UvLocation")] = locationIndex;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case VertexComponent::JointIndices:
|
||||||
|
config.optionValues[CRC32("JointIndicesLocation")] = locationIndex;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VertexComponent::JointWeights:
|
||||||
|
config.optionValues[CRC32("JointWeightsLocation")] = locationIndex;
|
||||||
|
break;
|
||||||
|
|
||||||
case VertexComponent::Unused:
|
case VertexComponent::Unused:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,6 @@ namespace Nz
|
||||||
{
|
{
|
||||||
std::shared_ptr<GraphicalMesh> GraphicalMesh::BuildFromMesh(const Mesh& mesh)
|
std::shared_ptr<GraphicalMesh> GraphicalMesh::BuildFromMesh(const Mesh& mesh)
|
||||||
{
|
{
|
||||||
assert(mesh.GetAnimationType() == AnimationType::Static);
|
|
||||||
|
|
||||||
const std::shared_ptr<RenderDevice>& renderDevice = Graphics::Instance()->GetRenderDevice();
|
const std::shared_ptr<RenderDevice>& renderDevice = Graphics::Instance()->GetRenderDevice();
|
||||||
|
|
||||||
std::shared_ptr<GraphicalMesh> gfxMesh = std::make_shared<GraphicalMesh>();
|
std::shared_ptr<GraphicalMesh> gfxMesh = std::make_shared<GraphicalMesh>();
|
||||||
|
|
|
||||||
|
|
@ -47,9 +47,11 @@ namespace Nz
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& textureSettings = m_settings->GetTextures();
|
const auto& textureSettings = m_settings->GetTextures();
|
||||||
|
const auto& sharedUboSettings = m_settings->GetSharedUniformBlocks();
|
||||||
const auto& uboSettings = m_settings->GetUniformBlocks();
|
const auto& uboSettings = m_settings->GetUniformBlocks();
|
||||||
|
|
||||||
m_textures.resize(textureSettings.size());
|
m_textures.resize(textureSettings.size());
|
||||||
|
m_sharedUniformBuffers.resize(sharedUboSettings.size());
|
||||||
|
|
||||||
m_uniformBuffers.reserve(uboSettings.size());
|
m_uniformBuffers.reserve(uboSettings.size());
|
||||||
for (const auto& uniformBufferInfo : uboSettings)
|
for (const auto& uniformBufferInfo : uboSettings)
|
||||||
|
|
@ -68,6 +70,7 @@ namespace Nz
|
||||||
void MaterialPass::FillShaderBinding(std::vector<ShaderBinding::Binding>& bindings) const
|
void MaterialPass::FillShaderBinding(std::vector<ShaderBinding::Binding>& bindings) const
|
||||||
{
|
{
|
||||||
const auto& textureSettings = m_settings->GetTextures();
|
const auto& textureSettings = m_settings->GetTextures();
|
||||||
|
const auto& sharedUboSettings = m_settings->GetSharedUniformBlocks();
|
||||||
const auto& uboSettings = m_settings->GetUniformBlocks();
|
const auto& uboSettings = m_settings->GetUniformBlocks();
|
||||||
|
|
||||||
// Textures
|
// 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
|
// Owned UBO
|
||||||
for (std::size_t i = 0; i < m_uniformBuffers.size(); ++i)
|
for (std::size_t i = 0; i < m_uniformBuffers.size(); ++i)
|
||||||
|
|
|
||||||
|
|
@ -186,7 +186,7 @@ namespace Nz
|
||||||
options.phongTextureIndexes->emissive = settings.textures.size();
|
options.phongTextureIndexes->emissive = settings.textures.size();
|
||||||
|
|
||||||
settings.textures.push_back({
|
settings.textures.push_back({
|
||||||
7,
|
8,
|
||||||
"Emissive",
|
"Emissive",
|
||||||
ImageType::E2D
|
ImageType::E2D
|
||||||
});
|
});
|
||||||
|
|
@ -195,7 +195,7 @@ namespace Nz
|
||||||
options.phongTextureIndexes->height = settings.textures.size();
|
options.phongTextureIndexes->height = settings.textures.size();
|
||||||
|
|
||||||
settings.textures.push_back({
|
settings.textures.push_back({
|
||||||
8,
|
9,
|
||||||
"Height",
|
"Height",
|
||||||
ImageType::E2D
|
ImageType::E2D
|
||||||
});
|
});
|
||||||
|
|
@ -204,7 +204,7 @@ namespace Nz
|
||||||
options.phongTextureIndexes->normal = settings.textures.size();
|
options.phongTextureIndexes->normal = settings.textures.size();
|
||||||
|
|
||||||
settings.textures.push_back({
|
settings.textures.push_back({
|
||||||
9,
|
10,
|
||||||
"Normal",
|
"Normal",
|
||||||
ImageType::E2D
|
ImageType::E2D
|
||||||
});
|
});
|
||||||
|
|
@ -213,7 +213,7 @@ namespace Nz
|
||||||
options.phongTextureIndexes->specular = settings.textures.size();
|
options.phongTextureIndexes->specular = settings.textures.size();
|
||||||
|
|
||||||
settings.textures.push_back({
|
settings.textures.push_back({
|
||||||
10,
|
11,
|
||||||
"Specular",
|
"Specular",
|
||||||
ImageType::E2D
|
ImageType::E2D
|
||||||
});
|
});
|
||||||
|
|
@ -229,8 +229,8 @@ namespace Nz
|
||||||
options.defaultValues
|
options.defaultValues
|
||||||
});
|
});
|
||||||
|
|
||||||
settings.sharedUniformBlocks.push_back(PredefinedLightData::GetUniformBlock(6, nzsl::ShaderStageType::Fragment));
|
settings.sharedUniformBlocks.push_back(PredefinedLightData::GetUniformBlock(7, nzsl::ShaderStageType::Fragment));
|
||||||
settings.predefinedBindings[UnderlyingCast(PredefinedShaderBinding::LightDataUbo)] = 6;
|
settings.predefinedBindings[UnderlyingCast(PredefinedShaderBinding::LightDataUbo)] = 7;
|
||||||
|
|
||||||
settings.shaders = options.shaders;
|
settings.shaders = options.shaders;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,13 @@ option ColorLocation: i32 = -1;
|
||||||
option PosLocation: i32;
|
option PosLocation: i32;
|
||||||
option UvLocation: i32 = -1;
|
option UvLocation: i32 = -1;
|
||||||
|
|
||||||
|
option JointIndicesLocation: i32 = -1;
|
||||||
|
option JointWeightsLocation: i32 = -1;
|
||||||
|
|
||||||
const HasVertexColor = (ColorLocation >= 0);
|
const HasVertexColor = (ColorLocation >= 0);
|
||||||
const HasColor = (HasVertexColor || Billboard);
|
const HasColor = (HasVertexColor || Billboard);
|
||||||
const HasUV = (UvLocation >= 0);
|
const HasUV = (UvLocation >= 0);
|
||||||
|
const HasSkinning = (JointIndicesLocation >= 0 && JointWeightsLocation >= 0);
|
||||||
|
|
||||||
[layout(std140)]
|
[layout(std140)]
|
||||||
struct MaterialSettings
|
struct MaterialSettings
|
||||||
|
|
@ -30,6 +34,14 @@ struct MaterialSettings
|
||||||
BaseColor: vec4[f32]
|
BaseColor: vec4[f32]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MaxJointCount: u32 = u32(200); //< FIXME: Fix integral value types
|
||||||
|
|
||||||
|
[layout(std140)]
|
||||||
|
struct SkeletalData
|
||||||
|
{
|
||||||
|
JointMatrices: array[mat4[f32], MaxJointCount]
|
||||||
|
}
|
||||||
|
|
||||||
external
|
external
|
||||||
{
|
{
|
||||||
[binding(0)] settings: uniform[MaterialSettings],
|
[binding(0)] settings: uniform[MaterialSettings],
|
||||||
|
|
@ -38,6 +50,7 @@ external
|
||||||
[binding(3)] TextureOverlay: sampler2D[f32],
|
[binding(3)] TextureOverlay: sampler2D[f32],
|
||||||
[binding(4)] instanceData: uniform[InstanceData],
|
[binding(4)] instanceData: uniform[InstanceData],
|
||||||
[binding(5)] viewerData: uniform[ViewerData],
|
[binding(5)] viewerData: uniform[ViewerData],
|
||||||
|
[binding(6)] skeletalData: uniform[SkeletalData]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fragment stage
|
// Fragment stage
|
||||||
|
|
@ -92,6 +105,12 @@ struct VertIn
|
||||||
[cond(HasUV), location(UvLocation)]
|
[cond(HasUV), location(UvLocation)]
|
||||||
uv: vec2[f32],
|
uv: vec2[f32],
|
||||||
|
|
||||||
|
[cond(HasSkinning), location(JointIndicesLocation)]
|
||||||
|
jointIndices: vec4[i32],
|
||||||
|
|
||||||
|
[cond(HasSkinning), location(JointWeightsLocation)]
|
||||||
|
jointWeights: vec4[f32],
|
||||||
|
|
||||||
[cond(Billboard), location(BillboardCenterLocation)]
|
[cond(Billboard), location(BillboardCenterLocation)]
|
||||||
billboardCenter: vec3[f32],
|
billboardCenter: vec3[f32],
|
||||||
|
|
||||||
|
|
@ -143,8 +162,26 @@ fn billboardMain(input: VertIn) -> VertOut
|
||||||
[entry(vert), cond(!Billboard)]
|
[entry(vert), cond(!Billboard)]
|
||||||
fn main(input: VertIn) -> VertOut
|
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;
|
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)
|
const if (HasColor)
|
||||||
output.color = input.color;
|
output.color = input.color;
|
||||||
|
|
|
||||||
|
|
@ -62,11 +62,11 @@ external
|
||||||
[binding(3)] TextureOverlay: sampler2D[f32],
|
[binding(3)] TextureOverlay: sampler2D[f32],
|
||||||
[binding(4)] instanceData: uniform[InstanceData],
|
[binding(4)] instanceData: uniform[InstanceData],
|
||||||
[binding(5)] viewerData: uniform[ViewerData],
|
[binding(5)] viewerData: uniform[ViewerData],
|
||||||
[binding(6)] lightData: uniform[LightData],
|
[binding(7)] lightData: uniform[LightData],
|
||||||
[binding(7)] MaterialEmissiveMap: sampler2D[f32],
|
[binding(8)] MaterialEmissiveMap: sampler2D[f32],
|
||||||
[binding(8)] MaterialHeightMap: sampler2D[f32],
|
[binding(9)] MaterialHeightMap: sampler2D[f32],
|
||||||
[binding(9)] MaterialNormalMap: sampler2D[f32],
|
[binding(10)] MaterialNormalMap: sampler2D[f32],
|
||||||
[binding(10)] MaterialSpecularMap: sampler2D[f32],
|
[binding(11)] MaterialSpecularMap: sampler2D[f32],
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VertToFrag
|
struct VertToFrag
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ namespace Nz
|
||||||
const WorldInstance* currentWorldInstance = nullptr;
|
const WorldInstance* currentWorldInstance = nullptr;
|
||||||
Recti currentScissorBox = invalidScissorBox;
|
Recti currentScissorBox = invalidScissorBox;
|
||||||
RenderBufferView currentLightData;
|
RenderBufferView currentLightData;
|
||||||
|
RenderBufferView currentSkeletalData;
|
||||||
|
|
||||||
auto FlushDrawCall = [&]()
|
auto FlushDrawCall = [&]()
|
||||||
{
|
{
|
||||||
|
|
@ -95,6 +96,12 @@ namespace Nz
|
||||||
currentLightData = renderState.lightData;
|
currentLightData = renderState.lightData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentSkeletalData != renderState.skeletalData)
|
||||||
|
{
|
||||||
|
FlushDrawData();
|
||||||
|
currentSkeletalData = renderState.skeletalData;
|
||||||
|
}
|
||||||
|
|
||||||
const Recti& scissorBox = submesh.GetScissorBox();
|
const Recti& scissorBox = submesh.GetScissorBox();
|
||||||
const Recti& targetScissorBox = (scissorBox.width >= 0) ? scissorBox : invalidScissorBox;
|
const Recti& targetScissorBox = (scissorBox.width >= 0) ? scissorBox : invalidScissorBox;
|
||||||
if (currentScissorBox != targetScissorBox)
|
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)
|
if (std::size_t bindingIndex = matSettings->GetPredefinedBinding(PredefinedShaderBinding::ViewerDataUbo); bindingIndex != MaterialSettings::InvalidIndex)
|
||||||
{
|
{
|
||||||
const auto& viewerBuffer = viewerInstance.GetViewerBuffer();
|
const auto& viewerBuffer = viewerInstance.GetViewerBuffer();
|
||||||
|
|
|
||||||
|
|
@ -1067,33 +1067,43 @@ namespace Nz
|
||||||
|
|
||||||
void SkinPosition(const SkinningData& skinningInfos, UInt64 startVertex, UInt64 vertexCount)
|
void SkinPosition(const SkinningData& skinningInfos, UInt64 startVertex, UInt64 vertexCount)
|
||||||
{
|
{
|
||||||
const SkeletalMeshVertex* inputVertex = &skinningInfos.inputVertex[startVertex];
|
NazaraAssert(skinningInfos.inputJointIndices, "missing input joint indices");
|
||||||
MeshVertex* outputVertex = &skinningInfos.outputVertex[startVertex];
|
NazaraAssert(skinningInfos.inputJointWeights, "missing input joint weights");
|
||||||
|
|
||||||
UInt64 endVertex = startVertex + vertexCount - 1;
|
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());
|
Vector3f finalPosition(Vector3f::Zero());
|
||||||
mat *= inputVertex->weights[j];
|
|
||||||
|
|
||||||
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;
|
if (skinningInfos.outputUv)
|
||||||
outputVertex->uv = inputVertex->uv;
|
{
|
||||||
|
NazaraAssert(skinningInfos.inputUv, "missing input uv");
|
||||||
|
|
||||||
inputVertex++;
|
for (UInt64 i = startVertex; i <= endVertex; ++i)
|
||||||
outputVertex++;
|
skinningInfos.outputUv[i] = skinningInfos.inputUv[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkinPositionNormal(const SkinningData& skinningInfos, UInt64 startVertex, UInt64 vertexCount)
|
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];
|
MeshVertex* outputVertex = &skinningInfos.outputVertex[startVertex];
|
||||||
|
|
||||||
UInt64 endVertex = startVertex + vertexCount - 1;
|
UInt64 endVertex = startVertex + vertexCount - 1;
|
||||||
|
|
@ -1102,7 +1112,7 @@ namespace Nz
|
||||||
Vector3f finalPosition(Vector3f::Zero());
|
Vector3f finalPosition(Vector3f::Zero());
|
||||||
Vector3f finalNormal(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());
|
Matrix4f mat(skinningInfos.joints[inputVertex->jointIndexes[j]].GetSkinningMatrix());
|
||||||
mat *= inputVertex->weights[j];
|
mat *= inputVertex->weights[j];
|
||||||
|
|
@ -1119,12 +1129,12 @@ namespace Nz
|
||||||
|
|
||||||
inputVertex++;
|
inputVertex++;
|
||||||
outputVertex++;
|
outputVertex++;
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkinPositionNormalTangent(const SkinningData& skinningInfos, UInt64 startVertex, UInt64 vertexCount)
|
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];
|
MeshVertex* outputVertex = &skinningInfos.outputVertex[startVertex];
|
||||||
|
|
||||||
UInt64 endVertex = startVertex + vertexCount - 1;
|
UInt64 endVertex = startVertex + vertexCount - 1;
|
||||||
|
|
@ -1134,7 +1144,7 @@ namespace Nz
|
||||||
Vector3f finalNormal(Vector3f::Zero());
|
Vector3f finalNormal(Vector3f::Zero());
|
||||||
Vector3f finalTangent(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());
|
Matrix4f mat(skinningInfos.joints[inputVertex->jointIndexes[j]].GetSkinningMatrix());
|
||||||
mat *= inputVertex->weights[j];
|
mat *= inputVertex->weights[j];
|
||||||
|
|
@ -1154,6 +1164,6 @@ namespace Nz
|
||||||
|
|
||||||
inputVertex++;
|
inputVertex++;
|
||||||
outputVertex++;
|
outputVertex++;
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,22 +62,27 @@ namespace Nz
|
||||||
Quaternionf rotationQuat = Quaternionf::RotationBetween(Vector3f::UnitX(), Vector3f::Forward()) *
|
Quaternionf rotationQuat = Quaternionf::RotationBetween(Vector3f::UnitX(), Vector3f::Forward()) *
|
||||||
Quaternionf::RotationBetween(Vector3f::UnitZ(), Vector3f::Up());
|
Quaternionf::RotationBetween(Vector3f::UnitZ(), Vector3f::Up());
|
||||||
|
|
||||||
for (UInt32 i = 0; i < jointCount; ++i)
|
//Matrix4f matrix = Matrix4f::Transform(Nz::Vector3f::Zero(), rotationQuat, Vector3f(1.f / 40.f));
|
||||||
{
|
//matrix *= parameters.matrix;
|
||||||
int parent = joints[i].parent;
|
|
||||||
for (UInt32 j = 0; j < frameCount; ++j)
|
|
||||||
{
|
|
||||||
SequenceJoint& sequenceJoint = sequenceJoints[j*jointCount + i];
|
|
||||||
|
|
||||||
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.position = frames[frameIndex].joints[jointIndex].pos;
|
||||||
sequenceJoint.rotation = frames[j].joints[i].orient;
|
sequenceJoint.rotation = frames[frameIndex].joints[jointIndex].orient;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sequenceJoint.position = rotationQuat * frames[j].joints[i].pos;
|
sequenceJoint.position = rotationQuat * frames[frameIndex].joints[jointIndex].pos;
|
||||||
sequenceJoint.rotation = rotationQuat * frames[j].joints[i].orient;
|
sequenceJoint.rotation = rotationQuat * frames[frameIndex].joints[jointIndex].orient;
|
||||||
}
|
}
|
||||||
|
|
||||||
sequenceJoint.scale.Set(1.f);
|
sequenceJoint.scale.Set(1.f);
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,19 @@ namespace Nz
|
||||||
return nullptr;
|
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
|
// 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 rotationQuat = Quaternionf::RotationBetween(Vector3f::UnitX(), Vector3f::Forward()) *
|
||||||
Quaternionf::RotationBetween(Vector3f::UnitZ(), Vector3f::Up());
|
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));
|
Matrix4f matrix = Matrix4f::Transform(Nz::Vector3f::Zero(), rotationQuat, Vector3f(1.f / 40.f));
|
||||||
matrix *= parameters.matrix;
|
matrix *= parameters.matrix;
|
||||||
|
|
||||||
|
rotationQuat = Quaternionf::Identity();
|
||||||
|
|
||||||
const MD5MeshParser::Joint* joints = parser.GetJoints();
|
const MD5MeshParser::Joint* joints = parser.GetJoints();
|
||||||
const MD5MeshParser::Mesh* meshes = parser.GetMeshes();
|
const MD5MeshParser::Mesh* meshes = parser.GetMeshes();
|
||||||
UInt32 jointCount = parser.GetJointCount();
|
UInt32 jointCount = parser.GetJointCount();
|
||||||
|
|
@ -120,8 +135,12 @@ namespace Nz
|
||||||
|
|
||||||
std::vector<Weight> tempWeights;
|
std::vector<Weight> tempWeights;
|
||||||
|
|
||||||
BufferMapper<VertexBuffer> vertexMapper(*vertexBuffer, 0, vertexBuffer->GetVertexCount());
|
VertexMapper vertexMapper(*vertexBuffer);
|
||||||
SkeletalMeshVertex* vertices = static_cast<SkeletalMeshVertex*>(vertexMapper.GetPointer());
|
|
||||||
|
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)
|
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.
|
// 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);
|
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];
|
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é
|
// 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
|
// 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 ?
|
// Avons nous plus de poids que le moteur ne peut en supporter ?
|
||||||
unsigned int weightCount = vertex.weightCount;
|
UInt32 weightCount = vertex.weightCount;
|
||||||
if (weightCount > NAZARA_UTILITY_SKINNING_MAX_WEIGHTS)
|
if (weightCount > maxWeightCount)
|
||||||
{
|
{
|
||||||
// Pour augmenter la qualité du skinning tout en ne gardant que X poids, on ne garde que les poids
|
// 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
|
// 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)
|
// Sans oublier bien sûr de renormaliser les poids (que leur somme soit 1)
|
||||||
float weightSum = 0.f;
|
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;
|
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;
|
tempWeights[j].bias /= weightSum;
|
||||||
|
|
||||||
weightCount = NAZARA_UTILITY_SKINNING_MAX_WEIGHTS;
|
weightCount = maxWeightCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
vertices->weightCount = weightCount;
|
if (posPtr)
|
||||||
for (unsigned int j = 0; j < NAZARA_UTILITY_SKINNING_MAX_WEIGHTS; ++j)
|
*posPtr++ = /*finalPos * */Vector3f(1.f / 40.f);
|
||||||
|
|
||||||
|
if (uvPtr)
|
||||||
|
*uvPtr++ = Vector2f(parameters.texCoordOffset + vertex.uv * parameters.texCoordScale);
|
||||||
|
|
||||||
|
if (jointIndicesPtr)
|
||||||
{
|
{
|
||||||
if (j < weightCount)
|
Vector4i32& jointIndices = *jointIndicesPtr++;
|
||||||
{
|
|
||||||
// On donne une valeur aux poids présents
|
for (UInt32 j = 0; j < maxWeightCount; ++j)
|
||||||
vertices->weights[j] = tempWeights[j].bias;
|
jointIndices[j] = (j < weightCount) ? tempWeights[j].jointIndex : 0;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vertices->position = finalPos;
|
if (jointWeightPtr)
|
||||||
vertices->uv.Set(parameters.texCoordOffset + vertex.uv * parameters.texCoordScale);
|
{
|
||||||
vertices++;
|
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();
|
vertexMapper.Unmap();
|
||||||
|
|
@ -196,7 +222,15 @@ namespace Nz
|
||||||
|
|
||||||
// Submesh
|
// Submesh
|
||||||
std::shared_ptr<SkeletalMesh> subMesh = std::make_shared<SkeletalMesh>(vertexBuffer, indexBuffer);
|
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);
|
subMesh->SetMaterialIndex(i);
|
||||||
|
|
||||||
mesh->AddSubMesh(subMesh);
|
mesh->AddSubMesh(subMesh);
|
||||||
|
|
|
||||||
|
|
@ -280,19 +280,14 @@ namespace Nz
|
||||||
0
|
0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
VertexComponent::Userdata,
|
VertexComponent::JointWeights,
|
||||||
ComponentType::Int1,
|
|
||||||
0 // Weight count
|
|
||||||
},
|
|
||||||
{
|
|
||||||
VertexComponent::Userdata,
|
|
||||||
ComponentType::Float4,
|
ComponentType::Float4,
|
||||||
1 // Weights
|
0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
VertexComponent::Userdata,
|
VertexComponent::JointIndices,
|
||||||
ComponentType::Int4,
|
ComponentType::Int4,
|
||||||
2 // Joint indexes
|
0
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue