2
0
mirror of https://github.com/AxioDL/metaforce.git synced 2025-12-09 04:27:42 +00:00

Add CPU-based skinning path for vertex manipulation effects

This commit is contained in:
Jack Andersen
2017-08-20 19:46:59 -10:00
parent d67d6a1aa7
commit c66f9d0ec7
28 changed files with 465 additions and 123 deletions

View File

@@ -66,6 +66,7 @@ set(GRAPHICS_SOURCES
CPVSAreaSet.hpp CPVSAreaSet.cpp
CGraphics.hpp CGraphics.cpp
CSimpleShadow.hpp CSimpleShadow.cpp
CModelPointSelector.hpp CModelPointSelector.cpp
Shaders/TShader.hpp Shaders/TMultiBlendShader.hpp Shaders/TShaderDecl.hpp Shaders/TMultiBlendShaderDecl.hpp
Shaders/CLineRendererShaders.hpp Shaders/CLineRendererShaders.cpp Shaders/CLineRendererShadersGLSL.cpp
Shaders/CTexturedQuadFilter.hpp Shaders/CTexturedQuadFilter.cpp Shaders/CTexturedQuadFilterGLSL.cpp

View File

@@ -105,11 +105,6 @@ private:
const MaterialSet* x4_matSet;
int m_matSetIdx = -1;
const std::vector<std::shared_ptr<hecl::Runtime::ShaderPipelines>>* m_pipelines;
boo::IVertexFormat* m_vtxFmt;
boo::IGraphicsBufferS* x8_vbo;
boo::IGraphicsBufferS* xc_ibo;
size_t m_weightVecCount;
size_t m_skinBankCount;
std::vector<TCachedToken<CTexture>> x1c_textures;
zeus::CAABox x20_aabb;
CBooSurface* x38_firstUnsortedSurface = nullptr;
@@ -135,9 +130,18 @@ private:
boo::GraphicsDataToken m_gfxToken;
boo::IGraphicsBufferD* m_uniformBuffer;
std::vector<std::vector<boo::IShaderDataBinding*>> m_shaderDataBindings;
boo::IVertexFormat* m_dynamicVtxFmt = nullptr;
boo::IGraphicsBufferD* m_dynamicVbo = nullptr;
boo::IGraphicsBuffer* GetBooVBO(const CBooModel& model, boo::IGraphicsDataFactory::Context& ctx);
boo::IVertexFormat* GetBooVtxFmt(const CBooModel& model, boo::IGraphicsDataFactory::Context& ctx);
};
std::vector<ModelInstance> m_instances;
boo::IVertexFormat* m_staticVtxFmt = nullptr;
boo::IGraphicsBufferS* m_staticVbo = nullptr;
boo::IGraphicsBufferS* m_staticIbo = nullptr;
boo::ITexture* m_txtrOverrides[8] = {};
ModelInstance* PushNewModelInstance();
@@ -156,7 +160,7 @@ public:
~CBooModel();
CBooModel(TToken<CModel>& token, std::vector<CBooSurface>* surfaces, SShader& shader,
boo::IVertexFormat* vtxFmt, boo::IGraphicsBufferS* vbo, boo::IGraphicsBufferS* ibo,
size_t weightVecCount, size_t skinBankCount, const zeus::CAABox& aabb, u8 renderMask,
const zeus::CAABox& aabb, u8 renderMask,
int numInsts, boo::ITexture* txtrOverrides[8]);
static void MakeTexturesFromMats(const MaterialSet& matSet,
@@ -173,9 +177,9 @@ public:
void UnlockTextures() const;
void Touch(int shaderIdx) const;
void VerifyCurrentShader(int shaderIdx);
void UpdateUniformData(const CModelFlags& flags,
const CSkinRules* cskr,
const CPoseAsTransforms* pose) const;
boo::IGraphicsBufferD* UpdateUniformData(const CModelFlags& flags,
const CSkinRules* cskr,
const CPoseAsTransforms* pose) const;
void DrawAlpha(const CModelFlags& flags,
const CSkinRules* cskr,
const CPoseAsTransforms* pose) const;
@@ -224,12 +228,11 @@ class CModel
/* urde addition: boo! */
boo::GraphicsDataToken m_gfxToken;
boo::IGraphicsBufferS* m_vbo;
boo::IVertexFormat* m_staticVtxFmt = nullptr;
boo::IGraphicsBufferS* m_staticVbo = nullptr;
hecl::HMDLMeta m_hmdlMeta;
std::unique_ptr<uint8_t[]> m_dynamicVertexData;
boo::IGraphicsBufferS* m_ibo;
boo::IVertexFormat* m_vtxFmt;
u32 m_weightVecCount;
u32 m_skinBankCount;
public:
using MaterialSet = DataSpec::DNAMP1::HMDLMaterialSet;
@@ -246,6 +249,13 @@ public:
const CBooModel& GetInstance() const {return *x28_modelInst;}
std::unique_ptr<CBooModel> MakeNewInstance(int shaderIdx, int subInsts, boo::ITexture* txtrOverrides[8] = nullptr);
void UpdateLastFrame() const { const_cast<CModel&>(*this).x38_lastFrame = CGraphics::GetFrameCounter(); }
size_t GetPoolVertexOffset(size_t idx) const;
zeus::CVector3f GetPoolVertex(size_t idx) const;
size_t GetPoolNormalOffset(size_t idx) const;
zeus::CVector3f GetPoolNormal(size_t idx) const;
void ApplyVerticesCPU(boo::IGraphicsBufferD* vertBuf,
const std::vector<std::pair<zeus::CVector3f, zeus::CVector3f>>& vn) const;
};
CFactoryFnReturn FModelFactory(const urde::SObjectTag& tag,

View File

@@ -135,12 +135,12 @@ CBooModel::~CBooModel()
CBooModel::CBooModel(TToken<CModel>& token, std::vector<CBooSurface>* surfaces, SShader& shader,
boo::IVertexFormat* vtxFmt, boo::IGraphicsBufferS* vbo, boo::IGraphicsBufferS* ibo,
size_t weightVecCount, size_t skinBankCount, const zeus::CAABox& aabb, u8 renderMask,
int instCount, boo::ITexture* txtrOverrides[8])
const zeus::CAABox& aabb, u8 renderMask,
int numInsts, boo::ITexture* txtrOverrides[8])
: m_model(token), x0_surfaces(surfaces), x4_matSet(&shader.m_matSet), m_matSetIdx(shader.m_matSetIdx),
m_pipelines(&shader.m_shaders), m_vtxFmt(vtxFmt), x8_vbo(vbo), xc_ibo(ibo), m_weightVecCount(weightVecCount),
m_skinBankCount(skinBankCount), x1c_textures(shader.x0_textures), x20_aabb(aabb),
x40_24_texturesLoaded(false), x40_25_modelVisible(0), x41_mask(renderMask)
m_pipelines(&shader.m_shaders), x1c_textures(shader.x0_textures), x20_aabb(aabb),
x40_24_texturesLoaded(false), x40_25_modelVisible(0), x41_mask(renderMask),
m_staticVtxFmt(vtxFmt), m_staticVbo(vbo), m_staticIbo(ibo)
{
if (txtrOverrides)
for (int i=0 ; i<8 ; ++i)
@@ -174,11 +174,41 @@ CBooModel::CBooModel(TToken<CModel>& token, std::vector<CBooSurface>* surfaces,
}
}
m_instances.reserve(instCount);
for (int i=0 ; i<instCount ; ++i)
m_instances.reserve(numInsts);
for (int i=0 ; i<numInsts ; ++i)
PushNewModelInstance();
}
boo::IGraphicsBuffer* CBooModel::ModelInstance::GetBooVBO(const CBooModel& model,
boo::IGraphicsDataFactory::Context& ctx)
{
if (model.m_staticVbo)
return model.m_staticVbo;
if (!m_dynamicVbo && model.m_model)
{
const CModel& parent = *model.m_model;
m_dynamicVbo = ctx.newDynamicBuffer(boo::BufferUse::Vertex,
parent.m_hmdlMeta.vertStride, parent.m_hmdlMeta.vertCount);
m_dynamicVbo->load(parent.m_dynamicVertexData.get(),
parent.m_hmdlMeta.vertStride * parent.m_hmdlMeta.vertCount);
}
return m_dynamicVbo;
}
boo::IVertexFormat* CBooModel::ModelInstance::GetBooVtxFmt(const CBooModel& model,
boo::IGraphicsDataFactory::Context& ctx)
{
if (model.m_staticVtxFmt)
return model.m_staticVtxFmt;
if (!m_dynamicVtxFmt && model.m_model)
{
const CModel& parent = *model.m_model;
m_dynamicVtxFmt = hecl::Runtime::HMDLData::NewVertexFormat(ctx, parent.m_hmdlMeta,
GetBooVBO(model, ctx), parent.m_ibo);
}
return m_dynamicVtxFmt;
}
CBooModel::ModelInstance* CBooModel::PushNewModelInstance()
{
if (!x40_24_texturesLoaded)
@@ -188,6 +218,13 @@ CBooModel::ModelInstance* CBooModel::PushNewModelInstance()
Log.report(logvisor::Fatal, "Model buffer overflow");
m_instances.emplace_back();
ModelInstance& newInst = m_instances.back();
size_t skinBankCount = 0;
size_t weightVecCount = 0;
if (const CModel* model = m_model.GetObj())
{
skinBankCount = model->m_hmdlMeta.bankCount;
weightVecCount = model->m_hmdlMeta.weightCount;
}
newInst.m_gfxToken = CGraphics::CommitResources(
[&](boo::IGraphicsDataFactory::Context& ctx) -> bool
@@ -195,8 +232,8 @@ CBooModel::ModelInstance* CBooModel::PushNewModelInstance()
/* Determine space required by uniform buffer */
std::vector<size_t> skinOffs;
std::vector<size_t> skinSizes;
skinOffs.reserve(std::max(size_t(1), m_skinBankCount));
skinSizes.reserve(std::max(size_t(1), m_skinBankCount));
skinOffs.reserve(std::max(size_t(1), skinBankCount));
skinSizes.reserve(std::max(size_t(1), skinBankCount));
std::vector<size_t> uvOffs;
std::vector<size_t> uvSizes;
@@ -205,12 +242,12 @@ CBooModel::ModelInstance* CBooModel::PushNewModelInstance()
/* Vert transform matrices */
size_t uniBufSize = 0;
if (m_skinBankCount)
if (skinBankCount)
{
/* Skinned */
for (size_t i=0 ; i<m_skinBankCount ; ++i)
for (size_t i=0 ; i<skinBankCount ; ++i)
{
size_t thisSz = ROUND_UP_256(sizeof(zeus::CMatrix4f) * (2 * m_weightVecCount * 4 + 1));
size_t thisSz = ROUND_UP_256(sizeof(zeus::CMatrix4f) * (2 * weightVecCount * 4 + 1));
skinOffs.push_back(uniBufSize);
skinSizes.push_back(thisSz);
uniBufSize += thisSz;
@@ -299,7 +336,7 @@ CBooModel::ModelInstance* CBooModel::PushNewModelInstance()
}
texs[7] = g_Renderer->x220_sphereRamp;
if (m_skinBankCount)
if (skinBankCount)
{
thisOffs[0] = skinOffs[surf.m_data.skinMtxBankIdx];
thisSizes[0] = skinSizes[surf.m_data.skinMtxBankIdx];
@@ -361,9 +398,9 @@ CBooModel::ModelInstance* CBooModel::PushNewModelInstance()
ltexs = texs;
}
extendeds.push_back(
ctx.newShaderDataBinding(pipeline, m_vtxFmt,
x8_vbo, nullptr, xc_ibo, 4, bufs, stages,
thisOffs, thisSizes, texCount, ltexs, nullptr, nullptr));
ctx.newShaderDataBinding(pipeline, newInst.GetBooVtxFmt(*this, ctx),
newInst.GetBooVBO(*this, ctx), nullptr, m_staticIbo, 4, bufs,
stages, thisOffs, thisSizes, texCount, ltexs, nullptr, nullptr));
++idx;
}
}
@@ -695,16 +732,24 @@ void CBooModel::UVAnimationBuffer::Update(u8*& bufOut, const MaterialSet* matSet
}
}
void CBooModel::UpdateUniformData(const CModelFlags& flags,
const CSkinRules* cskr,
const CPoseAsTransforms* pose) const
boo::IGraphicsBufferD* CBooModel::UpdateUniformData(const CModelFlags& flags,
const CSkinRules* cskr,
const CPoseAsTransforms* pose) const
{
size_t skinBankCount = 0;
size_t weightVecCount = 0;
if (const CModel* model = m_model.GetObj())
{
skinBankCount = model->m_hmdlMeta.bankCount;
weightVecCount = model->m_hmdlMeta.weightCount;
}
const ModelInstance* inst;
if (m_instances.size() <= m_uniUpdateCount)
{
inst = const_cast<CBooModel*>(this)->PushNewModelInstance();
if (!inst)
return;
return nullptr;
}
else
inst = &m_instances[m_uniUpdateCount];
@@ -713,13 +758,13 @@ void CBooModel::UpdateUniformData(const CModelFlags& flags,
u8* dataOut = reinterpret_cast<u8*>(inst->m_uniformBuffer->map(m_uniformDataSize));
u8* dataCur = dataOut;
if (m_skinBankCount)
if (skinBankCount)
{
/* Skinned */
std::vector<const zeus::CTransform*> bankTransforms;
size_t weightCount = m_weightVecCount * 4;
size_t weightCount = weightVecCount * 4;
bankTransforms.reserve(weightCount);
for (size_t i=0 ; i<m_skinBankCount ; ++i)
for (size_t i=0 ; i<skinBankCount ; ++i)
{
if (cskr && pose)
{
@@ -844,6 +889,7 @@ void CBooModel::UpdateUniformData(const CModelFlags& flags,
}
inst->m_uniformBuffer->unmap();
return inst->m_dynamicVbo;
}
void CBooModel::DrawAlpha(const CModelFlags& flags,
@@ -879,10 +925,10 @@ void CBooModel::Draw(const CModelFlags& flags,
}
}
static const u8* MemoryFromPartData(const u8*& dataCur, const s32*& secSizeCur)
static const u8* MemoryFromPartData(const u8*& dataCur, const u32*& secSizeCur)
{
const u8* ret;
if (*secSizeCur)
if (*secSizeCur != 0)
ret = dataCur;
else
ret = nullptr;
@@ -897,7 +943,7 @@ std::unique_ptr<CBooModel> CModel::MakeNewInstance(int shaderIdx, int subInsts,
if (shaderIdx >= x18_matSets.size())
shaderIdx = 0;
return std::make_unique<CBooModel>(m_selfToken, &x8_surfaces, x18_matSets[shaderIdx],
m_vtxFmt, m_vbo, m_ibo, m_weightVecCount, m_skinBankCount,
m_staticVtxFmt, m_staticVbo, m_ibo,
m_aabb, (m_flags & 0x2) != 0, subInsts, txtrOverrides);
}
@@ -916,7 +962,7 @@ CModel::CModel(std::unique_ptr<u8[]>&& in, u32 /* dataLen */, IObjectStore* stor
u32 matSetCount = hecl::SBig(*reinterpret_cast<u32*>(data.get() + 0x28));
x18_matSets.reserve(matSetCount);
const u8* dataCur = data.get() + ROUND_UP_32(0x2c + secCount * 4);
const s32* secSizeCur = reinterpret_cast<const s32*>(data.get() + 0x2c);
const u32* secSizeCur = reinterpret_cast<const u32*>(data.get() + 0x2c);
for (u32 i=0 ; i<matSetCount ; ++i)
{
u32 matSetSz = hecl::SBig(*secSizeCur);
@@ -928,15 +974,12 @@ CModel::CModel(std::unique_ptr<u8[]>&& in, u32 /* dataLen */, IObjectStore* stor
CBooModel::MakeTexturesFromMats(shader.m_matSet, shader.x0_textures, *store);
}
hecl::HMDLMeta hmdlMeta;
{
u32 hmdlSz = hecl::SBig(*secSizeCur);
const u8* hmdlMetadata = MemoryFromPartData(dataCur, secSizeCur);
athena::io::MemoryReader r(hmdlMetadata, hmdlSz);
hmdlMeta.read(r);
m_hmdlMeta.read(r);
}
m_weightVecCount = hmdlMeta.weightCount;
m_skinBankCount = hmdlMeta.bankCount;
const u8* vboData = MemoryFromPartData(dataCur, secSizeCur);
const u8* iboData = MemoryFromPartData(dataCur, secSizeCur);
@@ -955,8 +998,8 @@ CModel::CModel(std::unique_ptr<u8[]>&& in, u32 /* dataLen */, IObjectStore* stor
else
reflectionType = hecl::Backend::ReflectionType::None;
hecl::Runtime::ShaderTag tag(mat.heclIr,
hmdlMeta.colorCount, hmdlMeta.uvCount, hmdlMeta.weightCount,
hmdlMeta.weightCount * 4, 8, boo::Primitive(hmdlMeta.topology),
m_hmdlMeta.colorCount, m_hmdlMeta.uvCount, m_hmdlMeta.weightCount,
m_hmdlMeta.weightCount * 4, 8, boo::Primitive(m_hmdlMeta.topology),
reflectionType, true, true, true);
matSet.m_shaders.push_back(CModelShaders::g_ModelShaders->buildExtendedShader
(tag, mat.heclIr, "CMDL", *CGraphics::g_BooFactory));
@@ -965,9 +1008,23 @@ CModel::CModel(std::unique_ptr<u8[]>&& in, u32 /* dataLen */, IObjectStore* stor
m_gfxToken = CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{
m_vbo = ctx.newStaticBuffer(boo::BufferUse::Vertex, vboData, hmdlMeta.vertStride, hmdlMeta.vertCount);
m_ibo = ctx.newStaticBuffer(boo::BufferUse::Index, iboData, 4, hmdlMeta.indexCount);
m_vtxFmt = hecl::Runtime::HMDLData::NewVertexFormat(ctx, hmdlMeta, m_vbo, m_ibo);
if (!m_hmdlMeta.bankCount)
{
/* Non-skinned models use static vertex buffers shared with CBooModel instances */
m_staticVbo = ctx.newStaticBuffer(boo::BufferUse::Vertex, vboData,
m_hmdlMeta.vertStride, m_hmdlMeta.vertCount);
m_staticVtxFmt = hecl::Runtime::HMDLData::NewVertexFormat(ctx, m_hmdlMeta, m_staticVbo, m_ibo);
}
else
{
/* Skinned models use per-instance dynamic buffers for vertex manipulation effects */
size_t vboSz = m_hmdlMeta.vertStride * m_hmdlMeta.vertCount;
m_dynamicVertexData.reset(new uint8_t[vboSz]);
memmove(m_dynamicVertexData.get(), vboData, vboSz);
}
/* Index buffer is always static */
m_ibo = ctx.newStaticBuffer(boo::BufferUse::Index, iboData, 4, m_hmdlMeta.indexCount);
return true;
});
@@ -1042,6 +1099,46 @@ bool CModel::IsLoaded(int shaderIdx) const
return loaded;
}
size_t CModel::GetPoolVertexOffset(size_t idx) const
{
return m_hmdlMeta.vertStride * idx;
}
zeus::CVector3f CModel::GetPoolVertex(size_t idx) const
{
auto* floats = reinterpret_cast<const float*>(m_dynamicVertexData.get() + GetPoolVertexOffset(idx));
return {floats[0], floats[1], floats[2]};
}
size_t CModel::GetPoolNormalOffset(size_t idx) const
{
return m_hmdlMeta.vertStride * idx + 12;
}
zeus::CVector3f CModel::GetPoolNormal(size_t idx) const
{
auto* floats = reinterpret_cast<const float*>(m_dynamicVertexData.get() + GetPoolNormalOffset(idx));
return {floats[0], floats[1], floats[2]};
}
void CModel::ApplyVerticesCPU(boo::IGraphicsBufferD* vertBuf,
const std::vector<std::pair<zeus::CVector3f, zeus::CVector3f>>& vn) const
{
u8* data = reinterpret_cast<u8*>(vertBuf->map(m_hmdlMeta.vertStride * m_hmdlMeta.vertCount));
for (u32 i=0 ; i<std::min(u32(vn.size()), m_hmdlMeta.vertCount) ; ++i)
{
const std::pair<zeus::CVector3f, zeus::CVector3f>& avn = vn[i];
float* floats = reinterpret_cast<float*>(data + GetPoolVertexOffset(i));
floats[0] = avn.first.x;
floats[1] = avn.first.y;
floats[2] = avn.first.z;
floats[3] = avn.second.x;
floats[4] = avn.second.y;
floats[5] = avn.second.z;
}
vertBuf->unmap();
}
CFactoryFnReturn FModelFactory(const urde::SObjectTag& tag,
std::unique_ptr<u8[]>&& in, u32 len,
const urde::CVParamTransfer& vparms,

View File

@@ -0,0 +1,16 @@
#include "CModelPointSelector.hpp"
namespace urde
{
CModelPointSelector::CModelPointSelector(const zeus::CVector3f& scale, int, int, float, float)
{
}
void CModelPointSelector::GeneratePoints(const std::vector<std::pair<zeus::CVector3f, zeus::CVector3f>>& vn)
{
}
}

View File

@@ -0,0 +1,18 @@
#ifndef URDE_CMODELPOINTSELECTOR_HPP
#define URDE_CMODELPOINTSELECTOR_HPP
#include "RetroTypes.hpp"
namespace urde
{
class CModelPointSelector
{
public:
CModelPointSelector(const zeus::CVector3f& scale, int, int, float, float);
void GeneratePoints(const std::vector<std::pair<zeus::CVector3f, zeus::CVector3f>>& vn);
};
}
#endif // URDE_CMODELPOINTSELECTOR_HPP

View File

@@ -1,5 +1,6 @@
#include "CSkinnedModel.hpp"
#include "Character/CSkinRules.hpp"
#include "CVertexMorphEffect.hpp"
namespace urde
{
@@ -35,7 +36,20 @@ void CSkinnedModel::Calculate(const CPoseAsTransforms& pose,
const rstl::optional_object<CVertexMorphEffect>& morphEffect,
const float* morphMagnitudes)
{
m_modelInst->UpdateUniformData(drawFlags, x10_skinRules.GetObj(), &pose);
if (morphEffect || g_PointGenFunc)
{
boo::IGraphicsBufferD* vertBuf = m_modelInst->UpdateUniformData(drawFlags, nullptr, nullptr);
x10_skinRules->TransformVerticesCPU(m_vertWorkspace, pose, *x4_model);
if (morphEffect)
morphEffect->MorphVertices(m_vertWorkspace, morphMagnitudes, x10_skinRules, pose);
if (g_PointGenFunc)
g_PointGenFunc(g_PointGenCtx, m_vertWorkspace);
x4_model->ApplyVerticesCPU(vertBuf, m_vertWorkspace);
}
else
{
m_modelInst->UpdateUniformData(drawFlags, x10_skinRules.GetObj(), &pose);
}
}
void CSkinnedModel::Draw(const CModelFlags& drawFlags) const

View File

@@ -21,6 +21,7 @@ class CSkinnedModel
TLockedToken<CModel> x4_model;
TLockedToken<CSkinRules> x10_skinRules;
TLockedToken<CCharLayoutInfo> x1c_layoutInfo;
std::vector<std::pair<zeus::CVector3f, zeus::CVector3f>> m_vertWorkspace;
public:
enum class EDataOwnership
{
@@ -45,7 +46,7 @@ public:
const float* morphMagnitudes);
void Draw(const CModelFlags& drawFlags) const;
typedef void(*FPointGenerator)(void* item, const zeus::CVector3f* v1, const zeus::CVector3f* v2, int w1);
typedef void(*FPointGenerator)(void* item, const std::vector<std::pair<zeus::CVector3f, zeus::CVector3f>>& vn);
static void SetPointGeneratorFunc(void* ctx, FPointGenerator func)
{
g_PointGenFunc = func;

View File

@@ -0,0 +1,14 @@
#include "CVertexMorphEffect.hpp"
#include "Character/CSkinRules.hpp"
namespace urde
{
void CVertexMorphEffect::MorphVertices(std::vector<std::pair<zeus::CVector3f, zeus::CVector3f>>& vn,
const float* magnitudes, const TLockedToken<CSkinRules>& skinRules,
const CPoseAsTransforms& pose) const
{
}
}

View File

@@ -1,11 +1,19 @@
#ifndef __URDE_CVERTEXMORPHEFFECT_HPP__
#define __URDE_CVERTEXMORPHEFFECT_HPP__
#include "CToken.hpp"
#include "Character/CPoseAsTransforms.hpp"
namespace urde
{
class CSkinRules;
class CVertexMorphEffect
{
public:
void MorphVertices(std::vector<std::pair<zeus::CVector3f, zeus::CVector3f>>& vn,
const float* magnitudes, const TLockedToken<CSkinRules>& skinRules,
const CPoseAsTransforms& pose) const;
};
}