# Fixed skeletal animation algorithm.
* Skinning is done in a way that works when there's more than one bone influencing a vertex. * PMDs now store vertexes in world-space instead of bind-space. (The loader converts the old-version PMDs so they still work.) * Moved SkinPoint, SkinNormal into CModelDef so it could use them when loading the old PMDs. * Made the FastNormals approach non-optional, so the inverse-transpose bone matrices could be removed. Changed the explanation of why it's a valid approach. * Quaternion: Made GetInverse assume that the quaternions have unit length (which they do when they're representing 3D rotations). * lib: Added support for DDS files that aren't a multiple of 4x4 (most useful for 1x1, 2x2, etc that are still powers of two). * Actor Viewer: Added white terrain texture to the minimal test mod, so shadows are visible. Changed default so walk/run animations don't move the unit along the ground. * Removed some redundant repetition in doc comments. * Removed some unnecessary #includes. This was SVN commit r4696.
This commit is contained in:
parent
fc97a81743
commit
173c56140c
BIN
binaries/data/mods/_test.minimal/art/textures/terrain/types/whiteness.dds
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/_test.minimal/art/textures/terrain/types/whiteness.dds
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -55,7 +55,6 @@ struct BoneTransform
|
||||
{
|
||||
float translation[3];
|
||||
float orientation[4];
|
||||
FMMatrix44 matrix; // not output in the PMD, but useful for calculating bone transforms
|
||||
};
|
||||
|
||||
|
||||
@ -146,11 +145,7 @@ public:
|
||||
FCDGeometryPolygons* polys = GetPolysFromGeometry((FCDGeometry*)baseTarget);
|
||||
|
||||
// Make sure it doesn't use more bones per vertex than the game can handle
|
||||
// SkinReduceInfluences(skin, maxInfluences, 0.001f);
|
||||
|
||||
// XXX The game is broken if there's >1 influence per vertex. Until
|
||||
// that's fixed, just limit it to 1 and put up with the ugly blending...
|
||||
SkinReduceInfluences(skin, 1, 0.001f);
|
||||
SkinReduceInfluences(skin, maxInfluences, 0.001f);
|
||||
|
||||
// Convert the bone influences into VertexBlend structures for the PMD
|
||||
|
||||
@ -207,8 +202,7 @@ public:
|
||||
|
||||
BoneTransform b = {
|
||||
{ parts.t.x, parts.t.y, parts.t.z },
|
||||
{ parts.q.x, parts.q.y, parts.q.z, parts.q.w },
|
||||
bindPose
|
||||
{ parts.q.x, parts.q.y, parts.q.z, parts.q.w }
|
||||
};
|
||||
|
||||
int boneId = StdSkeletons::FindStandardBoneID(joint->joint->GetName());
|
||||
@ -231,7 +225,7 @@ public:
|
||||
assert(normal.size() == vertices*3);
|
||||
assert(texcoord.size() == vertices*2);
|
||||
|
||||
TransformVertices(position, normal, blends, bones, transform);
|
||||
TransformVertices(position, normal, bones, transform);
|
||||
|
||||
WritePMD(output, vertices, bones.size(), &position[0], &normal[0], &texcoord[0], &blends[0], &bones[0]);
|
||||
}
|
||||
@ -257,7 +251,7 @@ public:
|
||||
if (boneCount) assert(boneWeights && boneTransforms);
|
||||
|
||||
output("PSMD", 4); // magic number
|
||||
write<uint32>(output, 2); // version number
|
||||
write<uint32>(output, 3); // version number
|
||||
write<uint32>(output, (uint32)(
|
||||
4 + 13*4*vertexCount + // vertices
|
||||
4 + 6*vertexCount/3 + // faces
|
||||
@ -364,52 +358,17 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
static void TransformVertices(FloatList& position, FloatList& normal, std::vector<VertexBlend>& blends, std::vector<BoneTransform>& bones, const FMMatrix44& transform)
|
||||
static void TransformVertices(FloatList& position, FloatList& normal, std::vector<BoneTransform>& bones, const FMMatrix44& transform)
|
||||
{
|
||||
for (size_t vtxId = 0; vtxId < position.size()/3; ++vtxId)
|
||||
{
|
||||
// Skinned vertices need to be transformed by the inverse of their
|
||||
// rest states:
|
||||
|
||||
float zero16[16] = {0};
|
||||
FMMatrix44 bindPoseTransform (zero16);
|
||||
|
||||
// Calculate the weighted sum of influence matrices
|
||||
for (size_t j = 0; j < maxInfluences; ++j)
|
||||
{
|
||||
// Ignore unused bone influences
|
||||
if (blends[vtxId].bones[j] == 0xff)
|
||||
continue;
|
||||
|
||||
float weight = blends[vtxId].weights[j];
|
||||
const BoneTransform& b = bones[blends[vtxId].bones[j]];
|
||||
|
||||
// The transformation matrix could be reconstructed with:
|
||||
/*
|
||||
FMMatrix44 R = QuatToMatrix(b.orientation[0], b.orientation[1], b.orientation[2], b.orientation[3]);
|
||||
FMMatrix44 T = FMMatrix44::TranslationMatrix(FMVector3(b.translation, 0));
|
||||
FMMatrix44 boneMatrix = T * R;
|
||||
*/
|
||||
// but since we generated orientation/translation from a matrix,
|
||||
// just use that matrix directly:
|
||||
FMMatrix44 boneMatrix = b.matrix;
|
||||
|
||||
bindPoseTransform = bindPoseTransform + weight * boneMatrix;
|
||||
}
|
||||
|
||||
FMVector3 pos (&position[vtxId*3], 0);
|
||||
FMVector3 norm (&normal[vtxId*3], 0);
|
||||
|
||||
// Apply the scene-node transforms first
|
||||
// Apply the scene-node transforms
|
||||
pos = transform.TransformCoordinate(pos);
|
||||
norm = transform.TransformVector(norm).Normalize();
|
||||
|
||||
// Apply the inverse bind pose transform, so the model will display
|
||||
// correctly after it's been transform back again
|
||||
FMMatrix44 bindPoseTransformInverse = bindPoseTransform.Inverted();
|
||||
pos = bindPoseTransformInverse.TransformCoordinate(pos);
|
||||
norm = bindPoseTransformInverse.TransformVector(norm);
|
||||
|
||||
// Switch from Max's coordinate system into the game's:
|
||||
|
||||
std::swap(pos.y, pos.z);
|
||||
|
@ -27,9 +27,9 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
CModel::CModel()
|
||||
: m_Parent(0), m_Flags(0), m_Anim(0), m_AnimTime(0),
|
||||
m_BoneMatrices(0), m_InvTranspBoneMatrices(0),
|
||||
m_PositionValid(false), m_InvTranspValid(false), m_ShadingColor(1,1,1,1)
|
||||
: m_Parent(NULL), m_Flags(0), m_Anim(NULL), m_AnimTime(0),
|
||||
m_BoneMatrices(NULL), m_InverseBindBoneMatrices(NULL),
|
||||
m_PositionValid(false), m_ShadingColor(1,1,1,1)
|
||||
{
|
||||
}
|
||||
|
||||
@ -62,8 +62,9 @@ CModel::~CModel()
|
||||
void CModel::ReleaseData()
|
||||
{
|
||||
delete[] m_BoneMatrices;
|
||||
delete[] m_InvTranspBoneMatrices;
|
||||
for (size_t i=0;i<m_Props.size();i++) {
|
||||
delete[] m_InverseBindBoneMatrices;
|
||||
for (size_t i = 0; i < m_Props.size(); ++i)
|
||||
{
|
||||
m_Props[i].m_Model->m_Parent = 0;
|
||||
delete m_Props[i].m_Model;
|
||||
}
|
||||
@ -84,19 +85,24 @@ bool CModel::InitModel(CModelDefPtr modeldef)
|
||||
|
||||
m_pModelDef = modeldef;
|
||||
|
||||
size_t numBones=modeldef->GetNumBones();
|
||||
if (numBones != 0) {
|
||||
size_t numBones = modeldef->GetNumBones();
|
||||
if (numBones != 0)
|
||||
{
|
||||
// allocate matrices for bone transformations
|
||||
m_BoneMatrices=new CMatrix3D[numBones];
|
||||
m_InvTranspBoneMatrices=new CMatrix3D[numBones];
|
||||
m_BoneMatrices = new CMatrix3D[numBones];
|
||||
m_InverseBindBoneMatrices = new CMatrix3D[numBones];
|
||||
|
||||
// store default pose until animation assigned
|
||||
CBoneState* defpose=modeldef->GetBones();
|
||||
for (uint i=0;i<numBones;i++) {
|
||||
CMatrix3D& m=m_BoneMatrices[i];
|
||||
m.SetIdentity();
|
||||
m.Rotate(defpose[i].m_Rotation);
|
||||
m.Translate(defpose[i].m_Translation);
|
||||
m_InvTranspValid = false;
|
||||
CBoneState* defpose = modeldef->GetBones();
|
||||
for (size_t i = 0; i < numBones; ++i)
|
||||
{
|
||||
m_BoneMatrices[i].SetIdentity();
|
||||
m_BoneMatrices[i].Rotate(defpose[i].m_Rotation);
|
||||
m_BoneMatrices[i].Translate(defpose[i].m_Translation);
|
||||
|
||||
m_InverseBindBoneMatrices[i].SetIdentity();
|
||||
m_InverseBindBoneMatrices[i].Translate(-defpose[i].m_Translation);
|
||||
m_InverseBindBoneMatrices[i].Rotate(defpose[i].m_Rotation.GetInverse());
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,33 +112,6 @@ bool CModel::InitModel(CModelDefPtr modeldef)
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SkinPoint: skin the given point using the given blend and matrix data
|
||||
static CVector3D SkinPoint(const CVector3D& pos, const SVertexBlend& blend,
|
||||
const CMatrix3D* bonestates)
|
||||
{
|
||||
CVector3D result,tmp;
|
||||
|
||||
// must have at least one valid bone if we're using SkinPoint
|
||||
if (blend.m_Bone[0] == 0xff)
|
||||
{
|
||||
ONCE( debug_warn("SkinPoint called for vertex with no bone weights") );
|
||||
return CVector3D(0, 0, 0);
|
||||
}
|
||||
|
||||
const CMatrix3D& m = bonestates[blend.m_Bone[0]];
|
||||
m.Transform(pos, result);
|
||||
result *= blend.m_Weight[0];
|
||||
|
||||
for (int i = 1; i < SVertexBlend::SIZE && blend.m_Bone[i] != 0xff; i++) {
|
||||
const CMatrix3D& m = bonestates[blend.m_Bone[i]];
|
||||
m.Transform(pos, tmp);
|
||||
result += tmp*blend.m_Weight[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// CalcBound: calculate the world space bounds of this model
|
||||
void CModel::CalcBounds()
|
||||
@ -203,9 +182,9 @@ void CModel::CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result)
|
||||
ValidatePosition();
|
||||
|
||||
// extend bounds by vertex positions at the frame
|
||||
for (size_t i=0;i<numverts;i++) {
|
||||
CVector3D tmp = SkinPoint(verts[i].m_Coords,verts[i].m_Blend,GetBoneMatrices());
|
||||
result+=tmp;
|
||||
for (size_t i=0;i<numverts;i++)
|
||||
{
|
||||
result += CModelDef::SkinPoint(verts[i], GetAnimatedBoneMatrices(), GetInverseBindBoneMatrices());
|
||||
}
|
||||
// advance to next frame
|
||||
m_AnimTime += anim->GetFrameTime();
|
||||
@ -322,7 +301,6 @@ void CModel::ValidatePosition()
|
||||
}
|
||||
|
||||
m_PositionValid = true;
|
||||
m_InvTranspValid = false;
|
||||
|
||||
// re-position and validate all props
|
||||
for (size_t j = 0; j < m_Props.size(); ++j)
|
||||
@ -341,24 +319,6 @@ void CModel::ValidatePosition()
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// CalcInvTranspBoneMatrices
|
||||
void CModel::CalcInvTranspBoneMatrices()
|
||||
{
|
||||
debug_assert(m_BoneMatrices);
|
||||
|
||||
PROFILE( "invert transpose bone matrices" );
|
||||
|
||||
CMatrix3D tmp;
|
||||
for(size_t i = 0; i < m_pModelDef->GetNumBones(); ++i)
|
||||
{
|
||||
m_BoneMatrices[i].GetInverse(tmp);
|
||||
tmp.GetTranspose(m_InvTranspBoneMatrices[i]);
|
||||
}
|
||||
|
||||
m_InvTranspValid = true;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SetAnimation: set the given animation as the current animation on this model;
|
||||
// return false on error, else true
|
||||
|
@ -101,24 +101,26 @@ public:
|
||||
void CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result);
|
||||
|
||||
/**
|
||||
* SetTransform: Set transform of this object.
|
||||
* Set transform of this object.
|
||||
*
|
||||
* @note In order to ensure that all child props are updated properly,
|
||||
* you must call ValidatePosition().
|
||||
*/
|
||||
void SetTransform(const CMatrix3D& transform);
|
||||
|
||||
/**
|
||||
* Return whether this is a skinned/skeletal model. If it is, Get*BoneMatrices()
|
||||
* will return valid non-NULL arrays.
|
||||
*/
|
||||
bool IsSkinned() { return (m_BoneMatrices != NULL); }
|
||||
|
||||
// return the models bone matrices
|
||||
const CMatrix3D* GetBoneMatrices() {
|
||||
const CMatrix3D* GetAnimatedBoneMatrices() {
|
||||
debug_assert(m_PositionValid);
|
||||
return m_BoneMatrices;
|
||||
}
|
||||
// return the models inverted transposed bone matrices for normal transformation
|
||||
const CMatrix3D* GetInvTranspBoneMatrices() {
|
||||
debug_assert(m_PositionValid);
|
||||
if (!m_InvTranspValid)
|
||||
CalcInvTranspBoneMatrices();
|
||||
return m_InvTranspBoneMatrices;
|
||||
const CMatrix3D* GetInverseBindBoneMatrices() {
|
||||
return m_InverseBindBoneMatrices;
|
||||
}
|
||||
|
||||
// load raw animation frame animation from given file, and build a
|
||||
@ -137,7 +139,7 @@ public:
|
||||
CModel* Clone() const;
|
||||
|
||||
/**
|
||||
* ValidatePosition: Ensure that both the transformation and the bone
|
||||
* Ensure that both the transformation and the bone
|
||||
* matrices are correct for this model and all its props.
|
||||
*/
|
||||
void ValidatePosition();
|
||||
@ -147,19 +149,13 @@ private:
|
||||
void ReleaseData();
|
||||
|
||||
/**
|
||||
* InvalidatePosition: Mark this model's position and bone matrices,
|
||||
* Mark this model's position and bone matrices,
|
||||
* and all props' positions as invalid.
|
||||
*/
|
||||
void InvalidatePosition();
|
||||
|
||||
/**
|
||||
* CalcInvTranspBoneMatrices: Calc inverse transpose bone matrices
|
||||
* from bone matrices.
|
||||
*/
|
||||
void CalcInvTranspBoneMatrices();
|
||||
|
||||
/**
|
||||
* m_Parent: If non-null, m_Parent points to the model that we
|
||||
* If non-null, m_Parent points to the model that we
|
||||
* are attached to.
|
||||
*/
|
||||
CModel* m_Parent;
|
||||
@ -186,23 +182,16 @@ private:
|
||||
float m_AnimTime;
|
||||
// current state of all bones on this model; null if associated modeldef isn't skeletal
|
||||
CMatrix3D* m_BoneMatrices;
|
||||
// inverse of the transpose of the above matrices
|
||||
CMatrix3D* m_InvTranspBoneMatrices;
|
||||
// inverse matrices for the bind pose's bones; null if not skeletal
|
||||
CMatrix3D* m_InverseBindBoneMatrices;
|
||||
// list of current props on model
|
||||
std::vector<Prop> m_Props;
|
||||
|
||||
/**
|
||||
* m_PositionValid: true if both transform and and bone matrices
|
||||
* are valid.
|
||||
* true if both transform and and bone matrices are valid.
|
||||
*/
|
||||
bool m_PositionValid;
|
||||
|
||||
/**
|
||||
* m_InvTranspValid: true if m_InvTranspBoneMatrices match the current
|
||||
* m_BoneMatrices
|
||||
*/
|
||||
bool m_InvTranspValid;
|
||||
|
||||
// modulating color
|
||||
CColor m_ShadingColor;
|
||||
};
|
||||
|
@ -9,9 +9,73 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "ModelDef.h"
|
||||
#include "graphics/SkeletonAnimDef.h"
|
||||
#include "ps/FilePacker.h"
|
||||
#include "ps/FileUnpacker.h"
|
||||
#include "maths/Vector4D.h"
|
||||
|
||||
CVector3D CModelDef::SkinPoint(const SModelVertex& vtx,
|
||||
const CMatrix3D newPoseMatrices[],
|
||||
const CMatrix3D inverseBindMatrices[])
|
||||
{
|
||||
CVector3D result (0, 0, 0);
|
||||
|
||||
for (int i = 0; i < SVertexBlend::SIZE && vtx.m_Blend.m_Bone[i] != 0xff; ++i)
|
||||
{
|
||||
CVector3D bindSpace = inverseBindMatrices[vtx.m_Blend.m_Bone[i]].Transform(vtx.m_Coords);
|
||||
CVector3D worldSpace = newPoseMatrices[vtx.m_Blend.m_Bone[i]].Transform(bindSpace);
|
||||
result += worldSpace * vtx.m_Blend.m_Weight[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CVector3D CModelDef::SkinNormal(const SModelVertex& vtx,
|
||||
const CMatrix3D newPoseMatrices[],
|
||||
const CMatrix3D inverseBindMatrices[])
|
||||
{
|
||||
// To be correct, the normal vectors apparently need to be multiplied by the
|
||||
// inverse of the transpose. Unfortunately inverses are slow.
|
||||
// If a matrix is orthogonal, M * M^T = I and so the inverse of the transpose
|
||||
// is the original matrix. But that's not entirely relevant here, because
|
||||
// the cone matrices include translation components and so they're not
|
||||
// orthogonal.
|
||||
// But that's okay because we have
|
||||
// M = T * R
|
||||
// and want to find
|
||||
// n' = (M^T^-1) * n
|
||||
// = (T * R)^T^-1 * n
|
||||
// = (R^T * T^T)^-1 * n
|
||||
// = (T^T^-1 * R^T^-1) * n
|
||||
// R is indeed orthogonal so R^T^-1 = R. T isn't orthogonal at all.
|
||||
// But n is only a 3-vector, and from the forms of T and R (which have
|
||||
// lots of zeroes) I can convince myself that replacing T with T^T^-1 has no
|
||||
// effect on anything but the fourth component of M^T^-1 - and the fourth
|
||||
// component is discarded since it has no effect on n', and so we can happily
|
||||
// use n' = M*n.
|
||||
//
|
||||
// (This isn't very good as a proof, but it's better than assuming M is
|
||||
// orthogonal when it's clearly not.)
|
||||
|
||||
CVector3D result (0, 0, 0);
|
||||
|
||||
for (int i = 0; i < SVertexBlend::SIZE && vtx.m_Blend.m_Bone[i] != 0xff; ++i)
|
||||
{
|
||||
CVector3D bindSpace = inverseBindMatrices[vtx.m_Blend.m_Bone[i]].Rotate(vtx.m_Norm);
|
||||
CVector3D worldSpace = newPoseMatrices[vtx.m_Blend.m_Bone[i]].Rotate(bindSpace);
|
||||
result += worldSpace * vtx.m_Blend.m_Weight[i];
|
||||
}
|
||||
|
||||
// If there was more than one influence, the result is probably not going
|
||||
// to be of unit length (since it's a weighted sum of several independent
|
||||
// unit vectors), so we need to normalise it.
|
||||
// (It's fairly common to only have one influence, so it seems sensible to
|
||||
// optimise that case a bit.)
|
||||
if (vtx.m_Blend.m_Bone[1] != 0xff) // if more than one influence
|
||||
result.Normalize();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CModelDef Constructor
|
||||
@ -38,11 +102,10 @@ CModelDef::~CModelDef()
|
||||
// return null if no match (case insensitive search)
|
||||
SPropPoint* CModelDef::FindPropPoint(const char* name) const
|
||||
{
|
||||
for (uint i=0;i<m_NumPropPoints;i++) {
|
||||
if (stricmp(name,m_PropPoints[i].m_Name)==0) {
|
||||
for (u32 i = 0; i < m_NumPropPoints; ++i)
|
||||
if (m_PropPoints[i].m_Name == name)
|
||||
return &m_PropPoints[i];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -60,10 +123,9 @@ CModelDef* CModelDef::Load(const char* filename)
|
||||
throw PSERROR_File_InvalidVersion();
|
||||
}
|
||||
|
||||
CModelDef* mdef=new CModelDef;
|
||||
std::auto_ptr<CModelDef> mdef (new CModelDef);
|
||||
mdef->m_Name = filename;
|
||||
|
||||
try {
|
||||
// now unpack everything
|
||||
unpacker.UnpackRaw(&mdef->m_NumVertices,sizeof(mdef->m_NumVertices));
|
||||
mdef->m_pVertices=new SModelVertex[mdef->m_NumVertices];
|
||||
@ -74,12 +136,14 @@ CModelDef* CModelDef::Load(const char* filename)
|
||||
unpacker.UnpackRaw(mdef->m_pFaces,sizeof(SModelFace)*mdef->m_NumFaces);
|
||||
|
||||
unpacker.UnpackRaw(&mdef->m_NumBones,sizeof(mdef->m_NumBones));
|
||||
if (mdef->m_NumBones) {
|
||||
if (mdef->m_NumBones)
|
||||
{
|
||||
mdef->m_Bones=new CBoneState[mdef->m_NumBones];
|
||||
unpacker.UnpackRaw(mdef->m_Bones,mdef->m_NumBones*sizeof(CBoneState));
|
||||
}
|
||||
|
||||
if (unpacker.GetVersion()>=2) {
|
||||
if (unpacker.GetVersion() >= 2)
|
||||
{
|
||||
// versions >=2 also have prop point data
|
||||
unpacker.UnpackRaw(&mdef->m_NumPropPoints,sizeof(mdef->m_NumPropPoints));
|
||||
if (mdef->m_NumPropPoints) {
|
||||
@ -97,12 +161,38 @@ CModelDef* CModelDef::Load(const char* filename)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (PSERROR_File_UnexpectedEOF&) {
|
||||
delete mdef;
|
||||
throw;
|
||||
|
||||
if (unpacker.GetVersion() <= 2)
|
||||
{
|
||||
// Versions <=2 store the vertexes relative to the bind pose. That
|
||||
// isn't useful when you want to do correct skinning, so later versions
|
||||
// store them in world space. So, fix the old models by skinning each
|
||||
// vertex:
|
||||
|
||||
if (mdef->m_NumBones) // only do skinned models
|
||||
{
|
||||
CMatrix3D identity;
|
||||
identity.SetIdentity();
|
||||
std::vector<CMatrix3D> identityBones (mdef->m_NumBones, identity);
|
||||
|
||||
std::vector<CMatrix3D> bindPose (mdef->m_NumBones);
|
||||
|
||||
for (u32 i = 0; i < mdef->m_NumBones; ++i)
|
||||
{
|
||||
bindPose[i].SetIdentity();
|
||||
bindPose[i].Rotate(mdef->m_Bones[i].m_Rotation);
|
||||
bindPose[i].Translate(mdef->m_Bones[i].m_Translation);
|
||||
}
|
||||
|
||||
return mdef;
|
||||
for (u32 i = 0; i < mdef->m_NumVertices; ++i)
|
||||
{
|
||||
mdef->m_pVertices[i].m_Coords = SkinPoint(mdef->m_pVertices[i], &bindPose[0], &identityBones[0]);
|
||||
mdef->m_pVertices[i].m_Norm = SkinNormal(mdef->m_pVertices[i], &bindPose[0], &identityBones[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mdef.release();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -137,7 +227,6 @@ void CModelDef::Save(const char* filename,const CModelDef* mdef)
|
||||
packer.Write(filename);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// SetRenderData: Set the render data object for the given key,
|
||||
void CModelDef::SetRenderData(const void* key, CModelDefRPrivate* data)
|
||||
|
@ -11,13 +11,12 @@
|
||||
|
||||
#include "ps/CStr.h"
|
||||
#include "maths/Vector3D.h"
|
||||
#include "MeshManager.h"
|
||||
#include "SkeletonAnimDef.h"
|
||||
#include "maths/Quaternion.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
class CMeshManager;
|
||||
class CModelDef;
|
||||
class CBoneState;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// SPropPoint: structure describing a prop point
|
||||
@ -89,7 +88,7 @@ class CModelDef
|
||||
friend class CMeshManager;
|
||||
public:
|
||||
// current file version given to saved animations
|
||||
enum { FILE_VERSION = 2 };
|
||||
enum { FILE_VERSION = 3 };
|
||||
// supported file read version - files with a version less than this will be rejected
|
||||
enum { FILE_READ_VERSION = 1 };
|
||||
|
||||
@ -116,7 +115,6 @@ public:
|
||||
size_t GetNumBones() const { return (size_t)m_NumBones; }
|
||||
CBoneState* GetBones() const { return m_Bones; }
|
||||
|
||||
|
||||
// accessor: get prop data
|
||||
int GetNumPropPoints() const { return m_NumPropPoints; }
|
||||
SPropPoint* GetPropPoints() const { return m_PropPoints; }
|
||||
@ -126,7 +124,23 @@ public:
|
||||
SPropPoint* FindPropPoint(const char* name) const;
|
||||
|
||||
/**
|
||||
* SetRenderData: Register renderer private data. Use the key to
|
||||
* Transform the given vertex's position from the bind pose into the new pose.
|
||||
*
|
||||
* @return new world-space vertex coordinates
|
||||
*/
|
||||
static CVector3D SkinPoint(const SModelVertex& vtx,
|
||||
const CMatrix3D newPoseMatrices[], const CMatrix3D inverseBindMatrices[]);
|
||||
|
||||
/**
|
||||
* Transform the given vertex's normal from the bind pose into the new pose.
|
||||
*
|
||||
* @return new world-space vertex normal
|
||||
*/
|
||||
static CVector3D SkinNormal(const SModelVertex& vtx,
|
||||
const CMatrix3D newPoseMatrices[], const CMatrix3D inverseBindMatrices[]);
|
||||
|
||||
/**
|
||||
* Register renderer private data. Use the key to
|
||||
* distinguish between private data used by different render paths.
|
||||
* The private data will be managed by this CModelDef object:
|
||||
* It will be deleted when CModelDef is destructed or when private
|
||||
|
@ -2,18 +2,17 @@
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "MiniMap.h"
|
||||
|
||||
#include "graphics/GameView.h"
|
||||
#include "graphics/MiniPatch.h"
|
||||
#include "graphics/Model.h"
|
||||
#include "graphics/Terrain.h"
|
||||
#include "graphics/TextureEntry.h"
|
||||
#include "graphics/TextureManager.h"
|
||||
#include "graphics/Unit.h"
|
||||
#include "graphics/UnitManager.h"
|
||||
#include "gui/MiniMap.h"
|
||||
#include "lib/ogl.h"
|
||||
#include "lib/sdl.h"
|
||||
#include "maths/Bound.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/Interact.h"
|
||||
#include "network/NetMessage.h"
|
||||
|
@ -2,9 +2,10 @@
|
||||
#define __H_MINIMAP_H__
|
||||
|
||||
#include "gui/GUI.h"
|
||||
#include "ps/Vector2D.h"
|
||||
#include "maths/Vector3D.h"
|
||||
#include "graphics/Camera.h"
|
||||
|
||||
class CVector2D;
|
||||
class CVector3D;
|
||||
class CCamera;
|
||||
class CTerrain;
|
||||
class CUnitManager;
|
||||
|
||||
|
@ -517,16 +517,28 @@ static LibError decode_sd(const DDSURFACEDESC2* sd, uint* w_, uint* h_,
|
||||
// image dimensions
|
||||
const u32 h = read_le32(&sd->dwHeight);
|
||||
const u32 w = read_le32(&sd->dwWidth);
|
||||
// .. not padded to S3TC block size
|
||||
if(w % 4 || h % 4)
|
||||
WARN_RETURN(ERR::TEX_INVALID_SIZE);
|
||||
|
||||
// pixel format
|
||||
uint bpp, flags;
|
||||
RETURN_ERR(decode_pf(&sd->ddpfPixelFormat, &bpp, &flags));
|
||||
|
||||
// if the image is not aligned with the S3TC block size, it is stored
|
||||
// with extra pixels on the bottom left to fill up the space, so we need
|
||||
// to account for those when calculating how big it should be
|
||||
u32 stored_h, stored_w;
|
||||
if(flags & TEX_DXT)
|
||||
{
|
||||
stored_h = round_up(h, 4);
|
||||
stored_w = round_up(w, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
stored_h = h;
|
||||
stored_w = w;
|
||||
}
|
||||
|
||||
// verify pitch or linear size, if given
|
||||
const size_t pitch = w*bpp/8;
|
||||
const size_t pitch = stored_w*bpp/8;
|
||||
const u32 sd_pitch_or_size = read_le32(&sd->dwPitchOrLinearSize);
|
||||
if(sd_flags & DDSD_PITCH)
|
||||
{
|
||||
@ -535,7 +547,7 @@ static LibError decode_sd(const DDSURFACEDESC2* sd, uint* w_, uint* h_,
|
||||
}
|
||||
if(sd_flags & DDSD_LINEARSIZE)
|
||||
{
|
||||
if(sd_pitch_or_size != pitch*h)
|
||||
if(sd_pitch_or_size != pitch*stored_h)
|
||||
WARN_RETURN(ERR::CORRUPTED);
|
||||
}
|
||||
// note: both flags set would be invalid; no need to check for that,
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include "Matrix3D.h"
|
||||
#include "Quaternion.h"
|
||||
#include "Vector4D.h"
|
||||
|
||||
CMatrix3D::CMatrix3D ()
|
||||
{
|
||||
@ -262,8 +263,10 @@ void CMatrix3D::Scale (float x_scale, float y_scale, float z_scale)
|
||||
|
||||
//Returns the transpose of the matrix. For orthonormal
|
||||
//matrices, this is the same is the inverse matrix
|
||||
void CMatrix3D::GetTranspose(CMatrix3D& result) const
|
||||
CMatrix3D CMatrix3D::GetTranspose() const
|
||||
{
|
||||
CMatrix3D result;
|
||||
|
||||
result._11 = _11;
|
||||
result._21 = _12;
|
||||
result._31 = _13;
|
||||
@ -283,6 +286,8 @@ void CMatrix3D::GetTranspose(CMatrix3D& result) const
|
||||
result._24 = _42;
|
||||
result._34 = _43;
|
||||
result._44 = _44;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -474,6 +479,13 @@ void CMatrix3D::GetInverse(CMatrix3D& dst) const
|
||||
}
|
||||
}
|
||||
|
||||
CMatrix3D CMatrix3D::GetInverse() const
|
||||
{
|
||||
CMatrix3D r;
|
||||
GetInverse(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
void CMatrix3D::Rotate(const CQuaternion& quat)
|
||||
{
|
||||
CMatrix3D rotationMatrix=quat.ToMatrix();
|
||||
|
@ -1,10 +1,8 @@
|
||||
#ifndef __MATRIX3D_H
|
||||
#define __MATRIX3D_H
|
||||
|
||||
#include <math.h>
|
||||
#include "Vector3D.h"
|
||||
#include "Vector4D.h"
|
||||
|
||||
class CVector3D;
|
||||
class CVector4D;
|
||||
class CQuaternion;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
@ -97,8 +95,11 @@ public:
|
||||
// calculate the inverse of this matrix, store in dst
|
||||
void GetInverse(CMatrix3D& dst) const;
|
||||
|
||||
// return the inverse of this matrix
|
||||
CMatrix3D GetInverse() const;
|
||||
|
||||
// calculate the transpose of this matrix, store in dst
|
||||
void GetTranspose(CMatrix3D& dst) const;
|
||||
CMatrix3D GetTranspose() const;
|
||||
|
||||
// return the translation component of this matrix
|
||||
CVector3D GetTranslation() const;
|
||||
|
@ -130,26 +130,21 @@ CMatrix3D CQuaternion::ToMatrix () const
|
||||
|
||||
void CQuaternion::ToMatrix(CMatrix3D& result) const
|
||||
{
|
||||
float x2, y2, z2;
|
||||
float wx, wy, wz, xx, xy, xz, yy, yz, zz;
|
||||
|
||||
// calculate coefficients
|
||||
x2 = m_V.X + m_V.X;
|
||||
y2 = m_V.Y + m_V.Y;
|
||||
z2 = m_V.Z + m_V.Z;
|
||||
xx = m_V.X * m_V.X * 2.f;
|
||||
xy = m_V.X * m_V.Y * 2.f;
|
||||
xz = m_V.X * m_V.Z * 2.f;
|
||||
|
||||
xx = m_V.X * x2;
|
||||
xy = m_V.X * y2;
|
||||
xz = m_V.X * z2;
|
||||
yy = m_V.Y * m_V.Y * 2.f;
|
||||
yz = m_V.Y * m_V.Z * 2.f;
|
||||
|
||||
yy = m_V.Y * y2;
|
||||
yz = m_V.Y * z2;
|
||||
zz = m_V.Z * m_V.Z * 2.f;
|
||||
|
||||
zz = m_V.Z * z2;
|
||||
|
||||
wx = m_W * x2;
|
||||
wy = m_W * y2;
|
||||
wz = m_W * z2;
|
||||
wx = m_W * m_V.X * 2.f;
|
||||
wy = m_W * m_V.Y * 2.f;
|
||||
wz = m_W * m_V.Z * 2.f;
|
||||
|
||||
result._11 = 1.0f - (yy + zz);
|
||||
result._12 = xy - wz;
|
||||
@ -277,6 +272,8 @@ CVector3D CQuaternion::Rotate(const CVector3D& vec) const
|
||||
|
||||
CQuaternion CQuaternion::GetInverse() const
|
||||
{
|
||||
float lensqrd = SQR(m_V.X) + SQR(m_V.Y) + SQR(m_V.Z) + SQR(m_W);
|
||||
return CQuaternion(-m_V.X/lensqrd, -m_V.Y/lensqrd, -m_V.Z/lensqrd, m_W/lensqrd);
|
||||
// (x,y,z,w)^-1 = (-x/l^2, -y/l^2, -z/l^2, w/l^2) where l^2=x^2+y^2+z^2+w^2
|
||||
// Since we're only using quaternions for rotation, they should always have unit
|
||||
// length, so assume l=1
|
||||
return CQuaternion(-m_V.X, -m_V.Y, -m_V.Z, m_W);
|
||||
}
|
||||
|
@ -22,39 +22,39 @@ public:
|
||||
CQuaternion();
|
||||
CQuaternion(float x, float y, float z, float w);
|
||||
|
||||
//quaternion addition
|
||||
// Quaternion addition
|
||||
CQuaternion operator + (const CQuaternion &quat) const;
|
||||
//quaternion addition/assignment
|
||||
// Quaternion addition/assignment
|
||||
CQuaternion &operator += (const CQuaternion &quat);
|
||||
|
||||
//quaternion multiplication
|
||||
// Quaternion multiplication
|
||||
CQuaternion operator * (const CQuaternion &quat) const;
|
||||
//quaternion multiplication/assignment
|
||||
// Quaternion multiplication/assignment
|
||||
CQuaternion &operator *= (const CQuaternion &quat);
|
||||
|
||||
void FromEulerAngles (float x, float y, float z);
|
||||
CVector3D ToEulerAngles();
|
||||
|
||||
//convert the quaternion to matrix
|
||||
// Convert the quaternion to matrix
|
||||
CMatrix3D ToMatrix() const;
|
||||
void ToMatrix(CMatrix3D& result) const;
|
||||
|
||||
//sphere interpolation
|
||||
// Sphere interpolation
|
||||
void Slerp(const CQuaternion& from, const CQuaternion& to, float ratio);
|
||||
|
||||
// create a quaternion from axis/angle representation of a rotation
|
||||
// Create a quaternion from axis/angle representation of a rotation
|
||||
void FromAxisAngle(const CVector3D& axis, float angle);
|
||||
|
||||
// convert the quaternion to axis/angle representation of a rotation
|
||||
// Convert the quaternion to axis/angle representation of a rotation
|
||||
void ToAxisAngle(CVector3D& axis, float& angle);
|
||||
|
||||
// normalize this quaternion
|
||||
// Normalize this quaternion
|
||||
void Normalize();
|
||||
|
||||
// rotate a vector by this quaternion
|
||||
// Rotate a vector by this quaternion. Assumes the quaternion is normalised.
|
||||
CVector3D Rotate(const CVector3D& vec) const;
|
||||
|
||||
// calculate q^-1
|
||||
// Calculate q^-1. Assumes the quaternion is normalised.
|
||||
CQuaternion GetInverse() const;
|
||||
};
|
||||
|
||||
|
@ -49,8 +49,8 @@ public:
|
||||
q.ToMatrix(m);
|
||||
CQuaternion q2 = m.GetRotation();
|
||||
|
||||
// I hope there's a good reason why they're sometimes negated, and
|
||||
// it's not just a bug...
|
||||
// Quaternions (x,y,z,w) and (-x,-y,-z,-w) are equivalent when
|
||||
// interpreted as rotations, so it doesn't matter which we get
|
||||
const bool ok_oneway =
|
||||
feq(q2.m_W, q.m_W) &&
|
||||
feq(q2.m_V.X, q.m_V.X) &&
|
||||
|
@ -132,7 +132,7 @@ void* InstancingModelRenderer::CreateModelData(CModel* model)
|
||||
CModelDefPtr mdef = model->GetModelDef();
|
||||
IModelDef* imodeldef = (IModelDef*)mdef->GetRenderData(m);
|
||||
|
||||
debug_assert(!model->GetBoneMatrices());
|
||||
debug_assert(!model->IsSkinned());
|
||||
|
||||
if (!imodeldef)
|
||||
{
|
||||
|
@ -31,58 +31,6 @@
|
||||
#define LOG_CATEGORY "graphics"
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SkinPoint: skin the vertex position using it's blend data and given bone matrices
|
||||
static void SkinPoint(const SModelVertex& vertex,const CMatrix3D* matrices,CVector3D& result)
|
||||
{
|
||||
CVector3D tmp;
|
||||
const SVertexBlend& blend=vertex.m_Blend;
|
||||
|
||||
// must have at least one valid bone if we're using SkinPoint
|
||||
if (blend.m_Bone[0] == 0xff)
|
||||
{
|
||||
// (CModel should have already complained about this)
|
||||
result = CVector3D(0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const CMatrix3D& m=matrices[blend.m_Bone[0]];
|
||||
m.Transform(vertex.m_Coords,result);
|
||||
result*=blend.m_Weight[0];
|
||||
|
||||
for (u32 i=1; i<SVertexBlend::SIZE && blend.m_Bone[i]!=0xff; i++) {
|
||||
const CMatrix3D& m=matrices[blend.m_Bone[i]];
|
||||
m.Transform(vertex.m_Coords,tmp);
|
||||
result+=tmp*blend.m_Weight[i];
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SkinPoint: skin the vertex normal using it's blend data and given bone matrices
|
||||
static void SkinNormal(const SModelVertex& vertex, const CMatrix3D* invtranspmatrices, CVector3D& result)
|
||||
{
|
||||
CVector3D tmp;
|
||||
const SVertexBlend& blend=vertex.m_Blend;
|
||||
|
||||
// must have at least one valid bone if we're using SkinNormal
|
||||
if (blend.m_Bone[0] == 0xff)
|
||||
{
|
||||
// (CModel should have already complained about this)
|
||||
result = CVector3D(0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const CMatrix3D& m = invtranspmatrices[blend.m_Bone[0]];
|
||||
m.Rotate(vertex.m_Norm, result);
|
||||
result*=blend.m_Weight[0];
|
||||
|
||||
for (u32 i=1; i<SVertexBlend::SIZE && vertex.m_Blend.m_Bone[i]!=0xff; i++) {
|
||||
const CMatrix3D& m = invtranspmatrices[blend.m_Bone[i]];
|
||||
m.Rotate(vertex.m_Norm,tmp);
|
||||
result+=tmp*blend.m_Weight[i];
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ModelRenderer implementation
|
||||
|
||||
@ -112,27 +60,10 @@ void ModelRenderer::BuildPositionAndNormals(
|
||||
size_t numVertices = mdef->GetNumVertices();
|
||||
SModelVertex* vertices=mdef->GetVertices();
|
||||
|
||||
const CMatrix3D* bonematrices = model->GetBoneMatrices();
|
||||
if (bonematrices)
|
||||
if (model->IsSkinned())
|
||||
{
|
||||
// boned model - calculate skinned vertex positions/normals
|
||||
PROFILE( "skinning bones" );
|
||||
const CMatrix3D* invtranspbonematrices;
|
||||
|
||||
// Analytic geometry tells us that normal vectors need to be
|
||||
// multiplied by the inverse of the transpose. However, calculating
|
||||
// the inverse is slow, and analytic geometry also tells us that
|
||||
// for orthogonal matrices, the inverse is equal to the transpose,
|
||||
// so the inverse of the transpose is, in fact, the original matrix.
|
||||
//
|
||||
// The "fast normals" code assumes that bone transformation contain
|
||||
// no "weird" transformations like shears or non-uniform scaling
|
||||
// (actually, the entire code assumes no scaling) and thus gets
|
||||
// around the slow calculation of the inverse.
|
||||
if (g_Renderer.m_FastNormals)
|
||||
invtranspbonematrices = bonematrices;
|
||||
else
|
||||
invtranspbonematrices = model->GetInvTranspBoneMatrices();
|
||||
|
||||
// Avoid the noisy warnings that occur inside SkinPoint/SkinNormal in
|
||||
// some broken situations
|
||||
@ -144,8 +75,8 @@ void ModelRenderer::BuildPositionAndNormals(
|
||||
|
||||
for (size_t j=0; j<numVertices; j++)
|
||||
{
|
||||
SkinPoint(vertices[j], bonematrices, Position[j]);
|
||||
SkinNormal(vertices[j], invtranspbonematrices, Normal[j]);
|
||||
Position[j] = CModelDef::SkinPoint(vertices[j], model->GetAnimatedBoneMatrices(), model->GetInverseBindBoneMatrices());
|
||||
Normal[j] = CModelDef::SkinNormal(vertices[j], model->GetAnimatedBoneMatrices(), model->GetInverseBindBoneMatrices());
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -353,7 +353,6 @@ CRenderer::CRenderer()
|
||||
m_ClearColor[0]=m_ClearColor[1]=m_ClearColor[2]=m_ClearColor[3]=0;
|
||||
|
||||
m_SortAllTransparent = false;
|
||||
m_FastNormals = true;
|
||||
m_DisplayFrustum = false;
|
||||
m_DisableCopyShadow = false;
|
||||
m_FastPlayerColor = true;
|
||||
@ -1747,7 +1746,6 @@ void CRenderer::ScriptingInit()
|
||||
AddProperty(L"renderpath", &CRenderer::JSI_GetRenderPath, &CRenderer::JSI_SetRenderPath);
|
||||
AddProperty(L"useDepthTexture", &CRenderer::JSI_GetUseDepthTexture, &CRenderer::JSI_SetUseDepthTexture);
|
||||
AddProperty(L"sortAllTransparent", &CRenderer::m_SortAllTransparent);
|
||||
AddProperty(L"fastNormals", &CRenderer::m_FastNormals);
|
||||
AddProperty(L"displayFrustum", &CRenderer::m_DisplayFrustum);
|
||||
AddProperty(L"shadowZBias", &CRenderer::m_ShadowZBias);
|
||||
AddProperty(L"disableCopyShadow", &CRenderer::m_DisableCopyShadow);
|
||||
|
@ -431,13 +431,6 @@ protected:
|
||||
*/
|
||||
bool m_SortAllTransparent;
|
||||
|
||||
/**
|
||||
* m_FastNormals: Use faster normal transformation in the
|
||||
* software transform by multiplying with the bone matrix itself
|
||||
* instead of the transpose of the inverse.
|
||||
*/
|
||||
bool m_FastNormals;
|
||||
|
||||
/**
|
||||
* m_DisplayFrustum: Render the cull frustum and other data that may be interesting
|
||||
* to evaluate culling and shadow map calculations
|
||||
|
@ -166,7 +166,7 @@ static void SendToGame(const AtlasMessage::sEnvironmentSettings& settings)
|
||||
ActorViewer::ActorViewer(wxWindow* parent)
|
||||
: wxFrame(parent, wxID_ANY, _("Actor Viewer"), wxDefaultPosition, wxSize(800, 600)),
|
||||
m_CurrentSpeed(0.f), m_BackgroundColour(wxColour(255, 255, 255)),
|
||||
m_ToggledWalking(true), m_ToggledWireframe(false), m_ToggledGround(true),
|
||||
m_ToggledWalking(false), m_ToggledWireframe(false), m_ToggledGround(true),
|
||||
m_ToggledShadows(true), m_ToggledStats(false),
|
||||
m_ObjectSettings(m_ObjectSelection, AtlasMessage::eRenderView::ACTOR)
|
||||
{
|
||||
|
@ -51,7 +51,7 @@ ActorViewer::ActorViewer()
|
||||
: m(*new ActorViewerImpl())
|
||||
{
|
||||
m.Unit = NULL;
|
||||
m.WalkEnabled = true;
|
||||
m.WalkEnabled = false;
|
||||
m.Background = SColor4ub(255, 255, 255, 255);
|
||||
|
||||
// Set up the renderer
|
||||
|
@ -168,7 +168,7 @@ MESSAGEHANDLER(LookAt)
|
||||
0, 0, 0, 1
|
||||
);
|
||||
|
||||
M.GetTranspose(camera.m_Orientation);
|
||||
camera.m_Orientation = M.GetTranspose();
|
||||
camera.m_Orientation.Translate(-eye);
|
||||
|
||||
camera.UpdateFrustum();
|
||||
|
Loading…
Reference in New Issue
Block a user