Support multiple submeshes

stable-5.2
Armin Burgmeier 2010-01-12 00:54:36 +01:00
parent 1b58885145
commit 7556bc36eb
3 changed files with 251 additions and 163 deletions

View File

@ -669,11 +669,11 @@ namespace
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, Color);
}
void RenderMeshImpl(StdMeshInstance& instance, DWORD dwModClr, DWORD dwPlayerColor)
void RenderSubMeshImpl(StdMeshInstance& instance, unsigned int submesh_index, DWORD dwModClr, DWORD dwPlayerColor)
{
const StdMesh& mesh = instance.Mesh;
const StdMeshMaterial& material = mesh.GetMaterial();
const StdSubMesh& submesh = instance.Mesh.GetSubMesh(submesh_index);
const StdMeshMaterial& material = submesh.GetMaterial();
assert(material.BestTechniqueIndex != -1);
const StdMeshMaterialTechnique& technique = material.Techniques[material.BestTechniqueIndex];
@ -688,6 +688,7 @@ namespace
float Ambient[4], Diffuse[4], Specular[4];
// TODO: We might also want to modulate emissive
// TODO: Pass pass.Ambient, pass.Diffuse, pass.Specular directly to the GL if dwModClr==0xffffffff
Ambient[0] = pass.Ambient[0] * ((dwModClr >> 16) & 0xff) / 255.0f;
Ambient[1] = pass.Ambient[1] * ((dwModClr >> 8) & 0xff) / 255.0f;
Ambient[2] = pass.Ambient[2] * ((dwModClr ) & 0xff) / 255.0f;
@ -716,7 +717,7 @@ namespace
// states that "The texture coordinate state for other client texture units
// is not updated, regardless of whether the client texture unit is enabled
// or not."
glInterleavedArrays(GL_N3F_V3F, sizeof(StdMeshVertex), &instance.GetVertices()[0].nx);
glInterleavedArrays(GL_N3F_V3F, sizeof(StdMeshVertex), &instance.GetVertices(submesh_index)[0].nx);
glMatrixMode(GL_TEXTURE);
GLuint have_texture = 0;
@ -744,7 +745,7 @@ namespace
glBindTexture(GL_TEXTURE_2D, have_texture);
}
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, sizeof(StdMeshVertex), &instance.GetVertices()[0].u);
glTexCoordPointer(2, GL_FLOAT, sizeof(StdMeshVertex), &instance.GetVertices(submesh_index)[0].u);
glLoadIdentity();
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
@ -829,7 +830,7 @@ namespace
}
glMatrixMode(GL_MODELVIEW);
glDrawElements(GL_TRIANGLES, instance.GetNumFaces()*3, GL_UNSIGNED_INT, instance.GetFaces());
glDrawElements(GL_TRIANGLES, instance.GetNumFaces(submesh_index)*3, GL_UNSIGNED_INT, instance.GetFaces(submesh_index));
for(unsigned int j = 0; j < pass.TextureUnits.size(); ++j)
{
@ -838,6 +839,15 @@ namespace
glDisable(GL_TEXTURE_2D);
}
}
}
void RenderMeshImpl(StdMeshInstance& instance, DWORD dwModClr, DWORD dwPlayerColor)
{
const StdMesh& mesh = instance.Mesh;
// Render each submesh
for(unsigned int i = 0; i < mesh.GetNumSubMeshes(); ++i)
RenderSubMeshImpl(instance, i, dwModClr, dwPlayerColor);
#if 0
// Draw attached bone

View File

@ -34,8 +34,10 @@ namespace
struct StdMeshInstanceFaceOrderingCmpPred
{
const StdMeshInstance& m_inst;
StdMeshInstanceFaceOrderingCmpPred(const StdMeshInstance& inst):
m_inst(inst) {}
const StdMeshVertex* m_vertices;
StdMeshInstanceFaceOrderingCmpPred(const StdMeshInstance& inst, unsigned int submesh):
m_inst(inst), m_vertices(m_inst.GetVertices(submesh)) {}
bool operator()(const StdMeshFace& face1, const StdMeshFace& face2) const
{
@ -48,8 +50,8 @@ namespace
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;
float z1 = m_vertices[face1.Vertices[0]].z + m_vertices[face1.Vertices[1]].z + m_vertices[face1.Vertices[2]].z;
float z2 = m_vertices[face2.Vertices[0]].z + m_vertices[face2.Vertices[1]].z + m_vertices[face2.Vertices[2]].z;
if(m_inst.GetFaceOrdering() == StdMeshInstance::FO_FarthestToNearest)
return z1 < z2;
else
@ -808,8 +810,12 @@ StdMeshAnimation& StdMeshAnimation::operator=(const StdMeshAnimation& other)
return *this;
}
StdMesh::StdMesh():
StdSubMesh::StdSubMesh():
Material(NULL)
{
}
StdMesh::StdMesh()
{
BoundingBox.x1 = BoundingBox.y1 = BoundingBox.z1 = 0.0f;
BoundingBox.x2 = BoundingBox.y2 = BoundingBox.z2 = 0.0f;
@ -827,77 +833,84 @@ void StdMesh::InitXML(const char* filename, const char* xml_data, StdMeshSkeleto
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* submesh_elem_base = mesh.RequireFirstChild(submeshes_elem, "submesh");
for(TiXmlElement* submesh_elem = submesh_elem_base; submesh_elem != NULL; submesh_elem = submesh_elem->NextSiblingElement("submesh"))
{
TiXmlElement* position_elem = mesh.RequireFirstChild(vertex_elem, "position");
TiXmlElement* normal_elem = mesh.RequireFirstChild(vertex_elem, "normal");
TiXmlElement* texcoord_elem = mesh.RequireFirstChild(vertex_elem, "texcoord");
TiXmlElement* geometry_elem = mesh.RequireFirstChild(submesh_elem, "geometry");
SubMeshes.push_back(StdSubMesh());
StdSubMesh& submesh = SubMeshes.back();
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");
const char* material = mesh.RequireStrAttribute(submesh_elem, "material");
submesh.Material = manager.GetMaterial(material);
if(!submesh.Material)
mesh.Error(FormatString("There is no such material named '%s'", material), submesh_elem);
// Convert to Clonk coordinate system (type does not match, StdMeshVertex vs. StdMesh::Vertex)
StdMeshVertex Transformed = CoordCorrection * Vertices[i];
Vertices[i].nx = Transformed.nx; Vertices[i].ny = Transformed.ny; Vertices[i].nz = Transformed.nz;
Vertices[i].x = Transformed.x; Vertices[i].y = Transformed.y; Vertices[i].z = Transformed.z;
int VertexCount = mesh.RequireIntAttribute(geometry_elem, "vertexcount");
submesh.Vertices.resize(VertexCount);
// Construct BoundingBox
if(i == 0)
TiXmlElement* buffer_elem = mesh.RequireFirstChild(geometry_elem, "vertexbuffer");
unsigned int i = 0;
for(TiXmlElement* vertex_elem = buffer_elem->FirstChildElement("vertex"); vertex_elem != NULL && i < submesh.Vertices.size(); vertex_elem = vertex_elem->NextSiblingElement("vertex"), ++i)
{
BoundingBox.x1 = BoundingBox.x2 = Vertices[i].x;
BoundingBox.y1 = BoundingBox.y2 = Vertices[i].y;
BoundingBox.z1 = BoundingBox.z2 = Vertices[i].z;
TiXmlElement* position_elem = mesh.RequireFirstChild(vertex_elem, "position");
TiXmlElement* normal_elem = mesh.RequireFirstChild(vertex_elem, "normal");
TiXmlElement* texcoord_elem = mesh.RequireFirstChild(vertex_elem, "texcoord");
submesh.Vertices[i].x = mesh.RequireFloatAttribute(position_elem, "x");
submesh.Vertices[i].y = mesh.RequireFloatAttribute(position_elem, "y");
submesh.Vertices[i].z = mesh.RequireFloatAttribute(position_elem, "z");
submesh.Vertices[i].nx = mesh.RequireFloatAttribute(normal_elem, "x");
submesh.Vertices[i].ny = mesh.RequireFloatAttribute(normal_elem, "y");
submesh.Vertices[i].nz = mesh.RequireFloatAttribute(normal_elem, "z");
submesh.Vertices[i].u = mesh.RequireFloatAttribute(texcoord_elem, "u");
submesh.Vertices[i].v = mesh.RequireFloatAttribute(texcoord_elem, "v");
// Convert to Clonk coordinate system (type does not match, StdMeshVertex vs. StdMesh::Vertex)
StdMeshVertex Transformed = CoordCorrection * submesh.Vertices[i];
submesh.Vertices[i].nx = Transformed.nx; submesh.Vertices[i].ny = Transformed.ny; submesh.Vertices[i].nz = Transformed.nz;
submesh.Vertices[i].x = Transformed.x; submesh.Vertices[i].y = Transformed.y; submesh.Vertices[i].z = Transformed.z;
// Construct BoundingBox
if(i == 0 && SubMeshes.size() == 1)
{
// First vertex
BoundingBox.x1 = BoundingBox.x2 = submesh.Vertices[i].x;
BoundingBox.y1 = BoundingBox.y2 = submesh.Vertices[i].y;
BoundingBox.z1 = BoundingBox.z2 = submesh.Vertices[i].z;
}
else
{
BoundingBox.x1 = Min(submesh.Vertices[i].x, BoundingBox.x1);
BoundingBox.x2 = Max(submesh.Vertices[i].x, BoundingBox.x2);
BoundingBox.y1 = Min(submesh.Vertices[i].y, BoundingBox.y1);
BoundingBox.y2 = Max(submesh.Vertices[i].y, BoundingBox.y2);
BoundingBox.z1 = Min(submesh.Vertices[i].z, BoundingBox.z1);
BoundingBox.z2 = Max(submesh.Vertices[i].z, BoundingBox.z2);
}
}
else
TiXmlElement* faces_elem = mesh.RequireFirstChild(submesh_elem, "faces");
int FaceCount = mesh.RequireIntAttribute(faces_elem, "count");
submesh.Faces.resize(FaceCount);
i = 0;
for(TiXmlElement* face_elem = faces_elem->FirstChildElement("face"); face_elem != NULL && i < submesh.Faces.size(); face_elem = face_elem->NextSiblingElement("face"), ++i)
{
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);
}
}
int v[3];
TiXmlElement* faces_elem = mesh.RequireFirstChild(submesh_elem, "faces");
int FaceCount = mesh.RequireIntAttribute(faces_elem, "count");
Faces.resize(FaceCount);
v[0] = mesh.RequireIntAttribute(face_elem, "v1");
v[1] = mesh.RequireIntAttribute(face_elem, "v2");
v[2] = mesh.RequireIntAttribute(face_elem, "v3");
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];
for(unsigned int j = 0; j < 3; ++j)
{
if(v[j] < 0 || static_cast<unsigned int>(v[j]) >= submesh.Vertices.size())
mesh.Error(FormatString("Vertex index v%u (%d) is out of range", j+1, v[j]), face_elem);
submesh.Faces[i].Vertices[j] = v[j];
}
}
}
@ -989,41 +1002,47 @@ void StdMesh::InitXML(const char* filename, const char* xml_data, StdMeshSkeleto
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"))
// Vertex<->Bone assignments for all vertices (need to go through SubMeshes again...)
unsigned int submesh_index = 0;
for(TiXmlElement* submesh_elem = submesh_elem_base; submesh_elem != NULL; submesh_elem = submesh_elem->NextSiblingElement("submesh"), ++submesh_index)
{
int BoneID = mesh.RequireIntAttribute(vertexboneassignment_elem, "boneindex");
int VertexIndex = mesh.RequireIntAttribute(vertexboneassignment_elem, "vertexindex");
float weight = mesh.RequireFloatAttribute(vertexboneassignment_elem, "weight");
StdSubMesh& submesh = SubMeshes[submesh_index];
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);
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");
StdMeshBone* bone = NULL;
for(unsigned int i = 0; !bone && i < bones.size(); ++i)
if(bones[i]->ID == BoneID)
bone = bones[i];
if(VertexIndex < 0 || static_cast<unsigned int>(VertexIndex) >= submesh.Vertices.size())
mesh.Error(FormatString("Vertex index in bone assignment (%d) is out of range", VertexIndex), vertexboneassignment_elem);
if(!bone) mesh.Error(FormatString("There is no such bone with index %d", BoneID), vertexboneassignment_elem);
StdMeshBone* bone = NULL;
for(unsigned int i = 0; !bone && i < bones.size(); ++i)
if(bones[i]->ID == BoneID)
bone = bones[i];
Vertex& vertex = Vertices[VertexIndex];
vertex.BoneAssignments.push_back(StdMeshVertexBoneAssignment());
StdMeshVertexBoneAssignment& assignment = vertex.BoneAssignments.back();
assignment.BoneIndex = bone->Index;
assignment.Weight = weight;
}
if(!bone) mesh.Error(FormatString("There is no such bone with index %d", BoneID), vertexboneassignment_elem);
// 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;
StdSubMesh::Vertex& vertex = submesh.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 < submesh.Vertices.size(); ++i)
{
StdSubMesh::Vertex& vertex = submesh.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
@ -1093,12 +1112,15 @@ void StdMesh::InitXML(const char* filename, const char* xml_data, StdMeshSkeleto
else
{
// Mesh has no skeleton
TiXmlElement* boneassignments_elem = submesh_elem->FirstChildElement("boneassignments");
if(boneassignments_elem)
for(TiXmlElement* submesh_elem = submesh_elem_base; submesh_elem != NULL; submesh_elem = submesh_elem->NextSiblingElement("submesh"))
{
// 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);
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);
}
}
}
}
@ -1160,11 +1182,20 @@ void StdMeshInstance::AnimationRef::SetWeight(float weight)
StdMeshInstance::StdMeshInstance(const StdMesh& mesh):
Mesh(mesh), CurrentFaceOrdering(FO_Fixed),
BoneTransforms(Mesh.GetNumBones(), StdMeshMatrix::Identity()),
Vertices(Mesh.GetNumVertices()), Faces(Mesh.Faces),
BoneTransformsDirty(false), AttachParent(NULL)
Vertices(Mesh.GetNumSubMeshes()), Faces(Mesh.GetNumSubMeshes()),
AttachParent(NULL), BoneTransformsDirty(false)
{
for(unsigned int i = 0; i < Mesh.GetNumVertices(); ++i)
Vertices[i] = Mesh.GetVertex(i);
for(unsigned int i = 0; i < Mesh.GetNumSubMeshes(); ++i)
{
const StdSubMesh& submesh = Mesh.GetSubMesh(i);
Vertices[i].resize(submesh.GetNumVertices());
Faces[i].resize(submesh.GetNumFaces());
for(unsigned int j = 0; j < submesh.GetNumVertices(); ++j)
Vertices[i][j] = submesh.GetVertex(j);
for(unsigned int j = 0; j < submesh.GetNumFaces(); ++j)
Faces[i][j] = submesh.GetFace(j);
}
}
StdMeshInstance::~StdMeshInstance()
@ -1179,7 +1210,15 @@ void StdMeshInstance::SetFaceOrdering(FaceOrdering ordering)
{
CurrentFaceOrdering = ordering;
if(ordering == FO_Fixed)
Faces = Mesh.Faces;
{
// Copy original face ordering from StdMesh
for(unsigned int i = 0; i < Mesh.GetNumSubMeshes(); ++i)
{
const StdSubMesh& submesh = Mesh.GetSubMesh(i);
for(unsigned int j = 0; j < submesh.GetNumFaces(); ++j)
Faces[i][j] = submesh.GetFace(j);
}
}
BoneTransformsDirty = true;
@ -1247,16 +1286,16 @@ const StdMeshInstance::AttachedMesh* StdMeshInstance::AttachMesh(const StdMesh&
attach.Number = iter->Number + 1;
// Lookup parent bone
for(i = 0; i < Mesh.Bones.size(); ++i)
if(Mesh.Bones[i]->Name == parent_bone)
for(i = 0; i < Mesh.GetNumBones(); ++i)
if(Mesh.GetBone(i).Name == parent_bone)
{ attach.ParentBone = i; break; }
if(i == Mesh.Bones.size()) return NULL;
if(i == Mesh.GetNumBones()) return NULL;
// Lookup child bone
for(i = 0; i < mesh.Bones.size(); ++i)
if(mesh.Bones[i]->Name == child_bone)
for(i = 0; i < mesh.GetNumBones(); ++i)
if(mesh.GetBone(i).Name == child_bone)
{ attach.ChildBone = i; break; }
if(i == mesh.Bones.size()) return NULL;
if(i == mesh.GetNumBones()) return NULL;
attach.Scale = scale;
attach.Parent = this;
@ -1317,8 +1356,8 @@ void StdMeshInstance::UpdateBoneTransforms()
}
}
const StdMeshBone* bone = Mesh.Bones[i];
const StdMeshBone* parent = bone->GetParent();
const StdMeshBone& bone = Mesh.GetBone(i);
const StdMeshBone* parent = bone.GetParent();
assert(!parent || parent->Index < i);
if(!accum_weight)
@ -1334,7 +1373,7 @@ void StdMeshInstance::UpdateBoneTransforms()
Transformation.rotate.Normalize(); // renormalize. We can skip this if we decide to use slerp
Transformation.translate *= 1.0f/accum_weight;
BoneTransforms[i] = StdMeshMatrix::Transform(bone->Transformation * Transformation * bone->InverseTransformation);
BoneTransforms[i] = StdMeshMatrix::Transform(bone.Transformation * Transformation * bone.InverseTransformation);
if(parent)
BoneTransforms[i] = BoneTransforms[parent->Index] * BoneTransforms[i];
@ -1347,23 +1386,30 @@ void StdMeshInstance::UpdateBoneTransforms()
// (can only work for fixed face ordering though)
for(unsigned int i = 0; i < Vertices.size(); ++i)
{
const StdMesh::Vertex& vertex = Mesh.Vertices[i];
if(!vertex.BoneAssignments.empty())
const StdSubMesh& submesh = Mesh.GetSubMesh(i);
std::vector<StdMeshVertex>& instance_vertices = Vertices[i];
assert(submesh.GetNumVertices() == instance_vertices.size());
for(unsigned int j = 0; j < instance_vertices.size(); ++j)
{
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 StdSubMesh::Vertex& vertex = submesh.GetVertex(j);
StdMeshVertex& instance_vertex = instance_vertices[j];
if(!vertex.BoneAssignments.empty())
{
const StdMeshVertexBoneAssignment& assignment = vertex.BoneAssignments[j];
Vertices[i] += assignment.Weight * (BoneTransforms[assignment.BoneIndex] * vertex);
instance_vertex.x = instance_vertex.y = instance_vertex.z = 0.0f;
instance_vertex.nx = instance_vertex.ny = instance_vertex.nz = 0.0f;
instance_vertex.u = vertex.u; instance_vertex.v = vertex.v;
for(unsigned int k = 0; k < vertex.BoneAssignments.size(); ++k)
{
const StdMeshVertexBoneAssignment& assignment = vertex.BoneAssignments[k];
instance_vertex += assignment.Weight * (BoneTransforms[assignment.BoneIndex] * vertex);
}
}
else
{
instance_vertex = vertex;
}
}
else
{
Vertices[i] = vertex;
}
}
@ -1376,7 +1422,11 @@ void StdMeshInstance::UpdateBoneTransforms()
// The idea is that a vertex at the child bone's position transforms to the parent bone's position.
// Therefore (read from right to left) we first apply the inverse of the child bone transformation,
// then an optional scaling matrix, and finally the parent bone transformation
// TODO: we can cache the three matrices in the middle since they don't change over time,
// reducing this to two matrix multiplications instead of four each frame.
// Might even be worth to compute the complete transformation directly when rendering then
// (saves per-instance memory, but requires recomputation if the animation does not change).
iter->AttachTrans = BoneTransforms[iter->ParentBone]
* StdMeshMatrix::Transform(Mesh.GetBone(iter->ParentBone).Transformation)
* StdMeshMatrix::Scale(iter->Scale, iter->Scale, iter->Scale)
@ -1392,6 +1442,14 @@ void StdMeshInstance::UpdateBoneTransforms()
void StdMeshInstance::ReorderFaces()
{
StdMeshInstanceFaceOrderingCmpPred pred(*this);
std::sort(Faces.begin(), Faces.end(), pred);
for(unsigned int i = 0; i < Faces.size(); ++i)
{
StdMeshInstanceFaceOrderingCmpPred pred(*this, i);
std::sort(Faces[i].begin(), Faces[i].end(), pred);
}
// TODO: Also reorder submeshes and attached meshes... maybe this face ordering
// is not a good idea after all. Another possibility to obtain the effect would be
// to first render to an offscreen texture, then render to screen (only for meshes
// with halftransparent clrmod applied)
}

