mirror of
https://github.com/luanti-org/luanti.git
synced 2025-08-06 17:41:04 +00:00
glTF morph animation proof of concept
Some things to look at: - Normal recalculation for vertex morphing - Bounding boxes (esp. after rebasing this upon the other PR)
This commit is contained in:
parent
9938b02eee
commit
5a09102770
4 changed files with 174 additions and 38 deletions
|
@ -14,6 +14,7 @@
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace irr
|
namespace irr
|
||||||
|
@ -373,10 +374,15 @@ public:
|
||||||
//! Call this after changing the positions of any vertex.
|
//! Call this after changing the positions of any vertex.
|
||||||
void boundingBoxNeedsRecalculated() { BoundingBoxNeedsRecalculated = true; }
|
void boundingBoxNeedsRecalculated() { BoundingBoxNeedsRecalculated = true; }
|
||||||
|
|
||||||
void morph(const std::vector<f32> &weights)
|
void setMorph(const std::optional<std::vector<f32>> &weights)
|
||||||
|
{
|
||||||
|
resetMorph();
|
||||||
|
addMorph(weights.value_or(DefaultWeights));
|
||||||
|
}
|
||||||
|
|
||||||
|
void addMorph(const std::vector<f32> &weights)
|
||||||
{
|
{
|
||||||
assert(weights.size() == MorphTargets.size());
|
assert(weights.size() == MorphTargets.size());
|
||||||
resetMorph();
|
|
||||||
for (size_t i = 0; i < weights.size(); ++i) {
|
for (size_t i = 0; i < weights.size(); ++i) {
|
||||||
MorphTargets[i].add(getVertexBuffer(), weights[i]);
|
MorphTargets[i].add(getVertexBuffer(), weights[i]);
|
||||||
if (!MorphTargets[i].positions.empty())
|
if (!MorphTargets[i].positions.empty())
|
||||||
|
@ -401,6 +407,7 @@ public:
|
||||||
// TODO consolidate with Static(Pos|Normal) in weights
|
// TODO consolidate with Static(Pos|Normal) in weights
|
||||||
MorphTargetDelta MorphStaticPose;
|
MorphTargetDelta MorphStaticPose;
|
||||||
std::vector<MorphTargetDelta> MorphTargets;
|
std::vector<MorphTargetDelta> MorphTargets;
|
||||||
|
std::vector<f32> DefaultWeights;
|
||||||
|
|
||||||
video::SMaterial Material;
|
video::SMaterial Material;
|
||||||
video::E_VERTEX_TYPE VertexType;
|
video::E_VERTEX_TYPE VertexType;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "quaternion.h"
|
#include "quaternion.h"
|
||||||
#include "vector3d.h"
|
#include "vector3d.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -166,8 +167,6 @@ public:
|
||||||
//! Internal members used by SkinnedMesh
|
//! Internal members used by SkinnedMesh
|
||||||
friend class SkinnedMesh;
|
friend class SkinnedMesh;
|
||||||
char *Moved;
|
char *Moved;
|
||||||
core::vector3df StaticPos;
|
|
||||||
core::vector3df StaticNormal;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
|
@ -252,26 +251,98 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct WeightChannel
|
||||||
|
{
|
||||||
|
std::vector<f32> timestamps;
|
||||||
|
std::vector<f32> weights;
|
||||||
|
bool interpolate = true;
|
||||||
|
size_t n_weights;
|
||||||
|
|
||||||
|
bool empty() const {
|
||||||
|
return timestamps.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 getEndFrame() const {
|
||||||
|
return timestamps.empty() ? 0 : timestamps.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reserve(size_t n) {
|
||||||
|
timestamps.reserve(n);
|
||||||
|
weights.reserve(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup() {
|
||||||
|
timestamps.shrink_to_fit();
|
||||||
|
weights.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
static core::vector3df interpolateValue(core::vector3df from, core::vector3df to, f32 time) {
|
||||||
|
// Note: `from` and `to` are swapped here compared to quaternion slerp
|
||||||
|
return to.getInterpolated(from, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::vector<f32>> get(f32 time) const {
|
||||||
|
if (empty())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const size_t i = std::distance(timestamps.begin(),
|
||||||
|
std::lower_bound(timestamps.begin(), timestamps.end(), time));
|
||||||
|
if (i == 0)
|
||||||
|
return getWeightsVec(0);
|
||||||
|
if (i == timestamps.size())
|
||||||
|
return getWeightsVec(i - 1);
|
||||||
|
|
||||||
|
auto res = getWeightsVec(i - 1);
|
||||||
|
if (!interpolate)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
f32 prev_time = timestamps[i - 1];
|
||||||
|
f32 next_time = timestamps[i];
|
||||||
|
f32 progress = (time - prev_time) / (next_time - prev_time);
|
||||||
|
for (size_t j = 0; j < n_weights; ++j) {
|
||||||
|
res[j] = progress * *(getWeights(i) + j) + (1.0f - progress) * res[j];
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<f32>::const_iterator getWeights(size_t i) const
|
||||||
|
{
|
||||||
|
return weights.begin() + i * n_weights;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<f32> getWeightsVec(size_t i) const
|
||||||
|
{
|
||||||
|
std::vector<f32> res;
|
||||||
|
res.insert(res.begin(), getWeights(i), getWeights(i + 1));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct Keys {
|
struct Keys {
|
||||||
Channel<core::vector3df> position;
|
Channel<core::vector3df> position;
|
||||||
Channel<core::quaternion> rotation;
|
Channel<core::quaternion> rotation;
|
||||||
Channel<core::vector3df> scale;
|
Channel<core::vector3df> scale;
|
||||||
|
WeightChannel weights;
|
||||||
|
|
||||||
bool empty() const {
|
bool empty() const {
|
||||||
return position.empty() && rotation.empty() && scale.empty();
|
return position.empty() && rotation.empty() && scale.empty() && weights.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void append(const Keys &other) {
|
void append(const Keys &other) {
|
||||||
position.append(other.position);
|
position.append(other.position);
|
||||||
rotation.append(other.rotation);
|
rotation.append(other.rotation);
|
||||||
scale.append(other.scale);
|
scale.append(other.scale);
|
||||||
|
// This is only used by .x, which has no morph animation
|
||||||
|
assert(other.weights.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
f32 getEndFrame() const {
|
f32 getEndFrame() const {
|
||||||
return std::max({
|
return std::max({
|
||||||
position.getEndFrame(),
|
position.getEndFrame(),
|
||||||
rotation.getEndFrame(),
|
rotation.getEndFrame(),
|
||||||
scale.getEndFrame()
|
scale.getEndFrame(),
|
||||||
|
weights.getEndFrame(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,6 +361,7 @@ public:
|
||||||
position.cleanup();
|
position.cleanup();
|
||||||
rotation.cleanup();
|
rotation.cleanup();
|
||||||
scale.cleanup();
|
scale.cleanup();
|
||||||
|
weights.cleanup();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -307,6 +379,9 @@ public:
|
||||||
//! List of child joints
|
//! List of child joints
|
||||||
std::vector<SJoint *> Children;
|
std::vector<SJoint *> Children;
|
||||||
|
|
||||||
|
//! List of meshes which may be affected by morph animations targeting this joint
|
||||||
|
std::vector<size_t> MorphedMeshes;
|
||||||
|
|
||||||
//! List of attached meshes
|
//! List of attached meshes
|
||||||
std::vector<u32> AttachedMeshes;
|
std::vector<u32> AttachedMeshes;
|
||||||
|
|
||||||
|
@ -360,6 +435,9 @@ protected:
|
||||||
std::vector<SSkinMeshBuffer *> *SkinningBuffers; // Meshbuffer to skin, default is to skin localBuffers
|
std::vector<SSkinMeshBuffer *> *SkinningBuffers; // Meshbuffer to skin, default is to skin localBuffers
|
||||||
|
|
||||||
std::vector<SSkinMeshBuffer *> LocalBuffers;
|
std::vector<SSkinMeshBuffer *> LocalBuffers;
|
||||||
|
//! Buffers involved in skinning.
|
||||||
|
//! These have a MorphStaticPose which can be used to reset positions & normals.
|
||||||
|
std::vector<u32> SkinnedBuffers;
|
||||||
//! Mapping from meshbuffer number to bindable texture slot
|
//! Mapping from meshbuffer number to bindable texture slot
|
||||||
std::vector<u32> TextureSlots;
|
std::vector<u32> TextureSlots;
|
||||||
|
|
||||||
|
|
|
@ -440,6 +440,7 @@ void SelfType::MeshExtractor::addPrimitive(
|
||||||
const auto meshbufNr = m_irr_model->getMeshBufferCount() - 1;
|
const auto meshbufNr = m_irr_model->getMeshBufferCount() - 1;
|
||||||
|
|
||||||
if (auto morphTargets = primitive.targets) {
|
if (auto morphTargets = primitive.targets) {
|
||||||
|
parent->MorphedMeshes.push_back(meshbufNr);
|
||||||
MorphTargetDelta::Flags used;
|
MorphTargetDelta::Flags used;
|
||||||
std::vector<MorphTargetDelta> deltas;
|
std::vector<MorphTargetDelta> deltas;
|
||||||
deltas.reserve(morphTargets->size());
|
deltas.reserve(morphTargets->size());
|
||||||
|
@ -492,8 +493,8 @@ void SelfType::MeshExtractor::addPrimitive(
|
||||||
weights.reserve(morphWeights->size());
|
weights.reserve(morphWeights->size());
|
||||||
for (f64 weight : *morphWeights)
|
for (f64 weight : *morphWeights)
|
||||||
weights.push_back(static_cast<f32>(weight));
|
weights.push_back(static_cast<f32>(weight));
|
||||||
// TODO does some unnecessary work - resets what we just read
|
meshbuf->DefaultWeights = weights;
|
||||||
meshbuf->morph(weights);
|
meshbuf->addMorph(weights);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -522,6 +523,11 @@ void SelfType::MeshExtractor::addPrimitive(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!primitive.targets) {
|
||||||
|
// We use morphing to reset to static pose.
|
||||||
|
parent->MorphedMeshes.push_back(meshbufNr);
|
||||||
|
}
|
||||||
|
|
||||||
// Otherwise: "Only the joint transforms are applied to the skinned mesh;
|
// Otherwise: "Only the joint transforms are applied to the skinned mesh;
|
||||||
// the transform of the skinned mesh node MUST be ignored."
|
// the transform of the skinned mesh node MUST be ignored."
|
||||||
|
|
||||||
|
@ -708,6 +714,18 @@ void SelfType::MeshExtractor::loadSkins()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t countMorphTargets(const tiniergltf::GlTF &gltfModel, size_t nodeIdx)
|
||||||
|
{
|
||||||
|
const auto &node = gltfModel.nodes->at(nodeIdx);
|
||||||
|
const auto &mesh = gltfModel.meshes->at(node.mesh.value());
|
||||||
|
size_t morph_targets = 0;
|
||||||
|
for (const auto &primitive : mesh.primitives) {
|
||||||
|
if (primitive.targets)
|
||||||
|
morph_targets = std::max(morph_targets, primitive.targets->size());
|
||||||
|
}
|
||||||
|
return morph_targets;
|
||||||
|
}
|
||||||
|
|
||||||
void SelfType::MeshExtractor::loadAnimation(const std::size_t animIdx)
|
void SelfType::MeshExtractor::loadAnimation(const std::size_t animIdx)
|
||||||
{
|
{
|
||||||
const auto &anim = m_gltf_model.animations->at(animIdx);
|
const auto &anim = m_gltf_model.animations->at(animIdx);
|
||||||
|
@ -731,8 +749,9 @@ void SelfType::MeshExtractor::loadAnimation(const std::size_t animIdx)
|
||||||
|
|
||||||
if (!channel.target.node.has_value())
|
if (!channel.target.node.has_value())
|
||||||
throw std::runtime_error("no animated node");
|
throw std::runtime_error("no animated node");
|
||||||
|
size_t targetNodeIdx = *channel.target.node;
|
||||||
|
|
||||||
auto *joint = m_loaded_nodes.at(*channel.target.node);
|
auto *joint = m_loaded_nodes.at(targetNodeIdx);
|
||||||
switch (channel.target.path) {
|
switch (channel.target.path) {
|
||||||
case tiniergltf::AnimationChannelTarget::Path::TRANSLATION: {
|
case tiniergltf::AnimationChannelTarget::Path::TRANSLATION: {
|
||||||
const auto outputAccessor = Accessor<core::vector3df>::make(m_gltf_model, sampler.output);
|
const auto outputAccessor = Accessor<core::vector3df>::make(m_gltf_model, sampler.output);
|
||||||
|
@ -767,8 +786,24 @@ void SelfType::MeshExtractor::loadAnimation(const std::size_t animIdx)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case tiniergltf::AnimationChannelTarget::Path::WEIGHTS:
|
case tiniergltf::AnimationChannelTarget::Path::WEIGHTS: {
|
||||||
throw std::runtime_error("no support for morph animations");
|
// FIXME support normalized bytes and stuff
|
||||||
|
const auto outputAccessor = Accessor<f32>::make(m_gltf_model, sampler.output);
|
||||||
|
const size_t count = outputAccessor.getCount();
|
||||||
|
auto &channel = joint->keys.weights;
|
||||||
|
channel.n_weights = countMorphTargets(m_gltf_model, targetNodeIdx);
|
||||||
|
if (channel.n_weights == 0)
|
||||||
|
throw std::runtime_error("missing morph targets for morph animation");
|
||||||
|
if (count % channel.n_weights != 0)
|
||||||
|
throw std::runtime_error("wrong number of values in morph weight accessor");
|
||||||
|
for (size_t i = 0; i < count / channel.n_weights; ++i) {
|
||||||
|
channel.timestamps.push_back(inputAccessor.get(i));
|
||||||
|
for (size_t j = 0; j < channel.n_weights; ++j) {
|
||||||
|
channel.weights.push_back(outputAccessor.get(i * channel.n_weights + j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#include "IAnimatedMeshSceneNode.h"
|
#include "IAnimatedMeshSceneNode.h"
|
||||||
#include "SSkinMeshBuffer.h"
|
#include "SSkinMeshBuffer.h"
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
|
#include <bitset>
|
||||||
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
@ -73,6 +75,9 @@ void SkinnedMesh::animateMesh(f32 frame)
|
||||||
LastAnimatedFrame = frame;
|
LastAnimatedFrame = frame;
|
||||||
SkinnedLastFrame = false;
|
SkinnedLastFrame = false;
|
||||||
|
|
||||||
|
// TODO do something if there are too many mesh buffers
|
||||||
|
std::bitset<256> morphed_buffers;
|
||||||
|
|
||||||
for (auto *joint : AllJoints) {
|
for (auto *joint : AllJoints) {
|
||||||
// The joints can be animated here with no input from their
|
// The joints can be animated here with no input from their
|
||||||
// parents, but for setAnimationMode extra checks are needed
|
// parents, but for setAnimationMode extra checks are needed
|
||||||
|
@ -81,6 +86,17 @@ void SkinnedMesh::animateMesh(f32 frame)
|
||||||
joint->Animatedposition,
|
joint->Animatedposition,
|
||||||
joint->Animatedrotation,
|
joint->Animatedrotation,
|
||||||
joint->Animatedscale);
|
joint->Animatedscale);
|
||||||
|
auto weights = joint->keys.weights.get(frame);
|
||||||
|
for (size_t meshbufIdx : joint->MorphedMeshes) {
|
||||||
|
LocalBuffers[meshbufIdx]->setMorph(weights);
|
||||||
|
morphed_buffers.set(meshbufIdx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t meshbufIdx : SkinnedBuffers) {
|
||||||
|
if (!morphed_buffers.test(meshbufIdx)) {
|
||||||
|
LocalBuffers[meshbufIdx]->setMorph(std::nullopt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note:
|
// Note:
|
||||||
|
@ -226,11 +242,13 @@ void SkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint)
|
||||||
|
|
||||||
// Skin Vertices Positions and Normals...
|
// Skin Vertices Positions and Normals...
|
||||||
for (const auto &weight : joint->Weights) {
|
for (const auto &weight : joint->Weights) {
|
||||||
|
auto *buf = buffersUsed[weight.buffer_id];
|
||||||
// Pull this vertex...
|
// Pull this vertex...
|
||||||
jointVertexPull.transformVect(thisVertexMove, weight.StaticPos);
|
auto *vertex = buf->getVertex(weight.vertex_id);
|
||||||
|
jointVertexPull.transformVect(thisVertexMove, vertex->Pos);
|
||||||
|
|
||||||
if (AnimateNormals) {
|
if (AnimateNormals) {
|
||||||
thisNormalMove = jointVertexPull.rotateAndScaleVect(weight.StaticNormal);
|
thisNormalMove = jointVertexPull.rotateAndScaleVect(vertex->Normal);
|
||||||
thisNormalMove.normalize(); // must renormalize after potentially scaling
|
thisNormalMove.normalize(); // must renormalize after potentially scaling
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,12 +363,8 @@ bool SkinnedMesh::setHardwareSkinning(bool on)
|
||||||
|
|
||||||
// set mesh to static pose...
|
// set mesh to static pose...
|
||||||
for (auto *joint : AllJoints) {
|
for (auto *joint : AllJoints) {
|
||||||
for (const auto &weight : joint->Weights) {
|
for (auto buf_id : joint->MorphedMeshes) {
|
||||||
const u16 buffer_id = weight.buffer_id;
|
LocalBuffers[buf_id]->setMorph(std::nullopt);
|
||||||
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;
|
|
||||||
LocalBuffers[buffer_id]->boundingBoxNeedsRecalculated();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -360,15 +374,14 @@ bool SkinnedMesh::setHardwareSkinning(bool on)
|
||||||
return HardwareSkinning;
|
return HardwareSkinning;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO the only usage of this only needs to refresh normals
|
||||||
void SkinnedMesh::refreshJointCache()
|
void SkinnedMesh::refreshJointCache()
|
||||||
{
|
{
|
||||||
// copy cache from the mesh...
|
// copy cache from the mesh...
|
||||||
for (auto *joint : AllJoints) {
|
for (auto *joint : AllJoints) {
|
||||||
for (auto &weight : joint->Weights) {
|
for (auto buf_id : joint->MorphedMeshes) {
|
||||||
const u16 buffer_id = weight.buffer_id;
|
auto *buf = LocalBuffers[buf_id];
|
||||||
const u32 vertex_id = weight.vertex_id;
|
buf->MorphStaticPose.get(buf->getVertexBuffer(), {true, true, false});
|
||||||
weight.StaticPos = LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos;
|
|
||||||
weight.StaticNormal = LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -377,11 +390,8 @@ void SkinnedMesh::resetAnimation()
|
||||||
{
|
{
|
||||||
// copy from the cache to the mesh...
|
// copy from the cache to the mesh...
|
||||||
for (auto *joint : AllJoints) {
|
for (auto *joint : AllJoints) {
|
||||||
for (const auto &weight : joint->Weights) {
|
for (auto buf_id : joint->MorphedMeshes) {
|
||||||
const u16 buffer_id = weight.buffer_id;
|
LocalBuffers[buf_id]->setMorph(std::nullopt);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SkinnedLastFrame = false;
|
SkinnedLastFrame = false;
|
||||||
|
@ -449,6 +459,8 @@ void SkinnedMesh::checkForAnimation()
|
||||||
if (HasAnimation && !PreparedForSkinning) {
|
if (HasAnimation && !PreparedForSkinning) {
|
||||||
PreparedForSkinning = true;
|
PreparedForSkinning = true;
|
||||||
|
|
||||||
|
std::unordered_set<u16> bufs;
|
||||||
|
|
||||||
// check for bugs:
|
// check for bugs:
|
||||||
for (auto *joint : AllJoints) {
|
for (auto *joint : AllJoints) {
|
||||||
for (auto &weight : joint->Weights) {
|
for (auto &weight : joint->Weights) {
|
||||||
|
@ -463,6 +475,8 @@ void SkinnedMesh::checkForAnimation()
|
||||||
os::Printer::log("Skinned Mesh: Weight vertex id too large", ELL_WARNING);
|
os::Printer::log("Skinned Mesh: Weight vertex id too large", ELL_WARNING);
|
||||||
weight.buffer_id = weight.vertex_id = 0;
|
weight.buffer_id = weight.vertex_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bufs.insert(weight.buffer_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -472,18 +486,20 @@ void SkinnedMesh::checkForAnimation()
|
||||||
for (u32 j = 0; j < Vertices_Moved[i].size(); ++j)
|
for (u32 j = 0; j < Vertices_Moved[i].size(); ++j)
|
||||||
Vertices_Moved[i][j] = false;
|
Vertices_Moved[i][j] = false;
|
||||||
|
|
||||||
// For skinning: cache weight values for speed
|
for (auto id : bufs) {
|
||||||
|
SkinnedBuffers.emplace_back(id);
|
||||||
|
auto &msp = LocalBuffers[id]->MorphStaticPose;
|
||||||
|
// Make sure we have static pose data for positions & normals
|
||||||
|
msp.get(LocalBuffers[id]->getVertexBuffer(), {
|
||||||
|
msp.positions.empty(),
|
||||||
|
msp.normals.empty(),
|
||||||
|
false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
for (auto *joint : AllJoints) {
|
for (auto *joint : AllJoints) {
|
||||||
for (auto &weight : joint->Weights) {
|
for (auto &weight : joint->Weights) {
|
||||||
const u16 buffer_id = weight.buffer_id;
|
weight.Moved = &Vertices_Moved[weight.buffer_id][weight.vertex_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;
|
|
||||||
|
|
||||||
// weight._Pos=&Buffers[buffer_id]->getVertex(vertex_id)->Pos;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue