forked from Mirrors/openclonk
974 lines
34 KiB
C++
974 lines
34 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 2009 Armin Burgmeier
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
|
|
*
|
|
* Portions might be copyrighted by other authors who have contributed
|
|
* to OpenClonk.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
* See isc_license.txt for full license and disclaimer.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender.
|
|
* See clonk_trademark_license.txt for full license.
|
|
*/
|
|
|
|
#include "C4Include.h"
|
|
#include <StdMesh.h>
|
|
|
|
#ifdef _MSC_VER
|
|
# define _USE_MATH_DEFINES
|
|
# include <math.h>
|
|
#endif
|
|
|
|
#include <tinyxml/tinyxml.h>
|
|
|
|
#include <algorithm>
|
|
|
|
namespace
|
|
{
|
|
// Helper to sort faces for FaceOrdering
|
|
struct StdMeshInstanceFaceOrderingCmpPred
|
|
{
|
|
const StdMeshInstance& m_inst;
|
|
StdMeshInstanceFaceOrderingCmpPred(const StdMeshInstance& inst):
|
|
m_inst(inst) {}
|
|
|
|
bool operator()(const StdMeshFace& face1, const StdMeshFace& face2) const
|
|
{
|
|
switch(m_inst.GetFaceOrdering())
|
|
{
|
|
case StdMeshInstance::FO_Fixed:
|
|
assert(false);
|
|
return false;
|
|
case StdMeshInstance::FO_FarthestToNearest:
|
|
case StdMeshInstance::FO_NearestToFarthest:
|
|
{
|
|
float z1 = m_inst.GetVertex(face1.Vertices[0]).z + m_inst.GetVertex(face1.Vertices[1]).z + m_inst.GetVertex(face1.Vertices[2]).z;
|
|
float z2 = m_inst.GetVertex(face2.Vertices[0]).z + m_inst.GetVertex(face2.Vertices[1]).z + m_inst.GetVertex(face2.Vertices[2]).z;
|
|
if(m_inst.GetFaceOrdering() == StdMeshInstance::FO_FarthestToNearest)
|
|
return z1 < z2;
|
|
else
|
|
return z2 < z1;
|
|
}
|
|
default:
|
|
assert(false);
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Generate matrix to convert the mesh from Ogre coordinate system to Clonk
|
|
// coordinate system.
|
|
StdMeshMatrix CoordCorrectionMatrix()
|
|
{
|
|
StdMeshMatrix matrix;
|
|
StdMeshMatrix helper;
|
|
|
|
//matrix.SetIdentity();
|
|
matrix.SetScale(-1.0f, 1.0f, 1.0f);
|
|
|
|
//helper.SetRotate(M_PI/2.0f, 1.0f, 0.0f, 0.0f);
|
|
helper.SetRotate(M_PI/2.0f, 1.0f, 0.0f, 0.0f);
|
|
matrix.Mul(helper);
|
|
|
|
helper.SetRotate(M_PI/2.0f, 0.0f, 0.0f, 1.0f);
|
|
matrix.Mul(helper);
|
|
|
|
return matrix;
|
|
}
|
|
|
|
StdMeshMatrix CoordCorrectionMatrixInverse()
|
|
{
|
|
StdMeshMatrix matrix = CoordCorrectionMatrix();
|
|
matrix.SetInverse();
|
|
return matrix;
|
|
}
|
|
|
|
StdMeshMatrix CoordCorrection = CoordCorrectionMatrix();
|
|
StdMeshMatrix CoordCorrectionInverse = CoordCorrectionMatrixInverse();
|
|
}
|
|
|
|
StdMeshError::StdMeshError(const StdStrBuf& message, const char* file, unsigned int line)
|
|
{
|
|
Buf.Format("%s:%u: %s", file, line, message.getData());
|
|
}
|
|
|
|
// Helper class to load things from an XML file with error checking
|
|
class StdMeshXML
|
|
{
|
|
public:
|
|
StdMeshXML(const char* filename, const char* xml_data);
|
|
|
|
const char* RequireStrAttribute(TiXmlElement* element, const char* attribute) const;
|
|
int RequireIntAttribute(TiXmlElement* element, const char* attribute) const;
|
|
float RequireFloatAttribute(TiXmlElement* element, const char* attribute) const;
|
|
|
|
TiXmlElement* RequireFirstChild(TiXmlElement* element, const char* child);
|
|
|
|
void Error(const StdStrBuf& message, TiXmlElement* element) const;
|
|
|
|
private:
|
|
TiXmlDocument Document;
|
|
StdStrBuf FileName;
|
|
};
|
|
|
|
StdMeshXML::StdMeshXML(const char* filename, const char* xml_data):
|
|
FileName(filename)
|
|
{
|
|
Document.Parse(xml_data);
|
|
if(Document.Error())
|
|
throw StdMeshError(StdStrBuf(Document.ErrorDesc()), FileName.getData(), Document.ErrorRow());
|
|
}
|
|
|
|
const char* StdMeshXML::RequireStrAttribute(TiXmlElement* element, const char* attribute) const
|
|
{
|
|
const char* value = element->Attribute(attribute);
|
|
if(!value) Error(FormatString("Element '%s' does not have attribute '%s'", element->Value(), attribute), element);
|
|
return value;
|
|
}
|
|
|
|
int StdMeshXML::RequireIntAttribute(TiXmlElement* element, const char* attribute) const
|
|
{
|
|
int retval;
|
|
if(element->QueryIntAttribute(attribute, &retval) != TIXML_SUCCESS)
|
|
Error(FormatString("Element '%s' does not have integer attribute '%s'", element->Value(), attribute), element);
|
|
return retval;
|
|
}
|
|
|
|
float StdMeshXML::RequireFloatAttribute(TiXmlElement* element, const char* attribute) const
|
|
{
|
|
float retval;
|
|
if(element->QueryFloatAttribute(attribute, &retval) != TIXML_SUCCESS)
|
|
Error(FormatString("Element '%s' does not have integer attribute '%s'", element->Value(), attribute), element);
|
|
return retval;
|
|
}
|
|
|
|
TiXmlElement* StdMeshXML::RequireFirstChild(TiXmlElement* element, const char* child)
|
|
{
|
|
TiXmlElement* retval;
|
|
|
|
if(element)
|
|
{
|
|
retval = element->FirstChildElement(child);
|
|
if(!retval)
|
|
Error(FormatString("Element '%s' does not contain '%s' child", element->Value(), child), element);
|
|
}
|
|
else
|
|
{
|
|
retval = Document.RootElement();
|
|
if(strcmp(retval->Value(), child) != 0)
|
|
Error(FormatString("Root element is not '%s'", child), retval);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
void StdMeshXML::Error(const StdStrBuf& message, TiXmlElement* element) const
|
|
{
|
|
throw StdMeshError(message, FileName.getData(), element->Row());
|
|
}
|
|
|
|
void StdMeshMatrix::SetIdentity()
|
|
{
|
|
a[0][0] = 1.0f; a[0][1] = 0.0f; a[0][2] = 0.0f; a[0][3] = 0.0f;
|
|
a[1][0] = 0.0f; a[1][1] = 1.0f; a[1][2] = 0.0f; a[1][3] = 0.0f;
|
|
a[2][0] = 0.0f; a[2][1] = 0.0f; a[2][2] = 1.0f; a[2][3] = 0.0f;
|
|
}
|
|
|
|
void StdMeshMatrix::SetInverse()
|
|
{
|
|
const float det = a[0][0]*a[1][1]*a[2][2] + a[0][1]*a[1][2]*a[2][0] + a[0][2]*a[1][0]*a[2][1]
|
|
- a[0][0]*a[1][2]*a[2][1] - a[0][1]*a[1][0]*a[2][2] - a[0][2]*a[1][1]*a[2][0];
|
|
assert(det != 0.0f);
|
|
StdMeshMatrix old(*this);
|
|
|
|
a[0][0] = (old.a[1][1]*old.a[2][2] - old.a[1][2]*old.a[2][1]) / det;
|
|
a[1][0] = (old.a[1][2]*old.a[2][0] - old.a[1][0]*old.a[2][2]) / det;
|
|
a[2][0] = (old.a[1][0]*old.a[2][1] - old.a[1][1]*old.a[2][0]) / det;
|
|
|
|
a[0][1] = (old.a[0][2]*old.a[2][1] - old.a[0][1]*old.a[2][2]) / det;
|
|
a[1][1] = (old.a[0][0]*old.a[2][2] - old.a[0][2]*old.a[2][0]) / det;
|
|
a[2][1] = (old.a[0][1]*old.a[2][0] - old.a[0][0]*old.a[2][1]) / det;
|
|
|
|
a[0][2] = (old.a[0][1]*old.a[1][2] - old.a[0][2]*old.a[1][1]) / det;
|
|
a[1][2] = (old.a[0][2]*old.a[1][0] - old.a[0][0]*old.a[1][2]) / det;
|
|
a[2][2] = (old.a[0][0]*old.a[1][1] - old.a[0][1]*old.a[1][0]) / det;
|
|
|
|
a[0][3] = (old.a[0][1]*old.a[1][3]*old.a[2][2]
|
|
+ old.a[0][2]*old.a[1][1]*old.a[2][3]
|
|
+ old.a[0][3]*old.a[1][2]*old.a[2][1]
|
|
- old.a[0][1]*old.a[1][2]*old.a[2][3]
|
|
- old.a[0][2]*old.a[1][3]*old.a[2][1]
|
|
- old.a[0][3]*old.a[1][1]*old.a[2][2]) / det;
|
|
|
|
a[1][3] = (old.a[0][0]*old.a[1][2]*old.a[2][3]
|
|
+ old.a[0][2]*old.a[1][3]*old.a[2][0]
|
|
+ old.a[0][3]*old.a[1][0]*old.a[2][2]
|
|
- old.a[0][0]*old.a[1][3]*old.a[2][2]
|
|
- old.a[0][2]*old.a[1][0]*old.a[2][3]
|
|
- old.a[0][3]*old.a[1][2]*old.a[2][0]) / det;
|
|
|
|
a[2][3] = (old.a[0][0]*old.a[1][3]*old.a[2][1]
|
|
+ old.a[0][1]*old.a[1][0]*old.a[2][3]
|
|
+ old.a[0][3]*old.a[1][1]*old.a[2][0]
|
|
- old.a[0][0]*old.a[1][1]*old.a[2][3]
|
|
- old.a[0][1]*old.a[1][3]*old.a[2][0]
|
|
- old.a[0][3]*old.a[1][0]*old.a[2][1]) / det;
|
|
}
|
|
|
|
void StdMeshMatrix::SetTranslate(float dx, float dy, float dz)
|
|
{
|
|
a[0][0] = 1.0f; a[0][1] = 0.0f; a[0][2] = 0.0f; a[0][3] = dx;
|
|
a[1][0] = 0.0f; a[1][1] = 1.0f; a[1][2] = 0.0f; a[1][3] = dy;
|
|
a[2][0] = 0.0f; a[2][1] = 0.0f; a[2][2] = 1.0f; a[2][3] = dz;
|
|
}
|
|
|
|
void StdMeshMatrix::SetScale(float sx, float sy, float sz)
|
|
{
|
|
a[0][0] = sx; a[0][1] = 0.0f; a[0][2] = 0.0f; a[0][3] = 0.0f;
|
|
a[1][0] = 0.0f; a[1][1] = sy; a[1][2] = 0.0f; a[1][3] = 0.0f;
|
|
a[2][0] = 0.0f; a[2][1] = 0.0f; a[2][2] = sz; a[2][3] = 0.0f;
|
|
}
|
|
|
|
void StdMeshMatrix::SetRotate(float angle, float rx, float ry, float rz)
|
|
{
|
|
// We do normalize the rx,ry,rz vector here: This is only required for
|
|
// precalculations anyway, thus not time-critical.
|
|
float abs = sqrt(rx*rx+ry*ry+rz*rz);
|
|
rx/=abs; ry/=abs; rz/=abs;
|
|
float c = cos(angle), s = sin(angle);
|
|
|
|
a[0][0] = rx*rx*(1-c)+c; a[0][1] = rx*ry*(1-c)-rz*s; a[0][2] = rx*rz*(1-c)+ry*s; a[0][3] = 0.0f;
|
|
a[1][0] = ry*rx*(1-c)+rz*s; a[1][1] = ry*ry*(1-c)+c; a[1][2] = ry*rz*(1-c)-rx*s; a[1][3] = 0.0f;
|
|
a[2][0] = rz*rx*(1-c)-ry*s; a[2][1] = ry*rz*(1-c)+rx*s; a[2][2] = rz*rz*(1-c)+c; a[2][3] = 0.0f;
|
|
}
|
|
|
|
void StdMeshMatrix::Mul(const StdMeshMatrix& other)
|
|
{
|
|
StdMeshMatrix old(*this);
|
|
|
|
a[0][0] = old.a[0][0]*other.a[0][0] + old.a[0][1]*other.a[1][0] + old.a[0][2]*other.a[2][0];
|
|
a[1][0] = old.a[1][0]*other.a[0][0] + old.a[1][1]*other.a[1][0] + old.a[1][2]*other.a[2][0];
|
|
a[2][0] = old.a[2][0]*other.a[0][0] + old.a[2][1]*other.a[1][0] + old.a[2][2]*other.a[2][0];
|
|
|
|
a[0][1] = old.a[0][0]*other.a[0][1] + old.a[0][1]*other.a[1][1] + old.a[0][2]*other.a[2][1];
|
|
a[1][1] = old.a[1][0]*other.a[0][1] + old.a[1][1]*other.a[1][1] + old.a[1][2]*other.a[2][1];
|
|
a[2][1] = old.a[2][0]*other.a[0][1] + old.a[2][1]*other.a[1][1] + old.a[2][2]*other.a[2][1];
|
|
|
|
a[0][2] = old.a[0][0]*other.a[0][2] + old.a[0][1]*other.a[1][2] + old.a[0][2]*other.a[2][2];
|
|
a[1][2] = old.a[1][0]*other.a[0][2] + old.a[1][1]*other.a[1][2] + old.a[1][2]*other.a[2][2];
|
|
a[2][2] = old.a[2][0]*other.a[0][2] + old.a[2][1]*other.a[1][2] + old.a[2][2]*other.a[2][2];
|
|
|
|
a[0][3] = old.a[0][0]*other.a[0][3] + old.a[0][1]*other.a[1][3] + old.a[0][2]*other.a[2][3] + old.a[0][3];
|
|
a[1][3] = old.a[1][0]*other.a[0][3] + old.a[1][1]*other.a[1][3] + old.a[1][2]*other.a[2][3] + old.a[1][3];
|
|
a[2][3] = old.a[2][0]*other.a[0][3] + old.a[2][1]*other.a[1][3] + old.a[2][2]*other.a[2][3] + old.a[2][3];
|
|
}
|
|
|
|
void StdMeshMatrix::Mul(float f)
|
|
{
|
|
a[0][0] *= f;
|
|
a[0][1] *= f;
|
|
a[0][2] *= f;
|
|
a[0][3] *= f;
|
|
a[1][0] *= f;
|
|
a[1][1] *= f;
|
|
a[1][2] *= f;
|
|
a[1][3] *= f;
|
|
a[2][0] *= f;
|
|
a[2][1] *= f;
|
|
a[2][2] *= f;
|
|
a[2][3] *= f;
|
|
}
|
|
|
|
void StdMeshMatrix::Add(const StdMeshMatrix& other)
|
|
{
|
|
a[0][0] += other.a[0][0];
|
|
a[0][1] += other.a[0][1];
|
|
a[0][2] += other.a[0][2];
|
|
a[0][3] += other.a[0][3];
|
|
a[1][0] += other.a[1][0];
|
|
a[1][1] += other.a[1][1];
|
|
a[1][2] += other.a[1][2];
|
|
a[1][3] += other.a[1][3];
|
|
a[2][0] += other.a[2][0];
|
|
a[2][1] += other.a[2][1];
|
|
a[2][2] += other.a[2][2];
|
|
a[2][3] += other.a[2][3];
|
|
}
|
|
|
|
void StdMeshMatrix::Transform(const StdMeshMatrix& other)
|
|
{
|
|
StdMeshMatrix old(*this);
|
|
|
|
a[0][0] = other.a[0][0]*old.a[0][0] + other.a[0][1]*old.a[1][0] + other.a[0][2]*old.a[2][0];
|
|
a[1][0] = other.a[1][0]*old.a[0][0] + other.a[1][1]*old.a[1][0] + other.a[1][2]*old.a[2][0];
|
|
a[2][0] = other.a[2][0]*old.a[0][0] + other.a[2][1]*old.a[1][0] + other.a[2][2]*old.a[2][0];
|
|
|
|
a[0][1] = other.a[0][0]*old.a[0][1] + other.a[0][1]*old.a[1][1] + other.a[0][2]*old.a[2][1];
|
|
a[1][1] = other.a[1][0]*old.a[0][1] + other.a[1][1]*old.a[1][1] + other.a[1][2]*old.a[2][1];
|
|
a[2][1] = other.a[2][0]*old.a[0][1] + other.a[2][1]*old.a[1][1] + other.a[2][2]*old.a[2][1];
|
|
|
|
a[0][2] = other.a[0][0]*old.a[0][2] + other.a[0][1]*old.a[1][2] + other.a[0][2]*old.a[2][2];
|
|
a[1][2] = other.a[1][0]*old.a[0][2] + other.a[1][1]*old.a[1][2] + other.a[1][2]*old.a[2][2];
|
|
a[2][2] = other.a[2][0]*old.a[0][2] + other.a[2][1]*old.a[1][2] + other.a[2][2]*old.a[2][2];
|
|
|
|
a[0][3] = other.a[0][0]*old.a[0][3] + other.a[0][1]*old.a[1][3] + other.a[0][2]*old.a[2][3] + other.a[0][3];
|
|
a[1][3] = other.a[1][0]*old.a[0][3] + other.a[1][1]*old.a[1][3] + other.a[1][2]*old.a[2][3] + other.a[1][3];
|
|
a[2][3] = other.a[2][0]*old.a[0][3] + other.a[2][1]*old.a[1][3] + other.a[2][2]*old.a[2][3] + other.a[2][3];
|
|
}
|
|
|
|
void StdMeshVertex::Transform(const StdMeshMatrix& trans)
|
|
{
|
|
StdMeshVertex old(*this);
|
|
|
|
x = trans(0,0)*old.x + trans(0,1)*old.y + trans(0,2)*old.z + trans(0,3);
|
|
y = trans(1,0)*old.x + trans(1,1)*old.y + trans(1,2)*old.z + trans(1,3);
|
|
z = trans(2,0)*old.x + trans(2,1)*old.y + trans(2,2)*old.z + trans(2,3);
|
|
nx = trans(0,0)*old.nx + trans(0,1)*old.ny + trans(0,2)*old.nz;
|
|
ny = trans(1,0)*old.nx + trans(1,1)*old.ny + trans(0,2)*old.nz;
|
|
nz = trans(2,0)*old.nx + trans(2,1)*old.ny + trans(2,2)*old.nz;
|
|
}
|
|
|
|
void StdMeshVertex::Mul(float f)
|
|
{
|
|
x *= f;
|
|
y *= f;
|
|
z *= f;
|
|
|
|
// We also multiplicate normals because we expect this to happen in
|
|
// an expression such as a*v1 + (1-a)*v2 which would ensure normalization
|
|
// of the normals again. (TODO: It doesn't. We should just always
|
|
// keep them normalized).
|
|
nx *= f;
|
|
ny *= f;
|
|
nz *= f;
|
|
}
|
|
|
|
void StdMeshVertex::Add(const StdMeshVertex& other)
|
|
{
|
|
x += other.x;
|
|
y += other.y;
|
|
z += other.z;
|
|
|
|
nx += other.nx;
|
|
ny += other.ny;
|
|
nz += other.nz;
|
|
}
|
|
|
|
StdMeshMatrix StdMeshTrack::GetTransformAt(float time) const
|
|
{
|
|
std::map<float, StdMeshKeyFrame>::const_iterator iter = Frames.lower_bound(time);
|
|
|
|
// If this points to end(), then either
|
|
// a) time > animation length
|
|
// b) The track does not include a frame for the very end of the animation
|
|
// Both is considered an error
|
|
assert(iter != Frames.end());
|
|
|
|
if(iter == Frames.begin())
|
|
return iter->second.Trans;
|
|
|
|
std::map<float, StdMeshKeyFrame>::const_iterator prev_iter = iter;
|
|
-- prev_iter;
|
|
|
|
float dt = iter->first - prev_iter->first;
|
|
float weight1 = (time - prev_iter->first) / dt;
|
|
float weight2 = (iter->first - time) / dt;
|
|
|
|
assert(weight1 >= 0 && weight2 >= 0 && weight1 <= 1 && weight2 <= 1);
|
|
assert(fabs(weight1 + weight2 - 1) < 1e-6);
|
|
|
|
StdMeshMatrix trans1 = iter->second.Trans;
|
|
StdMeshMatrix trans2 = prev_iter->second.Trans;
|
|
trans1.Mul(weight1);
|
|
trans2.Mul(weight2);
|
|
|
|
trans1.Add(trans2);
|
|
return trans1;
|
|
}
|
|
|
|
StdMeshAnimation::StdMeshAnimation(const StdMeshAnimation& other):
|
|
Name(other.Name), Length(other.Length), Tracks(other.Tracks.size())
|
|
{
|
|
// Note that all Tracks are already default-initialized to zero
|
|
for(unsigned int i = 0; i < Tracks.size(); ++i)
|
|
if(other.Tracks[i])
|
|
Tracks[i] = new StdMeshTrack(*other.Tracks[i]);
|
|
}
|
|
|
|
StdMeshAnimation::~StdMeshAnimation()
|
|
{
|
|
for(unsigned int i = 0; i < Tracks.size(); ++i)
|
|
delete Tracks[i];
|
|
}
|
|
|
|
StdMeshAnimation& StdMeshAnimation::operator=(const StdMeshAnimation& other)
|
|
{
|
|
if(this == &other) return *this;
|
|
|
|
Name = other.Name;
|
|
Length = other.Length;
|
|
|
|
for(unsigned int i = 0; i < Tracks.size(); ++i)
|
|
delete Tracks[i];
|
|
|
|
Tracks.resize(other.Tracks.size());
|
|
|
|
for(unsigned int i = 0; i < Tracks.size(); ++i)
|
|
if(other.Tracks[i])
|
|
Tracks[i] = new StdMeshTrack(*other.Tracks[i]);
|
|
|
|
return *this;
|
|
}
|
|
|
|
StdMesh::StdMesh():
|
|
Material(NULL)
|
|
{
|
|
BoundingBox.x1 = BoundingBox.y1 = BoundingBox.z1 = 0.0f;
|
|
BoundingBox.x2 = BoundingBox.y2 = BoundingBox.z2 = 0.0f;
|
|
}
|
|
|
|
StdMesh::~StdMesh()
|
|
{
|
|
for(unsigned int i = 0; i < Bones.size(); ++i)
|
|
delete Bones[i];
|
|
}
|
|
|
|
void StdMesh::InitXML(const char* filename, const char* xml_data, StdMeshSkeletonLoader& skel_loader, const StdMeshMatManager& manager)
|
|
{
|
|
StdMeshXML mesh(filename, xml_data);
|
|
|
|
TiXmlElement* mesh_elem = mesh.RequireFirstChild(NULL, "mesh");
|
|
TiXmlElement* submeshes_elem = mesh.RequireFirstChild(mesh_elem, "submeshes");
|
|
// Load first submesh only for now
|
|
TiXmlElement* submesh_elem = mesh.RequireFirstChild(submeshes_elem, "submesh");
|
|
TiXmlElement* geometry_elem = mesh.RequireFirstChild(submesh_elem, "geometry");
|
|
|
|
const char* material = mesh.RequireStrAttribute(submesh_elem, "material");
|
|
Material = manager.GetMaterial(material);
|
|
if(!Material)
|
|
mesh.Error(FormatString("There is no such material named '%s'", material), submesh_elem);
|
|
|
|
int VertexCount = mesh.RequireIntAttribute(geometry_elem, "vertexcount");
|
|
Vertices.resize(VertexCount);
|
|
|
|
TiXmlElement* buffer_elem = mesh.RequireFirstChild(geometry_elem, "vertexbuffer");
|
|
|
|
unsigned int i = 0;
|
|
for(TiXmlElement* vertex_elem = buffer_elem->FirstChildElement("vertex"); vertex_elem != NULL && i < Vertices.size(); vertex_elem = vertex_elem->NextSiblingElement("vertex"), ++i)
|
|
{
|
|
TiXmlElement* position_elem = mesh.RequireFirstChild(vertex_elem, "position");
|
|
TiXmlElement* normal_elem = mesh.RequireFirstChild(vertex_elem, "normal");
|
|
TiXmlElement* texcoord_elem = mesh.RequireFirstChild(vertex_elem, "texcoord");
|
|
|
|
Vertices[i].x = mesh.RequireFloatAttribute(position_elem, "x");
|
|
Vertices[i].y = mesh.RequireFloatAttribute(position_elem, "y");
|
|
Vertices[i].z = mesh.RequireFloatAttribute(position_elem, "z");
|
|
Vertices[i].nx = mesh.RequireFloatAttribute(normal_elem, "x");
|
|
Vertices[i].ny = mesh.RequireFloatAttribute(normal_elem, "y");
|
|
Vertices[i].nz = mesh.RequireFloatAttribute(normal_elem, "z");
|
|
Vertices[i].u = mesh.RequireFloatAttribute(texcoord_elem, "u");
|
|
Vertices[i].v = mesh.RequireFloatAttribute(texcoord_elem, "v");
|
|
|
|
// Convert to Clonk coordinate system
|
|
Vertices[i].Transform(CoordCorrection);
|
|
|
|
// Construct BoundingBox
|
|
if(i == 0)
|
|
{
|
|
BoundingBox.x1 = BoundingBox.x2 = Vertices[i].x;
|
|
BoundingBox.y1 = BoundingBox.y2 = Vertices[i].y;
|
|
BoundingBox.z1 = BoundingBox.z2 = Vertices[i].z;
|
|
}
|
|
else
|
|
{
|
|
BoundingBox.x1 = Min(Vertices[i].x, BoundingBox.x1);
|
|
BoundingBox.x2 = Max(Vertices[i].x, BoundingBox.x2);
|
|
BoundingBox.y1 = Min(Vertices[i].y, BoundingBox.y1);
|
|
BoundingBox.y2 = Max(Vertices[i].y, BoundingBox.y2);
|
|
BoundingBox.z1 = Min(Vertices[i].z, BoundingBox.z1);
|
|
BoundingBox.z2 = Max(Vertices[i].z, BoundingBox.z2);
|
|
}
|
|
}
|
|
|
|
TiXmlElement* faces_elem = mesh.RequireFirstChild(submesh_elem, "faces");
|
|
int FaceCount = mesh.RequireIntAttribute(faces_elem, "count");
|
|
Faces.resize(FaceCount);
|
|
|
|
i = 0;
|
|
for(TiXmlElement* face_elem = faces_elem->FirstChildElement("face"); face_elem != NULL && i < Faces.size(); face_elem = face_elem->NextSiblingElement("face"), ++i)
|
|
{
|
|
int v[3];
|
|
|
|
v[0] = mesh.RequireIntAttribute(face_elem, "v1");
|
|
v[1] = mesh.RequireIntAttribute(face_elem, "v2");
|
|
v[2] = mesh.RequireIntAttribute(face_elem, "v3");
|
|
|
|
for(unsigned int j = 0; j < 3; ++j)
|
|
{
|
|
if(v[j] < 0 || static_cast<unsigned int>(v[j]) >= Vertices.size())
|
|
mesh.Error(FormatString("Vertex index v%u (%d) is out of range", j+1, v[j]), face_elem);
|
|
Faces[i].Vertices[j] = v[j];
|
|
}
|
|
}
|
|
|
|
// Read skeleton, if any
|
|
TiXmlElement* skeletonlink_elem = mesh_elem->FirstChildElement("skeletonlink");
|
|
if(skeletonlink_elem)
|
|
{
|
|
const char* name = mesh.RequireStrAttribute(skeletonlink_elem, "name");
|
|
StdCopyStrBuf xml_filename(name); xml_filename.Append(".xml");
|
|
|
|
StdStrBuf skeleton_xml_data = skel_loader.LoadSkeleton(xml_filename.getData());
|
|
if(skeleton_xml_data.isNull()) mesh.Error(FormatString("Failed to load '%s'", xml_filename.getData()), skeletonlink_elem);
|
|
|
|
StdMeshXML skeleton(xml_filename.getData(), skeleton_xml_data.getData());
|
|
TiXmlElement* skeleton_elem = skeleton.RequireFirstChild(NULL, "skeleton");
|
|
TiXmlElement* bones_elem = skeleton.RequireFirstChild(skeleton_elem, "bones");
|
|
|
|
// Read bones. Don't insert into Master bone table yet, as the master bone
|
|
// table is sorted hierarchically, and we will read the hierarchy only
|
|
// afterwards.
|
|
std::vector<StdMeshBone*> bones;
|
|
for(TiXmlElement* bone_elem = bones_elem->FirstChildElement("bone"); bone_elem != NULL; bone_elem = bone_elem->NextSiblingElement("bone"))
|
|
{
|
|
StdMeshBone* bone = new StdMeshBone;
|
|
bones.push_back(bone);
|
|
|
|
bone->ID = skeleton.RequireIntAttribute(bone_elem, "id");
|
|
bone->Name = skeleton.RequireStrAttribute(bone_elem, "name");
|
|
|
|
// TODO: Make sure ID and name are unique
|
|
|
|
TiXmlElement* position_elem = skeleton.RequireFirstChild(bone_elem, "position");
|
|
TiXmlElement* rotation_elem = skeleton.RequireFirstChild(bone_elem, "rotation");
|
|
TiXmlElement* axis_elem = skeleton.RequireFirstChild(rotation_elem, "axis");
|
|
|
|
float dx = skeleton.RequireFloatAttribute(position_elem, "x");
|
|
float dy = skeleton.RequireFloatAttribute(position_elem, "y");
|
|
float dz = skeleton.RequireFloatAttribute(position_elem, "z");
|
|
float angle = skeleton.RequireFloatAttribute(rotation_elem, "angle");
|
|
float rx = skeleton.RequireFloatAttribute(axis_elem, "x");
|
|
float ry = skeleton.RequireFloatAttribute(axis_elem, "y");
|
|
float rz = skeleton.RequireFloatAttribute(axis_elem, "z");
|
|
|
|
StdMeshMatrix helper;
|
|
helper.SetTranslate(dx, dy, dz);
|
|
bone->Trans.SetRotate(angle, rx, ry, rz);
|
|
bone->Trans.Transform(helper);
|
|
|
|
// Transform to Clonk coordinate system
|
|
bone->Trans.Mul(CoordCorrectionInverse);
|
|
bone->Trans.Transform(CoordCorrection);
|
|
|
|
#if 1
|
|
helper.SetRotate(-angle, rx, ry, rz);
|
|
bone->InverseTrans.SetTranslate(-dx, -dy, -dz);
|
|
bone->InverseTrans.Transform(helper);
|
|
|
|
// Transform to Clonk coordinate system
|
|
bone->InverseTrans.Mul(CoordCorrectionInverse);
|
|
bone->InverseTrans.Transform(CoordCorrection);
|
|
#else
|
|
bone->InverseTrans = bone->Trans;
|
|
bone->InverseTrans.SetInverse();
|
|
#endif
|
|
|
|
bone->Parent = NULL;
|
|
|
|
// Index of bone will be set when building Master Bone Table later
|
|
}
|
|
|
|
// Bone hierarchy
|
|
TiXmlElement* bonehierarchy_elem = skeleton.RequireFirstChild(skeleton_elem, "bonehierarchy");
|
|
for(TiXmlElement* boneparent_elem = bonehierarchy_elem->FirstChildElement("boneparent"); boneparent_elem != NULL; boneparent_elem = boneparent_elem->NextSiblingElement("boneparent"))
|
|
{
|
|
const char* child_name = skeleton.RequireStrAttribute(boneparent_elem, "bone");
|
|
const char* parent_name = skeleton.RequireStrAttribute(boneparent_elem, "parent");
|
|
|
|
// Lookup the two bones
|
|
StdMeshBone* child = NULL;
|
|
StdMeshBone* parent = NULL;
|
|
for(unsigned int i = 0; i < bones.size() && (!child || !parent); ++i)
|
|
{
|
|
if(!child && bones[i]->Name == child_name)
|
|
child = bones[i];
|
|
if(!parent && bones[i]->Name == parent_name)
|
|
parent = bones[i];
|
|
}
|
|
|
|
if(!child) skeleton.Error(FormatString("There is no such bone with name '%s'", child_name), boneparent_elem);
|
|
if(!parent) skeleton.Error(FormatString("There is no such bone with name '%s'", parent_name), boneparent_elem);
|
|
|
|
child->Parent = parent;
|
|
parent->Children.push_back(child);
|
|
|
|
// Apply parent transformation
|
|
child->Trans.Transform(parent->Trans);
|
|
child->InverseTrans.Mul(parent->InverseTrans);
|
|
}
|
|
|
|
// Fill master bone table in hierarchical order:
|
|
for(unsigned int i = 0; i < bones.size(); ++i)
|
|
if(bones[i]->Parent == NULL)
|
|
AddMasterBone(bones[i]);
|
|
|
|
// Vertex<->Bone assignments
|
|
TiXmlElement* boneassignments_elem = mesh.RequireFirstChild(submesh_elem, "boneassignments");
|
|
for(TiXmlElement* vertexboneassignment_elem = boneassignments_elem->FirstChildElement("vertexboneassignment"); vertexboneassignment_elem != NULL; vertexboneassignment_elem = vertexboneassignment_elem->NextSiblingElement("vertexboneassignment"))
|
|
{
|
|
int BoneID = mesh.RequireIntAttribute(vertexboneassignment_elem, "boneindex");
|
|
int VertexIndex = mesh.RequireIntAttribute(vertexboneassignment_elem, "vertexindex");
|
|
float weight = mesh.RequireFloatAttribute(vertexboneassignment_elem, "weight");
|
|
|
|
if(VertexIndex < 0 || static_cast<unsigned int>(VertexIndex) >= Vertices.size())
|
|
mesh.Error(FormatString("Vertex index in bone assignment (%d) is out of range", VertexIndex), vertexboneassignment_elem);
|
|
|
|
StdMeshBone* bone = NULL;
|
|
for(unsigned int i = 0; !bone && i < bones.size(); ++i)
|
|
if(bones[i]->ID == BoneID)
|
|
bone = bones[i];
|
|
|
|
if(!bone) mesh.Error(FormatString("There is no such bone with index %d", BoneID), vertexboneassignment_elem);
|
|
|
|
Vertex& vertex = Vertices[VertexIndex];
|
|
vertex.BoneAssignments.push_back(StdMeshVertexBoneAssignment());
|
|
StdMeshVertexBoneAssignment& assignment = vertex.BoneAssignments.back();
|
|
assignment.BoneIndex = bone->Index;
|
|
assignment.Weight = weight;
|
|
}
|
|
|
|
// Normalize vertex bone assignment weights (this is not guaranteed in the
|
|
// Ogre file format).
|
|
for(unsigned int i = 0; i < Vertices.size(); ++i)
|
|
{
|
|
Vertex& vertex = Vertices[i];
|
|
float sum = 0.0f;
|
|
for(unsigned int j = 0; j < vertex.BoneAssignments.size(); ++j)
|
|
sum += vertex.BoneAssignments[j].Weight;
|
|
for(unsigned int j = 0; j < vertex.BoneAssignments.size(); ++j)
|
|
vertex.BoneAssignments[j].Weight /= sum;
|
|
}
|
|
|
|
// Load Animations
|
|
TiXmlElement* animations_elem = skeleton.RequireFirstChild(skeleton_elem, "animations");
|
|
for(TiXmlElement* animation_elem = animations_elem->FirstChildElement("animation"); animation_elem != NULL; animation_elem = animation_elem->NextSiblingElement("animation"))
|
|
{
|
|
StdCopyStrBuf name(skeleton.RequireStrAttribute(animation_elem, "name"));
|
|
//StdStrBuf name(skeleton.RequireStrAttribute(animation_elem, "name"));
|
|
if(Animations.find(name) != Animations.end())
|
|
skeleton.Error(FormatString("There is already an animation with name '%s'", name.getData()), animation_elem);
|
|
|
|
StdMeshAnimation& animation = Animations.insert(std::make_pair(name, StdMeshAnimation())).first->second;
|
|
animation.Name = name;
|
|
animation.Length = skeleton.RequireFloatAttribute(animation_elem, "length");
|
|
animation.Tracks.resize(Bones.size());
|
|
|
|
TiXmlElement* tracks_elem = skeleton.RequireFirstChild(animation_elem, "tracks");
|
|
for(TiXmlElement* track_elem = tracks_elem->FirstChildElement("track"); track_elem != NULL; track_elem = track_elem->NextSiblingElement("track"))
|
|
{
|
|
const char* bone_name = skeleton.RequireStrAttribute(track_elem, "bone");
|
|
StdMeshBone* bone = NULL;
|
|
for(unsigned int i = 0; !bone && i < Bones.size(); ++i)
|
|
if(Bones[i]->Name == bone_name)
|
|
bone = Bones[i];
|
|
if(!bone) skeleton.Error(FormatString("There is no such bone with name '%s'", bone_name), track_elem);
|
|
|
|
if(animation.Tracks[bone->Index] != NULL) skeleton.Error(FormatString("There is already a track for bone '%s' in animation '%s'", bone_name, animation.Name.getData()), track_elem);
|
|
|
|
StdMeshTrack* track = new StdMeshTrack;
|
|
animation.Tracks[bone->Index] = track;
|
|
|
|
// Get inverse bone transformation matrix in OGRE coordiante system; we need it to apply
|
|
// the translation part of the bone transformation.
|
|
StdMeshMatrix bone_inverse_trans(bone->InverseTrans);
|
|
bone_inverse_trans.Mul(CoordCorrection);
|
|
bone_inverse_trans.Transform(CoordCorrectionInverse);
|
|
|
|
TiXmlElement* keyframes_elem = skeleton.RequireFirstChild(track_elem, "keyframes");
|
|
for(TiXmlElement* keyframe_elem = keyframes_elem->FirstChildElement("keyframe"); keyframe_elem != NULL; keyframe_elem = keyframe_elem->NextSiblingElement("keyframe"))
|
|
{
|
|
float time = skeleton.RequireFloatAttribute(keyframe_elem, "time");
|
|
StdMeshKeyFrame& frame = track->Frames[time];
|
|
|
|
TiXmlElement* translate_elem = skeleton.RequireFirstChild(keyframe_elem, "translate");
|
|
TiXmlElement* rotate_elem = skeleton.RequireFirstChild(keyframe_elem, "rotate");
|
|
TiXmlElement* scale_elem = skeleton.RequireFirstChild(keyframe_elem, "scale");
|
|
TiXmlElement* axis_elem = skeleton.RequireFirstChild(rotate_elem, "axis");
|
|
|
|
float dx = skeleton.RequireFloatAttribute(translate_elem, "x");
|
|
float dy = skeleton.RequireFloatAttribute(translate_elem, "y");
|
|
float dz = skeleton.RequireFloatAttribute(translate_elem, "z");
|
|
float sx = skeleton.RequireFloatAttribute(scale_elem, "x");
|
|
float sy = skeleton.RequireFloatAttribute(scale_elem, "y");
|
|
float sz = skeleton.RequireFloatAttribute(scale_elem, "z");
|
|
float angle = skeleton.RequireFloatAttribute(rotate_elem, "angle");
|
|
float rx = skeleton.RequireFloatAttribute(axis_elem, "x");
|
|
float ry = skeleton.RequireFloatAttribute(axis_elem, "y");
|
|
float rz = skeleton.RequireFloatAttribute(axis_elem, "z");
|
|
|
|
StdMeshMatrix helper;
|
|
frame.Trans.SetRotate(angle, rx, ry, rz);
|
|
helper.SetScale(sx, sy, sz);
|
|
frame.Trans.Transform(helper);
|
|
|
|
// Apply bone transformation to translation part
|
|
float dxp = bone_inverse_trans(0,0)*dx + bone_inverse_trans(0,1)*dy + bone_inverse_trans(0,2)*dz;
|
|
float dyp = bone_inverse_trans(1,0)*dx + bone_inverse_trans(1,1)*dy + bone_inverse_trans(1,2)*dz;
|
|
float dzp = bone_inverse_trans(2,0)*dx + bone_inverse_trans(2,1)*dy + bone_inverse_trans(2,2)*dz;
|
|
helper.SetTranslate(dxp, dyp, dzp);
|
|
frame.Trans.Transform(helper);
|
|
|
|
// Transform into Clonk coordinate system
|
|
frame.Trans.Transform(CoordCorrection);
|
|
frame.Trans.Mul(CoordCorrectionInverse);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
// Apply bone transformation on animation frames. We need to do this
|
|
// after the actual loading because we need to walk the bone list
|
|
// hierarchically.
|
|
for(unsigned int i = 0; i < Bones.size(); ++i)
|
|
{
|
|
if(animation.Tracks[i])
|
|
{
|
|
StdMeshTrack& track = *animation.Tracks[i];
|
|
|
|
// Get next parent track
|
|
StdMeshTrack* parent_track = NULL;
|
|
StdMeshBone* parent_bone = Bones[i]->Parent;
|
|
while(parent_bone && !(parent_track = animation.Tracks[parent_bone->Index]))
|
|
parent_bone = parent_bone->Parent;
|
|
assert(!parent_bone || parent_track);
|
|
|
|
for(std::map<float, StdMeshKeyFrame>::iterator iter = track.Frames.begin(); iter != track.Frames.end(); ++iter)
|
|
{
|
|
// TODO: If this bone's track is not as smooth as the parent animation
|
|
// (which means if there is more than one keyframe in the parent
|
|
// animation for each keyframe pair in the this bone's animation),
|
|
// then we need to insert additional child keyframes, so that the
|
|
// transformation for the child does not skip parent keyframes.
|
|
StdMeshKeyFrame& frame = iter->second;
|
|
|
|
// Apply transformation of parent tracks (for which we computed
|
|
// already the bone transformations, as we walk the bone list
|
|
// hierarchically) in bone's coordinate system.
|
|
frame.Trans.Mul(Bones[i]->InverseTrans);
|
|
frame.Trans.Transform(Bones[i]->Trans);
|
|
|
|
if(parent_bone)
|
|
frame.Trans.Transform(parent_track->GetTransformAt(iter->first));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Mesh has no skeleton
|
|
TiXmlElement* boneassignments_elem = submesh_elem->FirstChildElement("boneassignments");
|
|
if(boneassignments_elem)
|
|
{
|
|
// Bone assignements do not make sense then, as the
|
|
// actual bones are defined in the skeleton file.
|
|
mesh.Error(StdStrBuf("Mesh has bone assignments, but no skeleton"), boneassignments_elem);
|
|
}
|
|
}
|
|
}
|
|
|
|
void StdMesh::AddMasterBone(StdMeshBone* bone)
|
|
{
|
|
bone->Index = Bones.size(); // Remember index in master bone table
|
|
Bones.push_back(bone);
|
|
for(unsigned int i = 0; i < bone->Children.size(); ++i)
|
|
AddMasterBone(bone->Children[i]);
|
|
}
|
|
|
|
const StdMeshAnimation* StdMesh::GetAnimationByName(const StdStrBuf& name) const
|
|
{
|
|
StdCopyStrBuf name2(name);
|
|
std::map<StdCopyStrBuf, StdMeshAnimation>::const_iterator iter = Animations.find(name2);
|
|
if(iter == Animations.end()) return NULL;
|
|
return &iter->second;
|
|
}
|
|
|
|
StdMeshInstance::AnimationRef::AnimationRef(StdMeshInstance* instance, const StdStrBuf& animation_name):
|
|
Instance(instance), Anim(NULL), Changed(false)
|
|
{
|
|
const StdMeshAnimation* animation = instance->Mesh.GetAnimationByName(animation_name);
|
|
if(animation)
|
|
{
|
|
for(unsigned int i = 0; i < instance->Animations.size(); ++i)
|
|
if(instance->Animations[i].MeshAnimation == animation)
|
|
{ Anim = &instance->Animations[i]; break; }
|
|
}
|
|
}
|
|
|
|
StdMeshInstance::AnimationRef::AnimationRef(StdMeshInstance* instance, const StdMeshAnimation& animation):
|
|
Instance(instance), Anim(NULL), Changed(false)
|
|
{
|
|
for(unsigned int i = 0; i < instance->Animations.size(); ++i)
|
|
if(instance->Animations[i].MeshAnimation == &animation)
|
|
{ Anim = &instance->Animations[i]; break; }
|
|
}
|
|
|
|
const StdMeshAnimation& StdMeshInstance::AnimationRef::GetAnimation() const
|
|
{
|
|
return *Anim->MeshAnimation;
|
|
}
|
|
|
|
void StdMeshInstance::AnimationRef::SetPosition(float position)
|
|
{
|
|
assert(position <= Anim->MeshAnimation->Length);
|
|
Anim->Position = position;
|
|
Changed = true;
|
|
}
|
|
|
|
void StdMeshInstance::AnimationRef::SetWeight(float weight)
|
|
{
|
|
Anim->Weight = weight;
|
|
Changed = true;
|
|
}
|
|
|
|
StdMeshInstance::StdMeshInstance(const StdMesh& mesh):
|
|
Mesh(mesh), CurrentFaceOrdering(FO_Fixed),
|
|
BoneTransforms(Mesh.GetNumBones()), Vertices(Mesh.GetNumVertices()),
|
|
Faces(Mesh.Faces)
|
|
{
|
|
for(unsigned int i = 0; i < Mesh.GetNumVertices(); ++i)
|
|
Vertices[i] = Mesh.GetVertex(i);
|
|
}
|
|
|
|
void StdMeshInstance::SetFaceOrdering(FaceOrdering ordering)
|
|
{
|
|
CurrentFaceOrdering = ordering;
|
|
if(ordering != FO_Fixed)
|
|
ReorderFaces();
|
|
else
|
|
Faces = Mesh.Faces;
|
|
}
|
|
|
|
bool StdMeshInstance::PlayAnimation(const StdStrBuf& animation_name, float weight)
|
|
{
|
|
const StdMeshAnimation* animation = Mesh.GetAnimationByName(animation_name);
|
|
if(!animation) return false;
|
|
return PlayAnimation(*animation, weight);
|
|
}
|
|
|
|
bool StdMeshInstance::PlayAnimation(const StdMeshAnimation& animation, float weight)
|
|
{
|
|
for(unsigned int i = 0; i < Animations.size(); ++i)
|
|
if(Animations[i].MeshAnimation == &animation)
|
|
return false;
|
|
|
|
Animation anim;
|
|
anim.MeshAnimation = &animation;
|
|
anim.Position = 0.0f;
|
|
anim.Weight = weight;
|
|
Animations.push_back(anim);
|
|
UpdateBoneTransforms();
|
|
return true;
|
|
}
|
|
|
|
bool StdMeshInstance::StopAnimation(const StdStrBuf& animation_name)
|
|
{
|
|
const StdMeshAnimation* animation = Mesh.GetAnimationByName(animation_name);
|
|
if(!animation) return false;
|
|
return StopAnimation(*animation);
|
|
}
|
|
|
|
bool StdMeshInstance::StopAnimation(const StdMeshAnimation& animation)
|
|
{
|
|
for(std::vector<Animation>::iterator iter = Animations.begin();
|
|
iter != Animations.end(); ++iter)
|
|
{
|
|
if(iter->MeshAnimation == &animation)
|
|
{
|
|
Animations.erase(iter);
|
|
UpdateBoneTransforms();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void StdMeshInstance::UpdateBoneTransforms()
|
|
{
|
|
// Compute transformation matrix for each bone.
|
|
for(unsigned int i = 0; i < BoneTransforms.size(); ++i)
|
|
{
|
|
float accum_weight = 0.0f;
|
|
BoneTransforms[i].SetScale(0,0,0); // zero matrix
|
|
|
|
for(unsigned int j = 0; j < Animations.size(); ++j)
|
|
{
|
|
StdMeshTrack* track = Animations[j].MeshAnimation->Tracks[i];
|
|
if(track)
|
|
{
|
|
accum_weight += Animations[j].Weight;
|
|
StdMeshMatrix matr(track->GetTransformAt(Animations[j].Position));
|
|
matr.Mul(Animations[j].Weight);
|
|
BoneTransforms[i].Add(matr);
|
|
}
|
|
}
|
|
|
|
if(!accum_weight)
|
|
BoneTransforms[i].SetIdentity();
|
|
else
|
|
BoneTransforms[i].Mul(1.0f/accum_weight);
|
|
|
|
const StdMeshBone* bone = Mesh.Bones[i];
|
|
|
|
BoneTransforms[i].Mul(bone->InverseTrans);
|
|
BoneTransforms[i].Transform(bone->Trans);
|
|
|
|
const StdMeshBone* parent = bone->GetParent();
|
|
assert(!parent || parent->Index < i);
|
|
if(parent)
|
|
BoneTransforms[i].Transform(BoneTransforms[parent->Index]);
|
|
}
|
|
|
|
// Compute transformation for each vertex. We could later think about
|
|
// doing this on the GPU using a vertex shader. This would then probably
|
|
// need to go to CStdGL::PerformMesh and CStdD3D::PerformMesh.
|
|
for(unsigned int i = 0; i < Vertices.size(); ++i)
|
|
{
|
|
const StdMesh::Vertex& vertex = Mesh.Vertices[i];
|
|
if(!vertex.BoneAssignments.empty())
|
|
{
|
|
Vertices[i].x = Vertices[i].y = Vertices[i].z = 0.0f;
|
|
Vertices[i].nx = Vertices[i].ny = Vertices[i].nz = 0.0f;
|
|
Vertices[i].u = vertex.u; Vertices[i].v = vertex.v;
|
|
|
|
for(unsigned int j = 0; j < vertex.BoneAssignments.size(); ++j)
|
|
{
|
|
const StdMeshVertexBoneAssignment& assignment = vertex.BoneAssignments[j];
|
|
StdMeshVertex vtx = vertex;
|
|
vtx.Transform(BoneTransforms[assignment.BoneIndex]);
|
|
vtx.Mul(assignment.Weight);
|
|
Vertices[i].Add(vtx);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Vertices[i] = vertex;
|
|
}
|
|
}
|
|
|
|
if(CurrentFaceOrdering != FO_Fixed)
|
|
ReorderFaces();
|
|
}
|
|
|
|
void StdMeshInstance::ReorderFaces()
|
|
{
|
|
StdMeshInstanceFaceOrderingCmpPred pred(*this);
|
|
std::sort(Faces.begin(), Faces.end(), pred);
|
|
}
|