View File

@ -234,9 +234,37 @@ struct StdMeshBox
float x2, y2, z2;
};
class StdSubMesh
{
friend class StdMesh;
public:
// Remember bone assignments for vertices
class Vertex: public StdMeshVertex
{
public:
std::vector<StdMeshVertexBoneAssignment> BoneAssignments;
};
const Vertex& GetVertex(unsigned int i) const { return Vertices[i]; }
unsigned int GetNumVertices() const { return Vertices.size(); }
const StdMeshFace& GetFace(unsigned int i) const { return Faces[i]; }
unsigned int GetNumFaces() const { return Faces.size(); }
const StdMeshMaterial& GetMaterial() const { return *Material; }
private:
StdSubMesh();
std::vector<Vertex> Vertices;
std::vector<StdMeshFace> Faces;
const StdMeshMaterial* Material;
};
class StdMesh
{
friend class StdMeshInstance;
//friend class StdMeshInstance;
public:
StdMesh();
~StdMesh();
@ -246,17 +274,13 @@ public:
// Throws StdMeshError.
void InitXML(const char* filename, const char* xml_data, StdMeshSkeletonLoader& skel_loader, const StdMeshMatManager& manager);
const StdMeshVertex& GetVertex(unsigned int i) const { return Vertices[i]; }
unsigned int GetNumVertices() const { return Vertices.size(); }
const StdMeshFace& GetFace(unsigned int i) const { return Faces[i]; }
unsigned int GetNumFaces() const { return Faces.size(); }
const StdSubMesh& GetSubMesh(unsigned int i) const { return SubMeshes[i]; }
unsigned int GetNumSubMeshes() const { return SubMeshes.size(); }
const StdMeshBone& GetBone(unsigned int i) const { return *Bones[i]; }
unsigned int GetNumBones() const { return Bones.size(); }
const StdMeshAnimation* GetAnimationByName(const StdStrBuf& name) const;
const StdMeshMaterial& GetMaterial() const { return *Material; }
const StdMeshBox& GetBoundingBox() const { return BoundingBox; }
@ -266,21 +290,12 @@ private:
StdMesh(const StdMesh& other); // non-copyable
StdMesh& operator=(const StdMesh& other); // non-assignable
// Remember bone assignments for vertices
class Vertex: public StdMeshVertex
{
public:
std::vector<StdMeshVertexBoneAssignment> BoneAssignments;
};
std::vector<Vertex> Vertices;
std::vector<StdMeshFace> Faces;
std::vector<StdSubMesh> SubMeshes;
std::vector<StdMeshBone*> Bones; // Master Bone Table
std::map<StdCopyStrBuf, StdMeshAnimation> Animations;
StdMeshBox BoundingBox;
const StdMeshMaterial* Material;
};
class StdMeshInstance
@ -357,16 +372,16 @@ public:
// Get vertex of instance, with current animation applied. This needs to
// go elsewhere if/when we want to calculate this on the hardware.
const StdMeshVertex& GetVertex(unsigned int i) const { return Vertices[i]; }
const StdMeshVertex* GetVertices() const { return &Vertices[0]; }
unsigned int GetNumVertices() const { return Vertices.size(); }
//const StdMeshVertex& GetVertex(unsigned int i) const { return Vertices[i]; }
const StdMeshVertex* GetVertices(unsigned int submesh) const { return &Vertices[submesh][0]; }
unsigned int GetNumVertices(unsigned int submesh) const { return Vertices[submesh].size(); }
// Get face of instance. The instance faces are the same as the mesh faces,
// with the exception that they are differently ordered, depending on the
// current FaceOrdering. See also SetFaceOrdering.
const StdMeshFace& GetFace(unsigned int i) const { return Faces[i]; }
const StdMeshFace* GetFaces() const { return &Faces[0]; }
unsigned int GetNumFaces() const { return Faces.size(); }
//const StdMeshFace& GetFace(unsigned int i) const { return Faces[i]; }
const StdMeshFace* GetFaces(unsigned int submesh) const { return &Faces[submesh][0]; }
unsigned int GetNumFaces(unsigned int submesh) const { return Faces[submesh].size(); }
const StdMeshMatrix& GetBoneTransform(unsigned int i) const { return BoneTransforms[i]; }
@ -394,8 +409,13 @@ protected:
std::vector<StdMeshMatrix> BoneTransforms;
std::vector<StdMeshVertex> Vertices;
std::vector<StdMeshFace> Faces;
// Vertices transformed according to current animation, for each submesh
// Faces sorted according to current face ordering, for each submesh
// TODO: We can skip these if we decide to either
// a) recompute Vertex positions each frame or
// b) compute them on the GPU
std::vector<std::vector<StdMeshVertex> > Vertices;
std::vector<std::vector<StdMeshFace> > Faces;
// Not asymptotically efficient, but we do not expect many attached meshes anyway.
// In theory map would fit better, but it's probably not worth the extra overhead.