mirror of
https://github.com/luanti-org/luanti.git
synced 2025-12-21 06:05:33 +01:00
Refactor skinned mesh weight data structure (#16655)
This commit is contained in:
@@ -514,10 +514,6 @@ void AnimatedMeshSceneNode::setMesh(IAnimatedMesh *mesh)
|
||||
JointsUsed = false;
|
||||
checkJoints();
|
||||
}
|
||||
|
||||
// get start and begin time
|
||||
setAnimationSpeed(Mesh->getAnimationSpeed()); // NOTE: This had been commented out (but not removed!) in r3526. Which caused meshloader-values for speed to be ignored unless users specified explicitly. Missing a test-case where this could go wrong so I put the code back in.
|
||||
setFrameLoop(0, Mesh->getMaxFrameNumber());
|
||||
}
|
||||
|
||||
//! updates the absolute position based on the relative and the parents position
|
||||
|
||||
@@ -25,8 +25,11 @@ namespace scene
|
||||
|
||||
//! Constructor
|
||||
CB3DMeshFileLoader::CB3DMeshFileLoader(scene::ISceneManager *smgr) :
|
||||
AnimatedMesh(0), B3DFile(0), VerticesStart(0), NormalsInFile(false),
|
||||
HasVertexColors(false), ShowWarning(true)
|
||||
B3DFile(nullptr),
|
||||
VerticesStart(0),
|
||||
NormalsInFile(false),
|
||||
HasVertexColors(false),
|
||||
ShowWarning(true)
|
||||
{}
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
@@ -43,21 +46,17 @@ bool CB3DMeshFileLoader::isALoadableFileExtension(const io::path &filename) cons
|
||||
IAnimatedMesh *CB3DMeshFileLoader::createMesh(io::IReadFile *file)
|
||||
{
|
||||
if (!file)
|
||||
return 0;
|
||||
return nullptr;
|
||||
|
||||
B3DFile = file;
|
||||
AnimatedMesh = new scene::SkinnedMeshBuilder(SkinnedMesh::SourceFormat::B3D);
|
||||
AnimatedMesh = scene::SkinnedMeshBuilder(SkinnedMesh::SourceFormat::B3D);
|
||||
ShowWarning = true; // If true a warning is issued if too many textures are used
|
||||
VerticesStart = 0;
|
||||
|
||||
if (load()) {
|
||||
return AnimatedMesh->finalize();
|
||||
} else {
|
||||
AnimatedMesh->drop();
|
||||
AnimatedMesh = 0;
|
||||
}
|
||||
if (!load())
|
||||
return nullptr;
|
||||
|
||||
return AnimatedMesh;
|
||||
return std::move(AnimatedMesh).finalize();
|
||||
}
|
||||
|
||||
bool CB3DMeshFileLoader::load()
|
||||
@@ -130,7 +129,7 @@ bool CB3DMeshFileLoader::load()
|
||||
|
||||
bool CB3DMeshFileLoader::readChunkNODE(SkinnedMesh::SJoint *inJoint)
|
||||
{
|
||||
SkinnedMesh::SJoint *joint = AnimatedMesh->addJoint(inJoint);
|
||||
SkinnedMesh::SJoint *joint = AnimatedMesh.addJoint(inJoint);
|
||||
joint->Name = readString();
|
||||
|
||||
#ifdef _B3D_READER_DEBUG
|
||||
@@ -233,7 +232,7 @@ bool CB3DMeshFileLoader::readChunkMESH(SkinnedMesh::SJoint *inJoint)
|
||||
if (!readChunkVRTS(inJoint))
|
||||
return false;
|
||||
} else if (strncmp(B3dStack.getLast().name, "TRIS", 4) == 0) {
|
||||
scene::SSkinMeshBuffer *meshBuffer = AnimatedMesh->addMeshBuffer();
|
||||
scene::SSkinMeshBuffer *meshBuffer = AnimatedMesh.addMeshBuffer();
|
||||
|
||||
if (brushID == -1) { /* ok */
|
||||
} else if (brushID < 0 || (u32)brushID >= Materials.size()) {
|
||||
@@ -243,7 +242,7 @@ bool CB3DMeshFileLoader::readChunkMESH(SkinnedMesh::SJoint *inJoint)
|
||||
meshBuffer->Material = Materials[brushID].Material;
|
||||
}
|
||||
|
||||
if (readChunkTRIS(meshBuffer, AnimatedMesh->getMeshBufferCount() - 1, VerticesStart) == false)
|
||||
if (readChunkTRIS(meshBuffer, AnimatedMesh.getMeshBufferCount() - 1, VerticesStart) == false)
|
||||
return false;
|
||||
|
||||
if (!NormalsInFile) {
|
||||
@@ -541,11 +540,10 @@ bool CB3DMeshFileLoader::readChunkBONE(SkinnedMesh::SJoint *inJoint)
|
||||
if (AnimatedVertices_VertexID[globalVertexID] == -1) {
|
||||
os::Printer::log("B3dMeshLoader: Weight has bad vertex id (no link to meshbuffer index found)");
|
||||
} else if (strength > 0) {
|
||||
SkinnedMesh::SWeight *weight = AnimatedMesh->addWeight(inJoint);
|
||||
weight->strength = strength;
|
||||
// Find the meshbuffer and Vertex index from the Global Vertex ID:
|
||||
weight->vertex_id = AnimatedVertices_VertexID[globalVertexID];
|
||||
weight->buffer_id = AnimatedVertices_BufferID[globalVertexID];
|
||||
AnimatedMesh.addWeight(inJoint,
|
||||
AnimatedVertices_BufferID[globalVertexID],
|
||||
AnimatedVertices_VertexID[globalVertexID],
|
||||
strength);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -591,15 +589,15 @@ bool CB3DMeshFileLoader::readChunkKEYS(SkinnedMesh::SJoint *inJoint)
|
||||
f32 data[4];
|
||||
if (flags & 1) {
|
||||
readFloats(data, 3);
|
||||
AnimatedMesh->addPositionKey(inJoint, frame - 1, {data[0], data[1], data[2]});
|
||||
AnimatedMesh.addPositionKey(inJoint, frame - 1, {data[0], data[1], data[2]});
|
||||
}
|
||||
if (flags & 2) {
|
||||
readFloats(data, 3);
|
||||
AnimatedMesh->addScaleKey(inJoint, frame - 1, {data[0], data[1], data[2]});
|
||||
AnimatedMesh.addScaleKey(inJoint, frame - 1, {data[0], data[1], data[2]});
|
||||
}
|
||||
if (flags & 4) {
|
||||
readFloats(data, 4);
|
||||
AnimatedMesh->addRotationKey(inJoint, frame - 1, core::quaternion(data[1], data[2], data[3], data[0]));
|
||||
AnimatedMesh.addRotationKey(inJoint, frame - 1, core::quaternion(data[1], data[2], data[3], data[0]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -617,15 +615,13 @@ bool CB3DMeshFileLoader::readChunkANIM()
|
||||
os::Printer::log(logStr.c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
|
||||
s32 animFlags; // not stored\used
|
||||
s32 animFrames; // not stored\used
|
||||
f32 animFPS; // not stored\used
|
||||
s32 animFlags; // not stored/used
|
||||
s32 animFrames; // not stored/used
|
||||
f32 animFPS; // not stored/used
|
||||
|
||||
B3DFile->read(&animFlags, sizeof(s32));
|
||||
B3DFile->read(&animFrames, sizeof(s32));
|
||||
readFloats(&animFPS, 1);
|
||||
if (animFPS > 0.f)
|
||||
AnimatedMesh->setAnimationSpeed(animFPS);
|
||||
os::Printer::log("FPS", io::path((double)animFPS), ELL_DEBUG);
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
|
||||
@@ -60,7 +60,7 @@ private:
|
||||
|
||||
core::array<video::S3DVertex2TCoords> BaseVertices;
|
||||
|
||||
SkinnedMeshBuilder *AnimatedMesh;
|
||||
SkinnedMeshBuilder AnimatedMesh;
|
||||
io::IReadFile *B3DFile;
|
||||
|
||||
// B3Ds have Vertex ID's local within the mesh I don't want this
|
||||
|
||||
@@ -345,15 +345,14 @@ IAnimatedMesh* SelfType::createMesh(io::IReadFile* file)
|
||||
const char *filename = file->getFileName().c_str();
|
||||
try {
|
||||
tiniergltf::GlTF model = parseGLTF(file);
|
||||
irr_ptr<SkinnedMeshBuilder> mesh(new SkinnedMeshBuilder(
|
||||
SkinnedMesh::SourceFormat::GLTF));
|
||||
MeshExtractor extractor(std::move(model), mesh.get());
|
||||
SkinnedMeshBuilder mesh(SkinnedMesh::SourceFormat::GLTF);
|
||||
MeshExtractor extractor(std::move(model));
|
||||
try {
|
||||
extractor.load();
|
||||
auto *res = extractor.load();
|
||||
for (const auto &warning : extractor.getWarnings()) {
|
||||
os::Printer::log(filename, warning.c_str(), ELL_WARNING);
|
||||
}
|
||||
return mesh.release()->finalize();
|
||||
return res;
|
||||
} catch (const std::runtime_error &e) {
|
||||
os::Printer::log("error converting gltf to irrlicht mesh", e.what(), ELL_ERROR);
|
||||
}
|
||||
@@ -423,15 +422,14 @@ void SelfType::MeshExtractor::addPrimitive(
|
||||
}
|
||||
|
||||
auto *meshbuf = new SSkinMeshBuffer(std::move(*vertices), std::move(indices));
|
||||
m_irr_model->addMeshBuffer(meshbuf);
|
||||
const auto meshbufNr = m_irr_model->getMeshBufferCount() - 1;
|
||||
const auto meshbufNr = m_irr_model.addMeshBuffer(meshbuf);
|
||||
|
||||
if (primitive.material.has_value()) {
|
||||
const auto &material = m_gltf_model.materials->at(*primitive.material);
|
||||
if (material.pbrMetallicRoughness.has_value()) {
|
||||
const auto &texture = material.pbrMetallicRoughness->baseColorTexture;
|
||||
if (texture.has_value()) {
|
||||
m_irr_model->setTextureSlot(meshbufNr, static_cast<u32>(texture->index));
|
||||
m_irr_model.setTextureSlot(meshbufNr, static_cast<u32>(texture->index));
|
||||
const auto samplerIdx = m_gltf_model.textures->at(texture->index).sampler;
|
||||
if (samplerIdx.has_value()) {
|
||||
auto &sampler = m_gltf_model.samplers->at(*samplerIdx);
|
||||
@@ -501,10 +499,8 @@ void SelfType::MeshExtractor::addPrimitive(
|
||||
if (strength <= 0)
|
||||
continue; // note: also ignores negative weights
|
||||
|
||||
SkinnedMesh::SWeight *weight = m_irr_model->addWeight(m_loaded_nodes.at(skin.joints.at(jointIdx)));
|
||||
weight->buffer_id = meshbufNr;
|
||||
weight->vertex_id = v;
|
||||
weight->strength = strength;
|
||||
m_irr_model.addWeight(m_loaded_nodes.at(skin.joints.at(jointIdx)),
|
||||
meshbufNr, v, strength);
|
||||
}
|
||||
}
|
||||
if (negative_weights)
|
||||
@@ -571,7 +567,7 @@ void SelfType::MeshExtractor::loadNode(
|
||||
SkinnedMesh::SJoint *parent)
|
||||
{
|
||||
const auto &node = m_gltf_model.nodes->at(nodeIdx);
|
||||
auto *joint = m_irr_model->addJoint(parent);
|
||||
auto *joint = m_irr_model.addJoint(parent);
|
||||
const core::matrix4 transform = loadTransform(node.transform, joint);
|
||||
joint->GlobalMatrix = parent ? parent->GlobalMatrix * transform : transform;
|
||||
if (node.name.has_value()) {
|
||||
@@ -695,7 +691,7 @@ void SelfType::MeshExtractor::loadAnimation(const std::size_t animIdx)
|
||||
}
|
||||
}
|
||||
|
||||
void SelfType::MeshExtractor::load()
|
||||
SkinnedMesh *SelfType::MeshExtractor::load()
|
||||
{
|
||||
if (m_gltf_model.extensionsRequired)
|
||||
throw std::runtime_error("model requires extensions, but we support none");
|
||||
@@ -723,8 +719,8 @@ void SelfType::MeshExtractor::load()
|
||||
warn("multiple animations are not supported");
|
||||
|
||||
loadAnimation(0);
|
||||
m_irr_model->setAnimationSpeed(1);
|
||||
}
|
||||
return std::move(m_irr_model).finalize();
|
||||
} catch (const std::out_of_range &e) {
|
||||
throw std::runtime_error(e.what());
|
||||
} catch (const std::bad_optional_access &e) {
|
||||
|
||||
@@ -96,9 +96,10 @@ private:
|
||||
class MeshExtractor
|
||||
{
|
||||
public:
|
||||
MeshExtractor(tiniergltf::GlTF &&model,
|
||||
SkinnedMeshBuilder *mesh) noexcept
|
||||
: m_gltf_model(std::move(model)), m_irr_model(mesh) {};
|
||||
MeshExtractor(tiniergltf::GlTF &&model) noexcept
|
||||
: m_gltf_model(std::move(model))
|
||||
, m_irr_model(SkinnedMesh::SourceFormat::GLTF)
|
||||
{}
|
||||
|
||||
/* Gets indices for the given mesh/primitive.
|
||||
*
|
||||
@@ -114,14 +115,14 @@ private:
|
||||
|
||||
std::size_t getPrimitiveCount(const std::size_t meshIdx) const;
|
||||
|
||||
void load();
|
||||
SkinnedMesh *load();
|
||||
const std::unordered_set<std::string> &getWarnings() {
|
||||
return warnings;
|
||||
}
|
||||
|
||||
private:
|
||||
const tiniergltf::GlTF m_gltf_model;
|
||||
SkinnedMeshBuilder *m_irr_model;
|
||||
SkinnedMeshBuilder m_irr_model;
|
||||
|
||||
std::vector<std::function<void()>> m_mesh_loaders;
|
||||
std::vector<SkinnedMesh::SJoint *> m_loaded_nodes;
|
||||
|
||||
@@ -273,6 +273,7 @@ set(IRRMESHLOADER
|
||||
add_library(IRRMESHOBJ OBJECT
|
||||
CMeshSceneNode.h
|
||||
|
||||
WeightBuffer.cpp
|
||||
SkinnedMesh.cpp
|
||||
CMeshSceneNode.cpp
|
||||
AnimatedMeshSceneNode.cpp
|
||||
|
||||
@@ -110,7 +110,7 @@ void CMeshManipulator::recalculateNormals(scene::IMesh *mesh, bool smooth, bool
|
||||
|
||||
if (mesh->getMeshType() == EAMT_SKINNED) {
|
||||
auto *smesh = (SkinnedMesh *)mesh;
|
||||
smesh->refreshJointCache();
|
||||
smesh->updateStaticPose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include "CXMeshFileLoader.h"
|
||||
#include "SkinnedMesh.h"
|
||||
#include "Transform.h"
|
||||
#include "os.h"
|
||||
|
||||
#include "fast_atof.h"
|
||||
@@ -16,7 +15,6 @@
|
||||
#ifdef _DEBUG
|
||||
#define _XREADER_DEBUG
|
||||
#endif
|
||||
// #define BETTER_MESHBUFFER_SPLITTING_FOR_X
|
||||
|
||||
#define SET_ERR_AND_RETURN() \
|
||||
do { \
|
||||
@@ -29,8 +27,17 @@ namespace scene
|
||||
|
||||
//! Constructor
|
||||
CXMeshFileLoader::CXMeshFileLoader(scene::ISceneManager *smgr) :
|
||||
AnimatedMesh(0), Buffer(0), P(0), End(0), BinaryNumCount(0), Line(0), ErrorState(false),
|
||||
CurFrame(0), MajorVersion(0), MinorVersion(0), BinaryFormat(false), FloatSize(0)
|
||||
Buffer(nullptr),
|
||||
P(nullptr),
|
||||
End(nullptr),
|
||||
BinaryNumCount(0),
|
||||
Line(0),
|
||||
ErrorState(false),
|
||||
CurFrame(nullptr),
|
||||
MajorVersion(0),
|
||||
MinorVersion(0),
|
||||
BinaryFormat(false),
|
||||
FloatSize(0)
|
||||
{}
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
@@ -47,20 +54,17 @@ bool CXMeshFileLoader::isALoadableFileExtension(const io::path &filename) const
|
||||
IAnimatedMesh *CXMeshFileLoader::createMesh(io::IReadFile *file)
|
||||
{
|
||||
if (!file)
|
||||
return 0;
|
||||
return nullptr;
|
||||
|
||||
#ifdef _XREADER_DEBUG
|
||||
u32 time = os::Timer::getRealTime();
|
||||
#endif
|
||||
|
||||
AnimatedMesh = new SkinnedMeshBuilder(SkinnedMesh::SourceFormat::X);
|
||||
AnimatedMesh = SkinnedMeshBuilder(SkinnedMesh::SourceFormat::X);
|
||||
|
||||
SkinnedMesh *res = nullptr;
|
||||
if (load(file)) {
|
||||
res = AnimatedMesh->finalize();
|
||||
} else {
|
||||
AnimatedMesh->drop();
|
||||
AnimatedMesh = 0;
|
||||
res = std::move(AnimatedMesh).finalize();
|
||||
}
|
||||
#ifdef _XREADER_DEBUG
|
||||
time = os::Timer::getRealTime() - time;
|
||||
@@ -71,7 +75,7 @@ IAnimatedMesh *CXMeshFileLoader::createMesh(io::IReadFile *file)
|
||||
tmpString += "ms";
|
||||
os::Printer::log(tmpString.c_str());
|
||||
#endif
|
||||
// Clear up
|
||||
// Clean up
|
||||
|
||||
MajorVersion = 0;
|
||||
MinorVersion = 0;
|
||||
@@ -111,17 +115,15 @@ bool CXMeshFileLoader::load(io::IReadFile *file)
|
||||
u32 i;
|
||||
|
||||
mesh->Buffers.reallocate(mesh->Materials.size());
|
||||
#ifndef BETTER_MESHBUFFER_SPLITTING_FOR_X
|
||||
const u32 bufferOffset = AnimatedMesh->getMeshBufferCount();
|
||||
#endif
|
||||
const u32 bufferOffset = AnimatedMesh.getMeshBufferCount();
|
||||
for (i = 0; i < mesh->Materials.size(); ++i) {
|
||||
mesh->Buffers.push_back(AnimatedMesh->addMeshBuffer());
|
||||
mesh->Buffers.push_back(AnimatedMesh.addMeshBuffer());
|
||||
mesh->Buffers.getLast()->Material = mesh->Materials[i];
|
||||
|
||||
if (!mesh->HasSkinning) {
|
||||
// Set up rigid animation
|
||||
if (mesh->AttachedJointID != -1) {
|
||||
AnimatedMesh->getAllJoints()[mesh->AttachedJointID]->AttachedMeshes.push_back(AnimatedMesh->getMeshBufferCount() - 1);
|
||||
AnimatedMesh.getJoints()[mesh->AttachedJointID]->AttachedMeshes.push_back(AnimatedMesh.getMeshBufferCount() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,192 +142,103 @@ bool CXMeshFileLoader::load(io::IReadFile *file)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef BETTER_MESHBUFFER_SPLITTING_FOR_X
|
||||
{
|
||||
// the same vertex can be used in many different meshbuffers, but it's slow to work out
|
||||
core::array<u32> verticesLinkIndex;
|
||||
core::array<s16> verticesLinkBuffer;
|
||||
verticesLinkBuffer.set_used(mesh->Vertices.size());
|
||||
|
||||
core::array<core::array<u32>> verticesLinkIndex;
|
||||
verticesLinkIndex.reallocate(mesh->Vertices.size());
|
||||
core::array<core::array<u16>> verticesLinkBuffer;
|
||||
verticesLinkBuffer.reallocate(mesh->Vertices.size());
|
||||
// init with 0
|
||||
for (i = 0; i < mesh->Vertices.size(); ++i) {
|
||||
// watch out for vertices which are not part of the mesh
|
||||
// they will keep the -1 and can lead to out-of-bounds access
|
||||
verticesLinkBuffer[i] = -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < mesh->Vertices.size(); ++i) {
|
||||
verticesLinkIndex.push_back(core::array<u32>());
|
||||
verticesLinkBuffer.push_back(core::array<u16>());
|
||||
}
|
||||
|
||||
for (i = 0; i < mesh->FaceMaterialIndices.size(); ++i) {
|
||||
for (u32 id = i * 3 + 0; id <= i * 3 + 2; ++id) {
|
||||
core::array<u16> &Array = verticesLinkBuffer[mesh->Indices[id]];
|
||||
bool found = false;
|
||||
|
||||
for (u32 j = 0; j < Array.size(); ++j) {
|
||||
if (Array[j] == mesh->FaceMaterialIndices[i]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
bool warned = false;
|
||||
// store meshbuffer number per vertex
|
||||
for (i = 0; i < mesh->FaceMaterialIndices.size(); ++i) {
|
||||
for (u32 id = i * 3 + 0; id <= i * 3 + 2; ++id) {
|
||||
if ((verticesLinkBuffer[mesh->Indices[id]] != -1) && (verticesLinkBuffer[mesh->Indices[id]] != (s16)mesh->FaceMaterialIndices[i])) {
|
||||
if (!warned) {
|
||||
os::Printer::log("X loader", "Duplicated vertex, animation might be corrupted.", ELL_WARNING);
|
||||
warned = true;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
Array.push_back(mesh->FaceMaterialIndices[i]);
|
||||
const u32 tmp = mesh->Vertices.size();
|
||||
mesh->Vertices.push_back(mesh->Vertices[mesh->Indices[id]]);
|
||||
mesh->Indices[id] = tmp;
|
||||
verticesLinkBuffer.set_used(mesh->Vertices.size());
|
||||
}
|
||||
verticesLinkBuffer[mesh->Indices[id]] = mesh->FaceMaterialIndices[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < verticesLinkBuffer.size(); ++i) {
|
||||
if (!verticesLinkBuffer[i].size())
|
||||
verticesLinkBuffer[i].push_back(0);
|
||||
}
|
||||
|
||||
if (mesh->FaceMaterialIndices.size() != 0) {
|
||||
// store vertices in buffers and remember relation in verticesLinkIndex
|
||||
u32 *vCountArray = new u32[mesh->Buffers.size()];
|
||||
memset(vCountArray, 0, mesh->Buffers.size() * sizeof(u32));
|
||||
// count vertices in each buffer and reallocate
|
||||
for (i = 0; i < mesh->Vertices.size(); ++i) {
|
||||
core::array<u16> &Array = verticesLinkBuffer[i];
|
||||
verticesLinkIndex[i].reallocate(Array.size());
|
||||
for (u32 j = 0; j < Array.size(); ++j) {
|
||||
scene::SSkinMeshBuffer *buffer = mesh->Buffers[Array[j]];
|
||||
verticesLinkIndex[i].push_back(buffer->Vertices_Standard.size());
|
||||
buffer->Vertices_Standard.push_back(mesh->Vertices[i]);
|
||||
if (verticesLinkBuffer[i] != -1)
|
||||
++vCountArray[verticesLinkBuffer[i]];
|
||||
}
|
||||
if (mesh->TCoords2.size()) {
|
||||
for (i = 0; i != mesh->Buffers.size(); ++i) {
|
||||
mesh->Buffers[i]->Vertices_2TCoords->Data.reserve(vCountArray[i]);
|
||||
mesh->Buffers[i]->VertexType = video::EVT_2TCOORDS;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i != mesh->Buffers.size(); ++i)
|
||||
mesh->Buffers[i]->Vertices_Standard->Data.reserve(vCountArray[i]);
|
||||
}
|
||||
|
||||
verticesLinkIndex.set_used(mesh->Vertices.size());
|
||||
// actually store vertices
|
||||
for (i = 0; i < mesh->Vertices.size(); ++i) {
|
||||
// if a vertex is missing for some reason, just skip it
|
||||
if (verticesLinkBuffer[i] == -1)
|
||||
continue;
|
||||
scene::SSkinMeshBuffer *buffer = mesh->Buffers[verticesLinkBuffer[i]];
|
||||
|
||||
if (mesh->TCoords2.size()) {
|
||||
verticesLinkIndex[i] = buffer->Vertices_2TCoords->getCount();
|
||||
buffer->Vertices_2TCoords->Data.emplace_back(mesh->Vertices[i]);
|
||||
// We have a problem with correct tcoord2 handling here
|
||||
// crash fixed for now by checking the values
|
||||
buffer->Vertices_2TCoords->Data.back().TCoords2 = (i < mesh->TCoords2.size()) ? mesh->TCoords2[i] : mesh->Vertices[i].TCoords;
|
||||
} else {
|
||||
verticesLinkIndex[i] = buffer->Vertices_Standard->getCount();
|
||||
buffer->Vertices_Standard->Data.push_back(mesh->Vertices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// count indices per buffer and reallocate
|
||||
memset(vCountArray, 0, mesh->Buffers.size() * sizeof(u32));
|
||||
for (i = 0; i < mesh->FaceMaterialIndices.size(); ++i)
|
||||
++vCountArray[mesh->FaceMaterialIndices[i]];
|
||||
for (i = 0; i != mesh->Buffers.size(); ++i)
|
||||
mesh->Buffers[i]->Indices->Data.reserve(vCountArray[i]);
|
||||
delete[] vCountArray;
|
||||
// create indices per buffer
|
||||
for (i = 0; i < mesh->FaceMaterialIndices.size(); ++i) {
|
||||
scene::SSkinMeshBuffer *buffer = mesh->Buffers[mesh->FaceMaterialIndices[i]];
|
||||
|
||||
for (u32 id = i * 3 + 0; id <= i * 3 + 2; ++id) {
|
||||
core::array<u16> &Array = verticesLinkBuffer[mesh->Indices[id]];
|
||||
|
||||
for (u32 j = 0; j < Array.size(); ++j) {
|
||||
if (Array[j] == mesh->FaceMaterialIndices[i])
|
||||
buffer->Indices.push_back(verticesLinkIndex[mesh->Indices[id]][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 j = 0; j < mesh->WeightJoint.size(); ++j) {
|
||||
SkinnedMesh::SJoint *joint = AnimatedMesh->getAllJoints()[mesh->WeightJoint[j]];
|
||||
SkinnedMesh::SWeight &weight = joint->Weights[mesh->WeightNum[j]];
|
||||
|
||||
u32 id = weight.vertex_id;
|
||||
|
||||
if (id >= verticesLinkIndex.size()) {
|
||||
os::Printer::log("X loader: Weight id out of range", ELL_WARNING);
|
||||
id = 0;
|
||||
weight.strength = 0.f;
|
||||
}
|
||||
|
||||
if (verticesLinkBuffer[id].size() == 1) {
|
||||
weight.vertex_id = verticesLinkIndex[id][0];
|
||||
weight.buffer_id = verticesLinkBuffer[id][0];
|
||||
} else if (verticesLinkBuffer[id].size() != 0) {
|
||||
for (u32 k = 1; k < verticesLinkBuffer[id].size(); ++k) {
|
||||
SkinnedMesh::SWeight *WeightClone = AnimatedMesh->addWeight(joint);
|
||||
WeightClone->strength = weight.strength;
|
||||
WeightClone->vertex_id = verticesLinkIndex[id][k];
|
||||
WeightClone->buffer_id = verticesLinkBuffer[id][k];
|
||||
}
|
||||
for (u32 id = i * 3 + 0; id != i * 3 + 3; ++id) {
|
||||
buffer->Indices->Data.push_back(verticesLinkIndex[mesh->Indices[id]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
core::array<u32> verticesLinkIndex;
|
||||
core::array<s16> verticesLinkBuffer;
|
||||
verticesLinkBuffer.set_used(mesh->Vertices.size());
|
||||
|
||||
// init with 0
|
||||
for (i = 0; i < mesh->Vertices.size(); ++i) {
|
||||
// watch out for vertices which are not part of the mesh
|
||||
// they will keep the -1 and can lead to out-of-bounds access
|
||||
verticesLinkBuffer[i] = -1;
|
||||
for (const auto &weight : mesh->Weights) {
|
||||
u32 id = weight.global_vertex_id;
|
||||
|
||||
if (id >= verticesLinkIndex.size()) {
|
||||
os::Printer::log("X loader: Weight id out of range", ELL_WARNING);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool warned = false;
|
||||
// store meshbuffer number per vertex
|
||||
for (i = 0; i < mesh->FaceMaterialIndices.size(); ++i) {
|
||||
for (u32 id = i * 3 + 0; id <= i * 3 + 2; ++id) {
|
||||
if ((verticesLinkBuffer[mesh->Indices[id]] != -1) && (verticesLinkBuffer[mesh->Indices[id]] != (s16)mesh->FaceMaterialIndices[i])) {
|
||||
if (!warned) {
|
||||
os::Printer::log("X loader", "Duplicated vertex, animation might be corrupted.", ELL_WARNING);
|
||||
warned = true;
|
||||
}
|
||||
const u32 tmp = mesh->Vertices.size();
|
||||
mesh->Vertices.push_back(mesh->Vertices[mesh->Indices[id]]);
|
||||
mesh->Indices[id] = tmp;
|
||||
verticesLinkBuffer.set_used(mesh->Vertices.size());
|
||||
}
|
||||
verticesLinkBuffer[mesh->Indices[id]] = mesh->FaceMaterialIndices[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (mesh->FaceMaterialIndices.size() != 0) {
|
||||
// store vertices in buffers and remember relation in verticesLinkIndex
|
||||
u32 *vCountArray = new u32[mesh->Buffers.size()];
|
||||
memset(vCountArray, 0, mesh->Buffers.size() * sizeof(u32));
|
||||
// count vertices in each buffer and reallocate
|
||||
for (i = 0; i < mesh->Vertices.size(); ++i) {
|
||||
if (verticesLinkBuffer[i] != -1)
|
||||
++vCountArray[verticesLinkBuffer[i]];
|
||||
}
|
||||
if (mesh->TCoords2.size()) {
|
||||
for (i = 0; i != mesh->Buffers.size(); ++i) {
|
||||
mesh->Buffers[i]->Vertices_2TCoords->Data.reserve(vCountArray[i]);
|
||||
mesh->Buffers[i]->VertexType = video::EVT_2TCOORDS;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i != mesh->Buffers.size(); ++i)
|
||||
mesh->Buffers[i]->Vertices_Standard->Data.reserve(vCountArray[i]);
|
||||
}
|
||||
|
||||
verticesLinkIndex.set_used(mesh->Vertices.size());
|
||||
// actually store vertices
|
||||
for (i = 0; i < mesh->Vertices.size(); ++i) {
|
||||
// if a vertex is missing for some reason, just skip it
|
||||
if (verticesLinkBuffer[i] == -1)
|
||||
continue;
|
||||
scene::SSkinMeshBuffer *buffer = mesh->Buffers[verticesLinkBuffer[i]];
|
||||
|
||||
if (mesh->TCoords2.size()) {
|
||||
verticesLinkIndex[i] = buffer->Vertices_2TCoords->getCount();
|
||||
buffer->Vertices_2TCoords->Data.emplace_back(mesh->Vertices[i]);
|
||||
// We have a problem with correct tcoord2 handling here
|
||||
// crash fixed for now by checking the values
|
||||
buffer->Vertices_2TCoords->Data.back().TCoords2 = (i < mesh->TCoords2.size()) ? mesh->TCoords2[i] : mesh->Vertices[i].TCoords;
|
||||
} else {
|
||||
verticesLinkIndex[i] = buffer->Vertices_Standard->getCount();
|
||||
buffer->Vertices_Standard->Data.push_back(mesh->Vertices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// count indices per buffer and reallocate
|
||||
memset(vCountArray, 0, mesh->Buffers.size() * sizeof(u32));
|
||||
for (i = 0; i < mesh->FaceMaterialIndices.size(); ++i)
|
||||
++vCountArray[mesh->FaceMaterialIndices[i]];
|
||||
for (i = 0; i != mesh->Buffers.size(); ++i)
|
||||
mesh->Buffers[i]->Indices->Data.reserve(vCountArray[i]);
|
||||
delete[] vCountArray;
|
||||
// create indices per buffer
|
||||
for (i = 0; i < mesh->FaceMaterialIndices.size(); ++i) {
|
||||
scene::SSkinMeshBuffer *buffer = mesh->Buffers[mesh->FaceMaterialIndices[i]];
|
||||
for (u32 id = i * 3 + 0; id != i * 3 + 3; ++id) {
|
||||
buffer->Indices->Data.push_back(verticesLinkIndex[mesh->Indices[id]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 j = 0; j < mesh->WeightJoint.size(); ++j) {
|
||||
SkinnedMesh::SWeight &weight = (AnimatedMesh->getAllJoints()[mesh->WeightJoint[j]]->Weights[mesh->WeightNum[j]]);
|
||||
|
||||
u32 id = weight.vertex_id;
|
||||
|
||||
if (id >= verticesLinkIndex.size()) {
|
||||
os::Printer::log("X loader: Weight id out of range", ELL_WARNING);
|
||||
id = 0;
|
||||
weight.strength = 0.f;
|
||||
}
|
||||
|
||||
weight.vertex_id = verticesLinkIndex[id];
|
||||
weight.buffer_id = verticesLinkBuffer[id] + bufferOffset;
|
||||
}
|
||||
u16 buf_id = verticesLinkBuffer[id] + bufferOffset;
|
||||
u32 vert_id = verticesLinkIndex[id];
|
||||
auto *joint = AnimatedMesh.getJoints()[weight.joint_id];
|
||||
AnimatedMesh.addWeight(joint, buf_id, vert_id, weight.strength);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -508,10 +421,10 @@ bool CXMeshFileLoader::parseDataObjectFrame(SkinnedMesh::SJoint *Parent)
|
||||
SkinnedMesh::SJoint *joint = 0;
|
||||
|
||||
if (name.size()) {
|
||||
auto n = AnimatedMesh->getJointNumber(name.c_str());
|
||||
auto n = AnimatedMesh.getJointNumber(name.c_str());
|
||||
if (n.has_value()) {
|
||||
JointID = *n;
|
||||
joint = AnimatedMesh->getAllJoints()[JointID];
|
||||
joint = AnimatedMesh.getJoints()[JointID];
|
||||
joint->setParent(Parent);
|
||||
}
|
||||
}
|
||||
@@ -520,9 +433,9 @@ bool CXMeshFileLoader::parseDataObjectFrame(SkinnedMesh::SJoint *Parent)
|
||||
#ifdef _XREADER_DEBUG
|
||||
os::Printer::log("creating joint ", name.c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
joint = AnimatedMesh->addJoint(Parent);
|
||||
joint = AnimatedMesh.addJoint(Parent);
|
||||
joint->Name = name.c_str();
|
||||
JointID = AnimatedMesh->getAllJoints().size() - 1;
|
||||
JointID = AnimatedMesh.getJoints().size() - 1;
|
||||
} else {
|
||||
#ifdef _XREADER_DEBUG
|
||||
os::Printer::log("using joint ", name.c_str(), ELL_DEBUG);
|
||||
@@ -940,46 +853,34 @@ bool CXMeshFileLoader::parseDataObjectSkinWeights(SXMesh &mesh)
|
||||
|
||||
mesh.HasSkinning = true;
|
||||
|
||||
auto n = AnimatedMesh->getJointNumber(TransformNodeName.c_str());
|
||||
SkinnedMesh::SJoint *joint = n.has_value() ? AnimatedMesh->getAllJoints()[*n] : nullptr;
|
||||
auto joint_id = AnimatedMesh.getJointNumber(TransformNodeName.c_str());
|
||||
SkinnedMesh::SJoint *joint = joint_id.has_value() ? AnimatedMesh.getJoints()[*joint_id] : nullptr;
|
||||
|
||||
if (!joint) {
|
||||
#ifdef _XREADER_DEBUG
|
||||
os::Printer::log("creating joint for skinning ", TransformNodeName.c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
n = AnimatedMesh->getAllJoints().size();
|
||||
joint = AnimatedMesh->addJoint(0);
|
||||
joint = AnimatedMesh.addJoint(nullptr);
|
||||
joint->Name = TransformNodeName.c_str();
|
||||
joint_id = joint->JointID;
|
||||
}
|
||||
|
||||
// read vertex weights
|
||||
|
||||
const u32 nWeights = readInt();
|
||||
|
||||
// read vertex indices
|
||||
u32 i;
|
||||
mesh.Weights.reserve(mesh.Weights.size() + nWeights);
|
||||
|
||||
const u32 jointStart = joint->Weights.size();
|
||||
joint->Weights.reserve(jointStart + nWeights);
|
||||
std::vector<u32> vertex_ids;
|
||||
vertex_ids.reserve(nWeights);
|
||||
for (u32 i = 0; i < nWeights; ++i)
|
||||
vertex_ids.push_back(readInt());
|
||||
|
||||
mesh.WeightJoint.reallocate(mesh.WeightJoint.size() + nWeights);
|
||||
mesh.WeightNum.reallocate(mesh.WeightNum.size() + nWeights);
|
||||
|
||||
for (i = 0; i < nWeights; ++i) {
|
||||
mesh.WeightJoint.push_back(*n);
|
||||
mesh.WeightNum.push_back(joint->Weights.size()); // id of weight
|
||||
|
||||
// Note: This adds a weight to joint->Weights
|
||||
SkinnedMesh::SWeight *weight = AnimatedMesh->addWeight(joint);
|
||||
|
||||
weight->buffer_id = 0;
|
||||
weight->vertex_id = readInt();
|
||||
for (u32 i = 0; i < nWeights; ++i) {
|
||||
f32 strength = readFloat();
|
||||
mesh.Weights.emplace_back(SXMesh::Weight{
|
||||
(u16) *joint_id, vertex_ids[i], strength});
|
||||
}
|
||||
|
||||
// read vertex weights
|
||||
|
||||
for (i = jointStart; i < jointStart + nWeights; ++i)
|
||||
joint->Weights[i].strength = readFloat();
|
||||
|
||||
// read matrix offset
|
||||
|
||||
// transforms the mesh vertices to the space of the bone
|
||||
@@ -1320,7 +1221,7 @@ bool CXMeshFileLoader::parseDataObjectAnimationTicksPerSecond()
|
||||
SET_ERR_AND_RETURN();
|
||||
}
|
||||
|
||||
const u32 ticks = readInt();
|
||||
static_cast<void>(readInt());
|
||||
|
||||
if (!checkForOneFollowingSemicolons()) {
|
||||
os::Printer::log("No closing semicolon in AnimationTicksPerSecond in x file", ELL_WARNING);
|
||||
@@ -1334,8 +1235,6 @@ bool CXMeshFileLoader::parseDataObjectAnimationTicksPerSecond()
|
||||
SET_ERR_AND_RETURN();
|
||||
}
|
||||
|
||||
AnimatedMesh->setAnimationSpeed(static_cast<f32>(ticks));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1393,16 +1292,16 @@ bool CXMeshFileLoader::parseDataObjectAnimation()
|
||||
#ifdef _XREADER_DEBUG
|
||||
os::Printer::log("frame name", FrameName.c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
auto n = AnimatedMesh->getJointNumber(FrameName.c_str());
|
||||
auto joint_id = AnimatedMesh.getJointNumber(FrameName.c_str());
|
||||
|
||||
SkinnedMesh::SJoint *joint;
|
||||
if (n.has_value()) {
|
||||
joint = AnimatedMesh->getAllJoints()[*n];
|
||||
if (joint_id.has_value()) {
|
||||
joint = AnimatedMesh.getJoints()[*joint_id];
|
||||
} else {
|
||||
#ifdef _XREADER_DEBUG
|
||||
os::Printer::log("creating joint for animation ", FrameName.c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
joint = AnimatedMesh->addJoint(0);
|
||||
joint = AnimatedMesh.addJoint();
|
||||
joint->Name = FrameName.c_str();
|
||||
}
|
||||
|
||||
@@ -1472,7 +1371,7 @@ bool CXMeshFileLoader::parseDataObjectAnimationKey(SkinnedMesh::SJoint *joint)
|
||||
|
||||
core::quaternion rotation(X, Y, Z, W);
|
||||
rotation.normalize();
|
||||
AnimatedMesh->addRotationKey(joint, time, rotation);
|
||||
AnimatedMesh.addRotationKey(joint, time, rotation);
|
||||
} break;
|
||||
case 1: // scale
|
||||
case 2: // position
|
||||
@@ -1495,9 +1394,9 @@ bool CXMeshFileLoader::parseDataObjectAnimationKey(SkinnedMesh::SJoint *joint)
|
||||
}
|
||||
|
||||
if (keyType == 2) {
|
||||
AnimatedMesh->addPositionKey(joint, time, vector);
|
||||
AnimatedMesh.addPositionKey(joint, time, vector);
|
||||
} else {
|
||||
AnimatedMesh->addScaleKey(joint, time, vector);
|
||||
AnimatedMesh.addScaleKey(joint, time, vector);
|
||||
}
|
||||
} break;
|
||||
case 3:
|
||||
@@ -1522,8 +1421,8 @@ bool CXMeshFileLoader::parseDataObjectAnimationKey(SkinnedMesh::SJoint *joint)
|
||||
os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING);
|
||||
}
|
||||
|
||||
AnimatedMesh->addRotationKey(joint, time, core::quaternion(mat.getTransposed()));
|
||||
AnimatedMesh->addPositionKey(joint, time, mat.getTranslation());
|
||||
AnimatedMesh.addRotationKey(joint, time, core::quaternion(mat.getTransposed()));
|
||||
AnimatedMesh.addPositionKey(joint, time, mat.getTranslation());
|
||||
|
||||
/*
|
||||
core::vector3df scale=mat.getScale();
|
||||
|
||||
@@ -63,8 +63,12 @@ public:
|
||||
|
||||
core::array<video::SMaterial> Materials; // material array
|
||||
|
||||
core::array<u32> WeightJoint;
|
||||
core::array<u32> WeightNum;
|
||||
struct Weight {
|
||||
u16 joint_id;
|
||||
u32 global_vertex_id;
|
||||
f32 strength;
|
||||
};
|
||||
std::vector<Weight> Weights;
|
||||
|
||||
s32 AttachedJointID;
|
||||
|
||||
@@ -152,7 +156,7 @@ private:
|
||||
bool readRGB(video::SColor &color);
|
||||
bool readRGBA(video::SColor &color);
|
||||
|
||||
SkinnedMeshBuilder *AnimatedMesh;
|
||||
SkinnedMeshBuilder AnimatedMesh;
|
||||
|
||||
c8 *Buffer;
|
||||
const c8 *P;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace scene
|
||||
{
|
||||
@@ -35,21 +36,6 @@ f32 SkinnedMesh::getMaxFrameNumber() const
|
||||
return EndFrame;
|
||||
}
|
||||
|
||||
//! Gets the default animation speed of the animated mesh.
|
||||
/** \return Amount of frames per second. If the amount is 0, it is a static, non animated mesh. */
|
||||
f32 SkinnedMesh::getAnimationSpeed() const
|
||||
{
|
||||
return FramesPerSecond;
|
||||
}
|
||||
|
||||
//! Gets the frame count of the animated mesh.
|
||||
/** \param fps Frames per second to play the animation with. If the amount is 0, it is not animated.
|
||||
The actual speed is set in the scene node the mesh is instantiated in.*/
|
||||
void SkinnedMesh::setAnimationSpeed(f32 fps)
|
||||
{
|
||||
FramesPerSecond = fps;
|
||||
}
|
||||
|
||||
// Keyframe Animation
|
||||
|
||||
|
||||
@@ -102,56 +88,19 @@ void SkinnedMesh::skinMesh(const std::vector<core::matrix4> &global_matrices)
|
||||
}
|
||||
}
|
||||
|
||||
// clear skinning helper array
|
||||
for (std::vector<char> &buf : Vertices_Moved)
|
||||
std::fill(buf.begin(), buf.end(), false);
|
||||
|
||||
// skin starting with the root joints
|
||||
for (size_t i = 0; i < AllJoints.size(); ++i) {
|
||||
// Premultiply with global inversed matrices, if present
|
||||
// (which they should be for joints with weights)
|
||||
std::vector<core::matrix4> joint_transforms = global_matrices;
|
||||
for (u16 i = 0; i < AllJoints.size(); ++i) {
|
||||
auto *joint = AllJoints[i];
|
||||
if (joint->Weights.empty())
|
||||
continue;
|
||||
|
||||
// Find this joints pull on vertices
|
||||
// Note: It is assumed that the global inversed matrix has been calculated at this point.
|
||||
core::matrix4 jointVertexPull = global_matrices[i] * joint->GlobalInversedMatrix.value();
|
||||
|
||||
core::vector3df thisVertexMove, thisNormalMove;
|
||||
|
||||
auto &buffersUsed = *SkinningBuffers;
|
||||
|
||||
// Skin Vertices, Positions and Normals
|
||||
for (const auto &weight : joint->Weights) {
|
||||
// Pull this vertex...
|
||||
jointVertexPull.transformVect(thisVertexMove, weight.StaticPos);
|
||||
|
||||
if (AnimateNormals) {
|
||||
thisNormalMove = jointVertexPull.rotateAndScaleVect(weight.StaticNormal);
|
||||
thisNormalMove.normalize(); // must renormalize after potentially scaling
|
||||
}
|
||||
|
||||
if (!(*(weight.Moved))) {
|
||||
*(weight.Moved) = true;
|
||||
|
||||
buffersUsed[weight.buffer_id]->getVertex(weight.vertex_id)->Pos = thisVertexMove * weight.strength;
|
||||
|
||||
if (AnimateNormals)
|
||||
buffersUsed[weight.buffer_id]->getVertex(weight.vertex_id)->Normal = thisNormalMove * weight.strength;
|
||||
|
||||
//*(weight._Pos) = thisVertexMove * weight.strength;
|
||||
} else {
|
||||
buffersUsed[weight.buffer_id]->getVertex(weight.vertex_id)->Pos += thisVertexMove * weight.strength;
|
||||
|
||||
if (AnimateNormals)
|
||||
buffersUsed[weight.buffer_id]->getVertex(weight.vertex_id)->Normal += thisNormalMove * weight.strength;
|
||||
|
||||
//*(weight._Pos) += thisVertexMove * weight.strength;
|
||||
}
|
||||
}
|
||||
if (joint->GlobalInversedMatrix)
|
||||
joint_transforms[i] = joint_transforms[i] * (*joint->GlobalInversedMatrix);
|
||||
}
|
||||
|
||||
for (auto *buffer : *SkinningBuffers)
|
||||
buffer->setDirty(EBT_VERTEX);
|
||||
for (auto *buffer : *SkinningBuffers) {
|
||||
if (buffer->Weights)
|
||||
buffer->Weights->skin(buffer->getVertexBuffer(), joint_transforms);
|
||||
}
|
||||
}
|
||||
|
||||
//! Gets joint count.
|
||||
@@ -211,10 +160,6 @@ u32 SkinnedMesh::getTextureSlot(u32 meshbufNr) const
|
||||
return TextureSlots.at(meshbufNr);
|
||||
}
|
||||
|
||||
void SkinnedMesh::setTextureSlot(u32 meshbufNr, u32 textureSlot) {
|
||||
TextureSlots.at(meshbufNr) = textureSlot;
|
||||
}
|
||||
|
||||
//! set the hardware mapping hint, for driver
|
||||
void SkinnedMesh::setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint,
|
||||
E_BUFFER_TYPE buffer)
|
||||
@@ -230,29 +175,20 @@ void SkinnedMesh::setDirty(E_BUFFER_TYPE buffer)
|
||||
LocalBuffers[i]->setDirty(buffer);
|
||||
}
|
||||
|
||||
void SkinnedMesh::refreshJointCache()
|
||||
void SkinnedMesh::updateStaticPose()
|
||||
{
|
||||
// copy cache from the mesh...
|
||||
for (auto *joint : AllJoints) {
|
||||
for (auto &weight : joint->Weights) {
|
||||
const u16 buffer_id = weight.buffer_id;
|
||||
const u32 vertex_id = weight.vertex_id;
|
||||
weight.StaticPos = LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos;
|
||||
weight.StaticNormal = LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal;
|
||||
}
|
||||
for (auto *buf : LocalBuffers) {
|
||||
if (buf->Weights)
|
||||
buf->Weights->updateStaticPose(buf->getVertexBuffer());
|
||||
}
|
||||
}
|
||||
|
||||
void SkinnedMesh::resetAnimation()
|
||||
{
|
||||
// copy from the cache to the mesh...
|
||||
for (auto *joint : AllJoints) {
|
||||
for (const auto &weight : joint->Weights) {
|
||||
const u16 buffer_id = weight.buffer_id;
|
||||
const u32 vertex_id = weight.vertex_id;
|
||||
LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos = weight.StaticPos;
|
||||
LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal = weight.StaticNormal;
|
||||
}
|
||||
for (auto *buf : LocalBuffers) {
|
||||
if (buf->Weights)
|
||||
buf->Weights->resetToStatic(buf->getVertexBuffer());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,8 +213,8 @@ bool SkinnedMesh::checkForAnimation() const
|
||||
}
|
||||
|
||||
// meshes with weights are animatable
|
||||
for (auto *joint : AllJoints) {
|
||||
if (!joint->Weights.empty()) {
|
||||
for (auto *buf : LocalBuffers) {
|
||||
if (buf->Weights) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -299,40 +235,6 @@ void SkinnedMesh::prepareForSkinning()
|
||||
EndFrame = std::max(EndFrame, joint->keys.getEndFrame());
|
||||
}
|
||||
|
||||
for (auto *joint : AllJoints) {
|
||||
for (auto &weight : joint->Weights) {
|
||||
const u16 buffer_id = weight.buffer_id;
|
||||
const u32 vertex_id = weight.vertex_id;
|
||||
|
||||
// check for invalid ids
|
||||
if (buffer_id >= LocalBuffers.size()) {
|
||||
os::Printer::log("Skinned Mesh: Weight buffer id too large", ELL_WARNING);
|
||||
weight.buffer_id = weight.vertex_id = 0;
|
||||
} else if (vertex_id >= LocalBuffers[buffer_id]->getVertexCount()) {
|
||||
os::Printer::log("Skinned Mesh: Weight vertex id too large", ELL_WARNING);
|
||||
weight.buffer_id = weight.vertex_id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < Vertices_Moved.size(); ++i)
|
||||
for (u32 j = 0; j < Vertices_Moved[i].size(); ++j)
|
||||
Vertices_Moved[i][j] = false;
|
||||
|
||||
// For skinning: cache weight values for speed
|
||||
for (auto *joint : AllJoints) {
|
||||
for (auto &weight : joint->Weights) {
|
||||
const u16 buffer_id = weight.buffer_id;
|
||||
const u32 vertex_id = weight.vertex_id;
|
||||
|
||||
weight.Moved = &Vertices_Moved[buffer_id][vertex_id];
|
||||
weight.StaticPos = LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos;
|
||||
weight.StaticNormal = LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal;
|
||||
}
|
||||
}
|
||||
|
||||
normalizeWeights();
|
||||
|
||||
for (auto *joint : AllJoints) {
|
||||
joint->keys.cleanup();
|
||||
}
|
||||
@@ -340,29 +242,27 @@ void SkinnedMesh::prepareForSkinning()
|
||||
|
||||
void SkinnedMesh::calculateStaticBoundingBox()
|
||||
{
|
||||
std::vector<std::vector<bool>> animated(getMeshBufferCount());
|
||||
for (u32 mb = 0; mb < getMeshBufferCount(); mb++)
|
||||
animated[mb] = std::vector<bool>(getMeshBuffer(mb)->getVertexCount());
|
||||
|
||||
for (auto *joint : AllJoints) {
|
||||
for (auto &weight : joint->Weights) {
|
||||
const u16 buffer_id = weight.buffer_id;
|
||||
const u32 vertex_id = weight.vertex_id;
|
||||
animated[buffer_id][vertex_id] = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
std::vector<bool> animated;
|
||||
for (u16 mb = 0; mb < getMeshBufferCount(); mb++) {
|
||||
for (u32 v = 0; v < getMeshBuffer(mb)->getVertexCount(); v++) {
|
||||
if (!animated[mb][v]) {
|
||||
auto pos = getMeshBuffer(mb)->getVertexBuffer()->getPosition(v);
|
||||
if (!first) {
|
||||
StaticPartsBox.addInternalPoint(pos);
|
||||
} else {
|
||||
StaticPartsBox.reset(pos);
|
||||
first = false;
|
||||
}
|
||||
auto *buf = LocalBuffers[mb];
|
||||
animated.clear();
|
||||
animated.resize(buf->getVertexCount(), false);
|
||||
if (buf->Weights) {
|
||||
for (u32 vert_id : buf->Weights->animated_vertices.value()) {
|
||||
animated[vert_id] = true;
|
||||
}
|
||||
}
|
||||
for (u32 v = 0; v < buf->getVertexCount(); v++) {
|
||||
if (animated[v])
|
||||
continue;
|
||||
|
||||
auto pos = getMeshBuffer(mb)->getVertexBuffer()->getPosition(v);
|
||||
if (!first) {
|
||||
StaticPartsBox.addInternalPoint(pos);
|
||||
} else {
|
||||
StaticPartsBox.reset(pos);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -370,21 +270,33 @@ void SkinnedMesh::calculateStaticBoundingBox()
|
||||
|
||||
void SkinnedMesh::calculateJointBoundingBoxes()
|
||||
{
|
||||
for (auto *joint : AllJoints) {
|
||||
bool first = true;
|
||||
for (auto &weight : joint->Weights) {
|
||||
if (weight.strength < 1e-6)
|
||||
continue;
|
||||
auto pos = weight.StaticPos;
|
||||
joint->GlobalInversedMatrix.value().transformVect(pos);
|
||||
if (!first) {
|
||||
joint->LocalBoundingBox.addInternalPoint(pos);
|
||||
} else {
|
||||
joint->LocalBoundingBox.reset(pos);
|
||||
first = false;
|
||||
std::vector<std::optional<core::aabbox3df>> joint_boxes(AllJoints.size());
|
||||
for (auto *buf : LocalBuffers) {
|
||||
const auto &weights = buf->Weights;
|
||||
if (!weights)
|
||||
continue;
|
||||
for (u32 vert_id : weights->animated_vertices.value()) {
|
||||
const auto pos = buf->getVertex(vert_id)->Pos;
|
||||
for (u16 j = 0; j < WeightBuffer::MAX_WEIGHTS_PER_VERTEX; j++) {
|
||||
const u16 joint_id = weights->getJointIds(vert_id)[j];
|
||||
const SJoint *joint = AllJoints[joint_id];
|
||||
const f32 weight = weights->getWeights(vert_id)[j];
|
||||
if (core::equals(weight, 0.0f))
|
||||
continue;
|
||||
auto trans_pos = pos;
|
||||
joint->GlobalInversedMatrix.value().transformVect(trans_pos);
|
||||
if (joint_boxes[joint_id]) {
|
||||
joint_boxes[joint_id]->addInternalPoint(trans_pos);
|
||||
} else {
|
||||
joint_boxes[joint_id] = core::aabbox3df{trans_pos};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (u16 joint_id = 0; joint_id < AllJoints.size(); joint_id++) {
|
||||
auto *joint = AllJoints[joint_id];
|
||||
joint->LocalBoundingBox = joint_boxes[joint_id].value_or(core::aabbox3df{{0,0,0}});
|
||||
}
|
||||
}
|
||||
|
||||
void SkinnedMesh::calculateBufferBoundingBoxes()
|
||||
@@ -402,15 +314,16 @@ void SkinnedMesh::recalculateBaseBoundingBoxes() {
|
||||
calculateBufferBoundingBoxes();
|
||||
}
|
||||
|
||||
void SkinnedMesh::topoSortJoints()
|
||||
void SkinnedMeshBuilder::topoSortJoints()
|
||||
{
|
||||
size_t n = AllJoints.size();
|
||||
auto &joints = getJoints();
|
||||
const size_t n = joints.size();
|
||||
|
||||
std::vector<u16> new_to_old_id;
|
||||
|
||||
std::vector<std::vector<u16>> children(n);
|
||||
for (u16 i = 0; i < n; ++i) {
|
||||
if (auto parentId = AllJoints[i]->ParentJointID)
|
||||
if (auto parentId = joints[i]->ParentJointID)
|
||||
children[*parentId].push_back(i);
|
||||
else
|
||||
new_to_old_id.push_back(i);
|
||||
@@ -427,76 +340,93 @@ void SkinnedMesh::topoSortJoints()
|
||||
for (u16 i = 0; i < n; ++i)
|
||||
old_to_new_id[new_to_old_id[i]] = i;
|
||||
|
||||
std::vector<SJoint *> joints(n);
|
||||
std::vector<SJoint *> sorted_joints(n);
|
||||
for (u16 i = 0; i < n; ++i) {
|
||||
joints[i] = AllJoints[new_to_old_id[i]];
|
||||
joints[i]->JointID = i;
|
||||
if (auto parentId = joints[i]->ParentJointID)
|
||||
joints[i]->ParentJointID = old_to_new_id[*parentId];
|
||||
auto *joint = joints[new_to_old_id[i]];
|
||||
if (auto parentId = joint->ParentJointID)
|
||||
joint->ParentJointID = old_to_new_id[*parentId];
|
||||
sorted_joints[i] = joint;
|
||||
joint->JointID = i;
|
||||
}
|
||||
AllJoints = std::move(joints);
|
||||
|
||||
// Verify that the topological ordering is correct
|
||||
for (u16 i = 0; i < n; ++i) {
|
||||
if (auto pjid = AllJoints[i]->ParentJointID)
|
||||
if (auto pjid = sorted_joints[i]->ParentJointID)
|
||||
assert(*pjid < i);
|
||||
}
|
||||
getJoints() = std::move(sorted_joints);
|
||||
|
||||
for (auto &weight : weights) {
|
||||
weight.joint_id = old_to_new_id[weight.joint_id];
|
||||
}
|
||||
}
|
||||
|
||||
//! called by loader after populating with mesh and bone data
|
||||
SkinnedMesh *SkinnedMeshBuilder::finalize()
|
||||
SkinnedMesh *SkinnedMeshBuilder::finalize() &&
|
||||
{
|
||||
os::Printer::log("Skinned Mesh - finalize", ELL_DEBUG);
|
||||
|
||||
// Topologically sort the joints such that parents come before their children.
|
||||
// From this point on, transformations can be calculated in linear order.
|
||||
// (see e.g. SkinnedMesh::calculateGlobalMatrices)
|
||||
topoSortJoints();
|
||||
|
||||
// Set array sizes
|
||||
for (u32 i = 0; i < LocalBuffers.size(); ++i) {
|
||||
Vertices_Moved.emplace_back(LocalBuffers[i]->getVertexCount());
|
||||
}
|
||||
|
||||
prepareForSkinning();
|
||||
mesh->prepareForSkinning();
|
||||
|
||||
std::vector<core::matrix4> matrices;
|
||||
matrices.reserve(AllJoints.size());
|
||||
for (auto *joint : AllJoints) {
|
||||
matrices.reserve(getJoints().size());
|
||||
for (auto *joint : getJoints()) {
|
||||
if (const auto *matrix = std::get_if<core::matrix4>(&joint->transform))
|
||||
matrices.push_back(*matrix);
|
||||
else
|
||||
matrices.push_back(std::get<core::Transform>(joint->transform).buildMatrix());
|
||||
}
|
||||
calculateGlobalMatrices(matrices);
|
||||
mesh->calculateGlobalMatrices(matrices);
|
||||
|
||||
for (size_t i = 0; i < AllJoints.size(); ++i) {
|
||||
auto *joint = AllJoints[i];
|
||||
for (size_t i = 0; i < getJoints().size(); ++i) {
|
||||
auto *joint = getJoints()[i];
|
||||
if (!joint->GlobalInversedMatrix) {
|
||||
joint->GlobalInversedMatrix = matrices[i];
|
||||
joint->GlobalInversedMatrix->makeInverse();
|
||||
}
|
||||
// rigid animation for non animated meshes
|
||||
for (u32 attachedMeshIdx : joint->AttachedMeshes) {
|
||||
SSkinMeshBuffer *Buffer = (*SkinningBuffers)[attachedMeshIdx];
|
||||
SSkinMeshBuffer *Buffer = (*mesh->SkinningBuffers)[attachedMeshIdx];
|
||||
Buffer->Transformation = matrices[i];
|
||||
}
|
||||
}
|
||||
|
||||
recalculateBaseBoundingBoxes();
|
||||
StaticPoseBox = calculateBoundingBox(matrices);
|
||||
for (const auto &weight : weights) {
|
||||
auto *buf = mesh->LocalBuffers.at(weight.buffer_id);
|
||||
if (!buf->Weights)
|
||||
buf->Weights = WeightBuffer(buf->getVertexCount());
|
||||
buf->Weights->addWeight(weight.vertex_id, weight.joint_id, weight.strength);
|
||||
}
|
||||
|
||||
return this;
|
||||
for (auto *buffer : mesh->LocalBuffers) {
|
||||
if (buffer->Weights)
|
||||
buffer->Weights->finalize();
|
||||
}
|
||||
mesh->updateStaticPose();
|
||||
|
||||
mesh->recalculateBaseBoundingBoxes();
|
||||
mesh->StaticPoseBox = mesh->calculateBoundingBox(matrices);
|
||||
|
||||
return mesh.release();
|
||||
}
|
||||
|
||||
scene::SSkinMeshBuffer *SkinnedMeshBuilder::addMeshBuffer()
|
||||
{
|
||||
scene::SSkinMeshBuffer *buffer = new scene::SSkinMeshBuffer();
|
||||
TextureSlots.push_back(LocalBuffers.size());
|
||||
LocalBuffers.push_back(buffer);
|
||||
mesh->TextureSlots.push_back(mesh->LocalBuffers.size());
|
||||
mesh->LocalBuffers.push_back(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void SkinnedMeshBuilder::addMeshBuffer(SSkinMeshBuffer *meshbuf)
|
||||
u32 SkinnedMeshBuilder::addMeshBuffer(SSkinMeshBuffer *meshbuf)
|
||||
{
|
||||
TextureSlots.push_back(LocalBuffers.size());
|
||||
LocalBuffers.push_back(meshbuf);
|
||||
mesh->TextureSlots.push_back(mesh->LocalBuffers.size());
|
||||
mesh->LocalBuffers.push_back(meshbuf);
|
||||
return mesh->getMeshBufferCount() - 1;
|
||||
}
|
||||
|
||||
SkinnedMesh::SJoint *SkinnedMeshBuilder::addJoint(SJoint *parent)
|
||||
@@ -504,8 +434,8 @@ SkinnedMesh::SJoint *SkinnedMeshBuilder::addJoint(SJoint *parent)
|
||||
SJoint *joint = new SJoint;
|
||||
joint->setParent(parent);
|
||||
|
||||
joint->JointID = AllJoints.size();
|
||||
AllJoints.push_back(joint);
|
||||
joint->JointID = getJoints().size();
|
||||
getJoints().push_back(joint);
|
||||
|
||||
return joint;
|
||||
}
|
||||
@@ -528,51 +458,12 @@ void SkinnedMeshBuilder::addRotationKey(SJoint *joint, f32 frame, core::quaterni
|
||||
joint->keys.rotation.pushBack(frame, rot);
|
||||
}
|
||||
|
||||
SkinnedMesh::SWeight *SkinnedMeshBuilder::addWeight(SJoint *joint)
|
||||
void SkinnedMeshBuilder::addWeight(SJoint *joint, u16 buf_id, u32 vert_id, f32 strength)
|
||||
{
|
||||
if (!joint)
|
||||
return nullptr;
|
||||
|
||||
joint->Weights.emplace_back();
|
||||
return &joint->Weights.back();
|
||||
}
|
||||
|
||||
void SkinnedMesh::normalizeWeights()
|
||||
{
|
||||
// note: unsure if weights ids are going to be used.
|
||||
|
||||
// Normalise the weights on bones....
|
||||
|
||||
std::vector<std::vector<f32>> verticesTotalWeight;
|
||||
|
||||
verticesTotalWeight.reserve(LocalBuffers.size());
|
||||
for (u32 i = 0; i < LocalBuffers.size(); ++i) {
|
||||
verticesTotalWeight.emplace_back(LocalBuffers[i]->getVertexCount());
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < verticesTotalWeight.size(); ++i)
|
||||
for (u32 j = 0; j < verticesTotalWeight[i].size(); ++j)
|
||||
verticesTotalWeight[i][j] = 0;
|
||||
|
||||
for (auto *joint : AllJoints) {
|
||||
auto &weights = joint->Weights;
|
||||
|
||||
weights.erase(std::remove_if(weights.begin(), weights.end(), [](const auto &weight) {
|
||||
return weight.strength <= 0;
|
||||
}), weights.end());
|
||||
|
||||
for (const auto &weight : weights) {
|
||||
verticesTotalWeight[weight.buffer_id][weight.vertex_id] += weight.strength;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto *joint : AllJoints) {
|
||||
for (auto &weight : joint->Weights) {
|
||||
const f32 total = verticesTotalWeight[weight.buffer_id][weight.vertex_id];
|
||||
if (total != 0 && total != 1)
|
||||
weight.strength /= total;
|
||||
}
|
||||
}
|
||||
assert(joint);
|
||||
if (strength <= 0.0f)
|
||||
return;
|
||||
weights.emplace_back(Weight{joint->JointID, buf_id, vert_id, strength});
|
||||
}
|
||||
|
||||
void SkinnedMesh::convertMeshToTangents()
|
||||
|
||||
124
irr/src/WeightBuffer.cpp
Normal file
124
irr/src/WeightBuffer.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright (C) 2025 Lars Müller
|
||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "WeightBuffer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
namespace scene {
|
||||
|
||||
void WeightBuffer::VertexWeights::addWeight(u16 joint_id, f32 weight)
|
||||
{
|
||||
assert(weight >= 0.0f);
|
||||
auto min_weight = std::min_element(weights.begin(), weights.end());
|
||||
if (*min_weight > weight)
|
||||
return;
|
||||
|
||||
*min_weight = weight;
|
||||
joint_ids[std::distance(weights.begin(), min_weight)] = joint_id;
|
||||
}
|
||||
|
||||
void WeightBuffer::addWeight(u32 vertex_id, u16 joint_id, f32 weight)
|
||||
{
|
||||
weights.at(vertex_id).addWeight(joint_id, weight);
|
||||
}
|
||||
|
||||
void WeightBuffer::VertexWeights::skinVertex(core::vector3df &pos, core::vector3df &normal,
|
||||
const std::vector<core::matrix4> &joint_transforms) const
|
||||
{
|
||||
f32 total_weight = 0.0f;
|
||||
core::vector3df skinned_pos;
|
||||
core::vector3df skinned_normal;
|
||||
for (u16 i = 0; i < MAX_WEIGHTS_PER_VERTEX; ++i) {
|
||||
const u16 joint_id = joint_ids[i];
|
||||
const f32 weight = weights[i];
|
||||
if (core::equals(weight, 0.0f))
|
||||
continue;
|
||||
|
||||
const auto &transform = joint_transforms[joint_id];
|
||||
core::vector3df transformed_pos = pos;
|
||||
transform.transformVect(transformed_pos);
|
||||
skinned_pos += weight * transformed_pos;
|
||||
skinned_normal += weight * transform.rotateAndScaleVect(normal);
|
||||
total_weight += weight;
|
||||
}
|
||||
if (core::equals(total_weight, 0.0f))
|
||||
return;
|
||||
|
||||
pos = skinned_pos;
|
||||
// Need to renormalize normal after potentially scaling
|
||||
normal = skinned_normal.normalize();
|
||||
}
|
||||
|
||||
void WeightBuffer::skinVertex(u32 vertex_id, core::vector3df &pos, core::vector3df &normal,
|
||||
const std::vector<core::matrix4> &joint_transforms) const
|
||||
{
|
||||
return weights[vertex_id].skinVertex(pos, normal, joint_transforms);
|
||||
}
|
||||
|
||||
void WeightBuffer::skin(IVertexBuffer *dst,
|
||||
const std::vector<core::matrix4> &joint_transforms) const
|
||||
{
|
||||
assert(animated_vertices.has_value());
|
||||
for (u32 i = 0; i < animated_vertices->size(); ++i) {
|
||||
|
||||
u32 vertex_id = (*animated_vertices)[i];
|
||||
auto pos = static_positions[i];
|
||||
auto normal = static_normals[i];
|
||||
skinVertex(vertex_id, pos, normal, joint_transforms);
|
||||
dst->getPosition(vertex_id) = pos;
|
||||
dst->getNormal(vertex_id) = normal;
|
||||
}
|
||||
if (!animated_vertices->empty())
|
||||
dst->setDirty();
|
||||
}
|
||||
|
||||
void WeightBuffer::finalize()
|
||||
{
|
||||
// Normalizes weights so that they sum to 1.0 per vertex,
|
||||
// stores which vertices are animated.
|
||||
assert(!animated_vertices.has_value());
|
||||
animated_vertices.emplace();
|
||||
for (u32 i = 0; i < size(); ++i) {
|
||||
auto &weights_i = weights[i].weights;
|
||||
f32 total_weight = std::accumulate(weights_i.begin(), weights_i.end(), 0.0f);
|
||||
if (core::equals(total_weight, 0.0f)) {
|
||||
std::fill(weights_i.begin(), weights_i.end(), 0.0f);
|
||||
continue;
|
||||
}
|
||||
animated_vertices->emplace_back(i);
|
||||
if (core::equals(total_weight, 1.0f))
|
||||
continue;
|
||||
for (auto &strength : weights_i)
|
||||
strength /= total_weight;
|
||||
}
|
||||
animated_vertices->shrink_to_fit();
|
||||
}
|
||||
|
||||
void WeightBuffer::updateStaticPose(const IVertexBuffer *vbuf)
|
||||
{
|
||||
if (!static_normals)
|
||||
static_normals = std::make_unique<core::vector3df[]>(animated_vertices->size());
|
||||
if (!static_positions)
|
||||
static_positions = std::make_unique<core::vector3df[]>(animated_vertices->size());
|
||||
for (size_t idx = 0; idx < animated_vertices->size(); ++idx) {
|
||||
u32 vertex_id = (*animated_vertices)[idx];
|
||||
static_positions[idx] = vbuf->getPosition(vertex_id);
|
||||
static_normals[idx] = vbuf->getNormal(vertex_id);
|
||||
}
|
||||
}
|
||||
|
||||
void WeightBuffer::resetToStatic(IVertexBuffer *vbuf) const
|
||||
{
|
||||
assert(animated_vertices.has_value());
|
||||
for (size_t idx = 0; idx < animated_vertices->size(); ++idx) {
|
||||
u32 vertex_id = (*animated_vertices)[idx];
|
||||
vbuf->getPosition(vertex_id) = static_positions[idx];
|
||||
vbuf->getNormal(vertex_id) = static_normals[idx];
|
||||
}
|
||||
if (!animated_vertices->empty())
|
||||
vbuf->setDirty();
|
||||
}
|
||||
|
||||
} // end namespace scene
|
||||
Reference in New Issue
Block a user