Added celmodel module.
parent
31a31d4e2f
commit
ef146f24aa
|
@ -0,0 +1,728 @@
|
|||
// mesh.cpp
|
||||
//
|
||||
// Copyright (C) 2004-2010, the Celestia Development Team
|
||||
// Original version by Chris Laurel <claurel@gmail.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
|
||||
#include "mesh.h"
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
using namespace cmod;
|
||||
using namespace Eigen;
|
||||
using namespace std;
|
||||
|
||||
|
||||
static size_t VertexAttributeFormatSizes[Mesh::FormatMax] =
|
||||
{
|
||||
4, // Float1
|
||||
8, // Float2
|
||||
12, // Float3
|
||||
16, // Float4,
|
||||
4, // UByte4
|
||||
};
|
||||
|
||||
|
||||
Mesh::VertexDescription::VertexDescription(unsigned int _stride,
|
||||
unsigned int _nAttributes,
|
||||
VertexAttribute* _attributes) :
|
||||
stride(_stride),
|
||||
nAttributes(_nAttributes),
|
||||
attributes(NULL)
|
||||
{
|
||||
if (nAttributes != 0)
|
||||
{
|
||||
attributes = new VertexAttribute[nAttributes];
|
||||
for (unsigned int i = 0; i < nAttributes; i++)
|
||||
attributes[i] = _attributes[i];
|
||||
buildSemanticMap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Mesh::VertexDescription::VertexDescription(const VertexDescription& desc) :
|
||||
stride(desc.stride),
|
||||
nAttributes(desc.nAttributes),
|
||||
attributes(NULL)
|
||||
{
|
||||
if (nAttributes != 0)
|
||||
{
|
||||
attributes = new VertexAttribute[nAttributes];
|
||||
for (unsigned int i = 0; i < nAttributes; i++)
|
||||
attributes[i] = desc.attributes[i];
|
||||
buildSemanticMap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Mesh::VertexDescription&
|
||||
Mesh::VertexDescription::operator=(const Mesh::VertexDescription& desc)
|
||||
{
|
||||
if (nAttributes < desc.nAttributes)
|
||||
{
|
||||
if (attributes != NULL)
|
||||
delete[] attributes;
|
||||
attributes = new VertexAttribute[desc.nAttributes];
|
||||
}
|
||||
|
||||
nAttributes = desc.nAttributes;
|
||||
stride = desc.stride;
|
||||
for (unsigned int i = 0; i < nAttributes; i++)
|
||||
attributes[i] = desc.attributes[i];
|
||||
clearSemanticMap();
|
||||
buildSemanticMap();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
// TODO: This should be called in the constructor; we should start using
|
||||
// exceptions in Celestia.
|
||||
bool
|
||||
Mesh::VertexDescription::validate() const
|
||||
{
|
||||
for (unsigned int i = 0; i < nAttributes; i++)
|
||||
{
|
||||
VertexAttribute& attr = attributes[i];
|
||||
|
||||
// Validate the attribute
|
||||
if (attr.semantic >= SemanticMax || attr.format >= FormatMax)
|
||||
return false;
|
||||
if (attr.offset % 4 != 0)
|
||||
return false;
|
||||
if (attr.offset + VertexAttributeFormatSizes[attr.format] > stride)
|
||||
return false;
|
||||
// TODO: check for repetition of attributes
|
||||
// if (vertexAttributeMap[attr->semantic].format != InvalidFormat)
|
||||
// return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Mesh::VertexDescription::~VertexDescription()
|
||||
{
|
||||
delete[] attributes;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Mesh::VertexDescription::buildSemanticMap()
|
||||
{
|
||||
for (unsigned int i = 0; i < nAttributes; i++)
|
||||
semanticMap[attributes[i].semantic] = attributes[i];
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Mesh::VertexDescription::clearSemanticMap()
|
||||
{
|
||||
for (unsigned int i = 0; i < SemanticMax; i++)
|
||||
semanticMap[i] = VertexAttribute();
|
||||
}
|
||||
|
||||
|
||||
Mesh::PrimitiveGroup::PrimitiveGroup()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Mesh::PrimitiveGroup::~PrimitiveGroup()
|
||||
{
|
||||
// TODO: probably should free index list; need to sort out
|
||||
// ownership issues.
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
Mesh::PrimitiveGroup::getPrimitiveCount() const
|
||||
{
|
||||
switch (prim)
|
||||
{
|
||||
case TriList:
|
||||
return nIndices / 3;
|
||||
case TriStrip:
|
||||
case TriFan:
|
||||
return nIndices - 2;
|
||||
case LineList:
|
||||
return nIndices / 2;
|
||||
case LineStrip:
|
||||
return nIndices - 2;
|
||||
case PointList:
|
||||
case SpriteList:
|
||||
return nIndices;
|
||||
default:
|
||||
// Invalid value
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Mesh::Mesh() :
|
||||
vertexDesc(0, 0, NULL),
|
||||
nVertices(0),
|
||||
vertices(NULL),
|
||||
vbResource(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Mesh::~Mesh()
|
||||
{
|
||||
for (vector<PrimitiveGroup*>::iterator iter = groups.begin();
|
||||
iter != groups.end(); iter++)
|
||||
{
|
||||
delete *iter;
|
||||
}
|
||||
|
||||
// TODO: this is just to cast away void* and shut up GCC warnings;
|
||||
// should probably be static_cast<VertexList::VertexPart*>
|
||||
if (vertices != NULL)
|
||||
{
|
||||
delete[] static_cast<char*>(vertices);
|
||||
}
|
||||
|
||||
if (vbResource)
|
||||
{
|
||||
delete vbResource;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Mesh::setVertices(unsigned int _nVertices, void* vertexData)
|
||||
{
|
||||
if (vertexData == vertices)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: this is just to cast away void* and shut up GCC warnings;
|
||||
// should probably be static_cast<VertexList::VertexPart*>
|
||||
if (vertices != NULL)
|
||||
{
|
||||
delete[] static_cast<char*>(vertices);
|
||||
}
|
||||
|
||||
nVertices = _nVertices;
|
||||
vertices = vertexData;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Mesh::setVertexDescription(const VertexDescription& desc)
|
||||
{
|
||||
if (!desc.validate())
|
||||
return false;
|
||||
|
||||
vertexDesc = desc;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const Mesh::VertexDescription& Mesh::getVertexDescription() const
|
||||
{
|
||||
return vertexDesc;
|
||||
}
|
||||
|
||||
|
||||
const Mesh::PrimitiveGroup*
|
||||
Mesh::getGroup(unsigned int index) const
|
||||
{
|
||||
if (index >= groups.size())
|
||||
return NULL;
|
||||
else
|
||||
return groups[index];
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
Mesh::addGroup(PrimitiveGroup* group)
|
||||
{
|
||||
groups.push_back(group);
|
||||
return groups.size();
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
Mesh::addGroup(PrimitiveGroupType prim,
|
||||
unsigned int materialIndex,
|
||||
unsigned int nIndices,
|
||||
index32* indices)
|
||||
{
|
||||
PrimitiveGroup* g = new PrimitiveGroup();
|
||||
g->prim = prim;
|
||||
g->materialIndex = materialIndex;
|
||||
g->nIndices = nIndices;
|
||||
g->indices = indices;
|
||||
|
||||
return addGroup(g);
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
Mesh::getGroupCount() const
|
||||
{
|
||||
return groups.size();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Mesh::clearGroups()
|
||||
{
|
||||
for (vector<PrimitiveGroup*>::iterator iter = groups.begin();
|
||||
iter != groups.end(); iter++)
|
||||
{
|
||||
delete *iter;
|
||||
}
|
||||
|
||||
groups.clear();
|
||||
}
|
||||
|
||||
|
||||
const string&
|
||||
Mesh::getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Mesh::setName(const string& _name)
|
||||
{
|
||||
name = _name;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Mesh::remapIndices(const vector<index32>& indexMap)
|
||||
{
|
||||
for (vector<PrimitiveGroup*>::iterator iter = groups.begin();
|
||||
iter != groups.end(); iter++)
|
||||
{
|
||||
PrimitiveGroup* group = *iter;
|
||||
for (index32 i = 0; i < group->nIndices; i++)
|
||||
{
|
||||
group->indices[i] = indexMap[group->indices[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Mesh::remapMaterials(const vector<unsigned int>& materialMap)
|
||||
{
|
||||
for (vector<PrimitiveGroup*>::iterator iter = groups.begin();
|
||||
iter != groups.end(); iter++)
|
||||
{
|
||||
(*iter)->materialIndex = materialMap[(*iter)->materialIndex];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PrimitiveGroupComparator : public std::binary_function<const Mesh::PrimitiveGroup*, const Mesh::PrimitiveGroup*, bool>
|
||||
{
|
||||
public:
|
||||
bool operator()(const Mesh::PrimitiveGroup* g0, const Mesh::PrimitiveGroup* g1) const
|
||||
{
|
||||
return g0->materialIndex < g1->materialIndex;
|
||||
}
|
||||
|
||||
private:
|
||||
int unused;
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
Mesh::aggregateByMaterial()
|
||||
{
|
||||
sort(groups.begin(), groups.end(), PrimitiveGroupComparator());
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Mesh::pick(const Vector3d& rayOrigin, const Vector3d& rayDirection, double& distance) const
|
||||
{
|
||||
double maxDistance = 1.0e30;
|
||||
double closest = maxDistance;
|
||||
|
||||
// Pick will automatically fail without vertex positions--no reasonable
|
||||
// mesh should lack these.
|
||||
if (vertexDesc.getAttribute(Position).semantic != Position ||
|
||||
vertexDesc.getAttribute(Position).format != Float3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int posOffset = vertexDesc.getAttribute(Position).offset;
|
||||
char* vdata = reinterpret_cast<char*>(vertices);
|
||||
|
||||
// Iterate over all primitive groups in the mesh
|
||||
for (vector<PrimitiveGroup*>::const_iterator iter = groups.begin();
|
||||
iter != groups.end(); iter++)
|
||||
{
|
||||
Mesh::PrimitiveGroupType primType = (*iter)->prim;
|
||||
index32 nIndices = (*iter)->nIndices;
|
||||
|
||||
// Only attempt to compute the intersection of the ray with triangle
|
||||
// groups.
|
||||
if ((primType == TriList || primType == TriStrip || primType == TriFan) &&
|
||||
(nIndices >= 3) &&
|
||||
!(primType == TriList && nIndices % 3 != 0))
|
||||
{
|
||||
index32 index = 0;
|
||||
index32 i0 = (*iter)->indices[0];
|
||||
index32 i1 = (*iter)->indices[1];
|
||||
index32 i2 = (*iter)->indices[2];
|
||||
|
||||
// Iterate over the triangles in the primitive group
|
||||
do
|
||||
{
|
||||
// Get the triangle vertices v0, v1, and v2
|
||||
Vector3d v0 = Map<Vector3f>(reinterpret_cast<float*>(vdata + i0 * vertexDesc.stride + posOffset)).cast<double>();
|
||||
Vector3d v1 = Map<Vector3f>(reinterpret_cast<float*>(vdata + i1 * vertexDesc.stride + posOffset)).cast<double>();
|
||||
Vector3d v2 = Map<Vector3f>(reinterpret_cast<float*>(vdata + i2 * vertexDesc.stride + posOffset)).cast<double>();
|
||||
|
||||
// Compute the edge vectors e0 and e1, and the normal n
|
||||
Vector3d e0 = v1 - v0;
|
||||
Vector3d e1 = v2 - v0;
|
||||
Vector3d n = e0.cross(e1);
|
||||
|
||||
// c is the cosine of the angle between the ray and triangle normal
|
||||
double c = n.dot(rayDirection);
|
||||
|
||||
// If the ray is parallel to the triangle, it either misses the
|
||||
// triangle completely, or is contained in the triangle's plane.
|
||||
// If it's contained in the plane, we'll still call it a miss.
|
||||
if (c != 0.0)
|
||||
{
|
||||
double t = (n.dot(v0 - rayOrigin)) / c;
|
||||
if (t < closest && t > 0.0)
|
||||
{
|
||||
double m00 = e0.dot(e0);
|
||||
double m01 = e0.dot(e1);
|
||||
double m10 = e1.dot(e0);
|
||||
double m11 = e1.dot(e1);
|
||||
double det = m00 * m11 - m01 * m10;
|
||||
if (det != 0.0)
|
||||
{
|
||||
Vector3d p = rayOrigin + rayDirection * t;
|
||||
Vector3d q = p - v0;
|
||||
double q0 = e0.dot(q);
|
||||
double q1 = e1.dot(q);
|
||||
double d = 1.0 / det;
|
||||
double s0 = (m11 * q0 - m01 * q1) * d;
|
||||
double s1 = (m00 * q1 - m10 * q0) * d;
|
||||
if (s0 >= 0.0 && s1 >= 0.0 && s0 + s1 <= 1.0)
|
||||
closest = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the indices for the next triangle
|
||||
if (primType == TriList)
|
||||
{
|
||||
index += 3;
|
||||
if (index < nIndices)
|
||||
{
|
||||
i0 = (*iter)->indices[index + 0];
|
||||
i1 = (*iter)->indices[index + 1];
|
||||
i2 = (*iter)->indices[index + 2];
|
||||
}
|
||||
}
|
||||
else if (primType == TriStrip)
|
||||
{
|
||||
index += 1;
|
||||
if (index < nIndices)
|
||||
{
|
||||
i0 = i1;
|
||||
i1 = i2;
|
||||
i2 = (*iter)->indices[index];
|
||||
// TODO: alternate orientation of triangles in a strip
|
||||
}
|
||||
}
|
||||
else // primType == TriFan
|
||||
{
|
||||
index += 1;
|
||||
if (index < nIndices)
|
||||
{
|
||||
index += 1;
|
||||
i1 = i2;
|
||||
i2 = (*iter)->indices[index];
|
||||
}
|
||||
}
|
||||
|
||||
} while (index < nIndices);
|
||||
}
|
||||
}
|
||||
|
||||
if (closest != maxDistance)
|
||||
{
|
||||
distance = closest;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
void
|
||||
Mesh::render(const std::vector<const Material*>& materials,
|
||||
RenderContext& rc) const
|
||||
{
|
||||
// The first time the mesh is rendered, we will try and place the
|
||||
// vertex data in a vertex buffer object and potentially get a huge
|
||||
// rendering performance boost. This can consume a great deal of
|
||||
// memory, since we're duplicating the vertex data. TODO: investigate
|
||||
// the possibility of deleting the original data. We can always map
|
||||
// read-only later on for things like picking, but this could be a low
|
||||
// performance path.
|
||||
if (!vbInitialized && isVBOSupported())
|
||||
{
|
||||
vbInitialized = true;
|
||||
|
||||
if (nVertices * vertexDesc.stride > MinVBOSize)
|
||||
{
|
||||
glGenBuffersARB(1, &vbObject);
|
||||
if (vbObject != 0)
|
||||
{
|
||||
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbObject);
|
||||
glBufferDataARB(GL_ARRAY_BUFFER_ARB,
|
||||
nVertices * vertexDesc.stride,
|
||||
vertices,
|
||||
GL_STATIC_DRAW_ARB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vbObject != 0)
|
||||
{
|
||||
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbObject);
|
||||
rc.setVertexArrays(vertexDesc, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc.setVertexArrays(vertexDesc, vertices);
|
||||
}
|
||||
|
||||
unsigned int lastMaterial = ~0u;
|
||||
|
||||
// Iterate over all primitive groups in the mesh
|
||||
for (vector<PrimitiveGroup*>::const_iterator iter = groups.begin();
|
||||
iter != groups.end(); iter++)
|
||||
{
|
||||
// Set up the material
|
||||
const Material* mat = NULL;
|
||||
unsigned int materialIndex = (*iter)->materialIndex;
|
||||
if (materialIndex != lastMaterial && materialIndex < materials.size())
|
||||
mat = materials[materialIndex];
|
||||
|
||||
rc.setMaterial(mat);
|
||||
rc.drawGroup(**iter);
|
||||
}
|
||||
|
||||
if (vbObject != 0)
|
||||
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
AlignedBox<float, 3>
|
||||
Mesh::getBoundingBox() const
|
||||
{
|
||||
AlignedBox<float, 3> bbox;
|
||||
|
||||
// Return an empty box if there's no position info
|
||||
if (vertexDesc.getAttribute(Position).format != Float3)
|
||||
return bbox;
|
||||
|
||||
char* vdata = reinterpret_cast<char*>(vertices) + vertexDesc.getAttribute(Position).offset;
|
||||
|
||||
if (vertexDesc.getAttribute(PointSize).format == Float1)
|
||||
{
|
||||
// Handle bounding box calculation for point sprites. Unlike other
|
||||
// primitives, point sprite vertices have a non-zero size.
|
||||
int pointSizeOffset = (int) vertexDesc.getAttribute(PointSize).offset -
|
||||
(int) vertexDesc.getAttribute(Position).offset;
|
||||
|
||||
for (unsigned int i = 0; i < nVertices; i++, vdata += vertexDesc.stride)
|
||||
{
|
||||
Vector3f center = Map<Vector3f>(reinterpret_cast<float*>(vdata));
|
||||
float pointSize = (reinterpret_cast<float*>(vdata + pointSizeOffset))[0];
|
||||
Vector3f offsetVec = Vector3f::Constant(pointSize);
|
||||
|
||||
AlignedBox<float, 3> pointbox(center - offsetVec, center + offsetVec);
|
||||
bbox.extend(pointbox);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned int i = 0; i < nVertices; i++, vdata += vertexDesc.stride)
|
||||
bbox.extend(Map<Vector3f>(reinterpret_cast<float*>(vdata)));
|
||||
}
|
||||
|
||||
return bbox;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Mesh::transform(const Vector3f& translation, float scale)
|
||||
{
|
||||
if (vertexDesc.getAttribute(Position).format != Float3)
|
||||
return;
|
||||
|
||||
char* vdata = reinterpret_cast<char*>(vertices) + vertexDesc.getAttribute(Position).offset;
|
||||
unsigned int i;
|
||||
|
||||
// Scale and translate the vertex positions
|
||||
for (i = 0; i < nVertices; i++, vdata += vertexDesc.stride)
|
||||
{
|
||||
const Vector3f tv = (Map<Vector3f>(reinterpret_cast<float*>(vdata)) + translation) * scale;
|
||||
Map<Vector3f>(reinterpret_cast<float*>(vdata)) = tv;
|
||||
#if CELVEC
|
||||
reinterpret_cast<float*>(vdata)[0] = tv.x();
|
||||
reinterpret_cast<float*>(vdata)[1] = tv.y();
|
||||
reinterpret_cast<float*>(vdata)[2] = tv.z();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Point sizes need to be scaled as well
|
||||
if (vertexDesc.getAttribute(PointSize).format == Float1)
|
||||
{
|
||||
vdata = reinterpret_cast<char*>(vertices) + vertexDesc.getAttribute(PointSize).offset;
|
||||
for (i = 0; i < nVertices; i++, vdata += vertexDesc.stride)
|
||||
reinterpret_cast<float*>(vdata)[0] *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
Mesh::getPrimitiveCount() const
|
||||
{
|
||||
unsigned int count = 0;
|
||||
|
||||
for (vector<PrimitiveGroup*>::const_iterator iter = groups.begin();
|
||||
iter != groups.end(); iter++)
|
||||
{
|
||||
count += (*iter)->getPrimitiveCount();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Mesh::PrimitiveGroupType
|
||||
Mesh::parsePrimitiveGroupType(const string& name)
|
||||
{
|
||||
if (name == "trilist")
|
||||
return TriList;
|
||||
else if (name == "tristrip")
|
||||
return TriStrip;
|
||||
else if (name == "trifan")
|
||||
return TriFan;
|
||||
else if (name == "linelist")
|
||||
return LineList;
|
||||
else if (name == "linestrip")
|
||||
return LineStrip;
|
||||
else if (name == "points")
|
||||
return PointList;
|
||||
else if (name == "sprites")
|
||||
return SpriteList;
|
||||
else
|
||||
return InvalidPrimitiveGroupType;
|
||||
}
|
||||
|
||||
|
||||
Mesh::VertexAttributeSemantic
|
||||
Mesh::parseVertexAttributeSemantic(const string& name)
|
||||
{
|
||||
if (name == "position")
|
||||
return Position;
|
||||
else if (name == "normal")
|
||||
return Normal;
|
||||
else if (name == "color0")
|
||||
return Color0;
|
||||
else if (name == "color1")
|
||||
return Color1;
|
||||
else if (name == "tangent")
|
||||
return Tangent;
|
||||
else if (name == "texcoord0")
|
||||
return Texture0;
|
||||
else if (name == "texcoord1")
|
||||
return Texture1;
|
||||
else if (name == "texcoord2")
|
||||
return Texture2;
|
||||
else if (name == "texcoord3")
|
||||
return Texture3;
|
||||
else if (name == "pointsize")
|
||||
return PointSize;
|
||||
else
|
||||
return InvalidSemantic;
|
||||
}
|
||||
|
||||
|
||||
Mesh::VertexAttributeFormat
|
||||
Mesh::parseVertexAttributeFormat(const string& name)
|
||||
{
|
||||
if (name == "f1")
|
||||
return Float1;
|
||||
else if (name == "f2")
|
||||
return Float2;
|
||||
else if (name == "f3")
|
||||
return Float3;
|
||||
else if (name == "f4")
|
||||
return Float4;
|
||||
else if (name == "ub4")
|
||||
return UByte4;
|
||||
else
|
||||
return InvalidFormat;
|
||||
}
|
||||
|
||||
|
||||
Material::TextureSemantic
|
||||
Mesh::parseTextureSemantic(const string& name)
|
||||
{
|
||||
if (name == "texture0")
|
||||
return Material::DiffuseMap;
|
||||
else if (name == "normalmap")
|
||||
return Material::NormalMap;
|
||||
else if (name == "specularmap")
|
||||
return Material::SpecularMap;
|
||||
else if (name == "emissivemap")
|
||||
return Material::EmissiveMap;
|
||||
else
|
||||
return Material::InvalidTextureSemantic;
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
Mesh::getVertexAttributeSize(VertexAttributeFormat fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case Float1:
|
||||
case UByte4:
|
||||
return 4;
|
||||
case Float2:
|
||||
return 8;
|
||||
case Float3:
|
||||
return 12;
|
||||
case Float4:
|
||||
return 16;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
// mesh.h
|
||||
//
|
||||
// Copyright (C) 2004-2010, Celestia Development Team
|
||||
// Original version by Chris Laurel <claurel@gmail.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
|
||||
#ifndef _CELMODEL_MESH_H_
|
||||
#define _CELMODEL_MESH_H_
|
||||
|
||||
#include "material.h"
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/Geometry>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace cmod
|
||||
{
|
||||
|
||||
class Mesh
|
||||
{
|
||||
public:
|
||||
// 32-bit index type
|
||||
typedef unsigned int index32;
|
||||
class BufferResource
|
||||
{
|
||||
};
|
||||
|
||||
enum VertexAttributeSemantic
|
||||
{
|
||||
Position = 0,
|
||||
Color0 = 1,
|
||||
Color1 = 2,
|
||||
Normal = 3,
|
||||
Tangent = 4,
|
||||
Texture0 = 5,
|
||||
Texture1 = 6,
|
||||
Texture2 = 7,
|
||||
Texture3 = 8,
|
||||
PointSize = 9,
|
||||
SemanticMax = 10,
|
||||
InvalidSemantic = -1,
|
||||
};
|
||||
|
||||
enum VertexAttributeFormat
|
||||
{
|
||||
Float1 = 0,
|
||||
Float2 = 1,
|
||||
Float3 = 2,
|
||||
Float4 = 3,
|
||||
UByte4 = 4,
|
||||
FormatMax = 5,
|
||||
InvalidFormat = -1,
|
||||
};
|
||||
|
||||
struct VertexAttribute
|
||||
{
|
||||
VertexAttribute() :
|
||||
semantic(InvalidSemantic),
|
||||
format(InvalidFormat),
|
||||
offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
VertexAttribute(VertexAttributeSemantic _semantic,
|
||||
VertexAttributeFormat _format,
|
||||
unsigned int _offset) :
|
||||
semantic(_semantic),
|
||||
format(_format),
|
||||
offset(_offset)
|
||||
{
|
||||
}
|
||||
|
||||
VertexAttributeSemantic semantic;
|
||||
VertexAttributeFormat format;
|
||||
unsigned int offset;
|
||||
};
|
||||
|
||||
struct VertexDescription
|
||||
{
|
||||
VertexDescription(unsigned int _stride,
|
||||
unsigned int _nAttributes,
|
||||
VertexAttribute* _attributes);
|
||||
VertexDescription(const VertexDescription& desc);
|
||||
~VertexDescription();
|
||||
|
||||
const VertexAttribute& getAttribute(VertexAttributeSemantic semantic) const
|
||||
{
|
||||
return semanticMap[semantic];
|
||||
}
|
||||
|
||||
bool validate() const;
|
||||
|
||||
VertexDescription& operator=(const VertexDescription&);
|
||||
|
||||
unsigned int stride;
|
||||
unsigned int nAttributes;
|
||||
VertexAttribute* attributes;
|
||||
|
||||
private:
|
||||
void clearSemanticMap();
|
||||
void buildSemanticMap();
|
||||
|
||||
// Vertex attributes indexed by semantic
|
||||
VertexAttribute semanticMap[SemanticMax];
|
||||
};
|
||||
|
||||
enum PrimitiveGroupType
|
||||
{
|
||||
TriList = 0,
|
||||
TriStrip = 1,
|
||||
TriFan = 2,
|
||||
LineList = 3,
|
||||
LineStrip = 4,
|
||||
PointList = 5,
|
||||
SpriteList = 6,
|
||||
PrimitiveTypeMax = 7,
|
||||
InvalidPrimitiveGroupType = -1
|
||||
};
|
||||
|
||||
class PrimitiveGroup
|
||||
{
|
||||
public:
|
||||
PrimitiveGroup();
|
||||
~PrimitiveGroup();
|
||||
|
||||
unsigned int getPrimitiveCount() const;
|
||||
|
||||
PrimitiveGroupType prim;
|
||||
unsigned int materialIndex;
|
||||
index32* indices;
|
||||
unsigned int nIndices;
|
||||
};
|
||||
|
||||
Mesh();
|
||||
~Mesh();
|
||||
|
||||
void setVertices(unsigned int _nVertices, void* vertexData);
|
||||
bool setVertexDescription(const VertexDescription& desc);
|
||||
const VertexDescription& getVertexDescription() const;
|
||||
|
||||
const PrimitiveGroup* getGroup(unsigned int index) const;
|
||||
unsigned int addGroup(PrimitiveGroup* group);
|
||||
unsigned int addGroup(PrimitiveGroupType prim,
|
||||
unsigned int materialIndex,
|
||||
unsigned int nIndices,
|
||||
index32* indices);
|
||||
unsigned int getGroupCount() const;
|
||||
void remapIndices(const std::vector<index32>& indexMap);
|
||||
void clearGroups();
|
||||
|
||||
void remapMaterials(const std::vector<unsigned int>& materialMap);
|
||||
|
||||
/*! Reorder primitive groups so that groups with identical materials
|
||||
* appear sequentially in the primitive group list. This will reduce
|
||||
* the number of graphics state changes at render time.
|
||||
*/
|
||||
void aggregateByMaterial();
|
||||
|
||||
const std::string& getName() const;
|
||||
void setName(const std::string&);
|
||||
|
||||
bool pick(const Eigen::Vector3d& origin, const Eigen::Vector3d& direction, double& distance) const;
|
||||
|
||||
Eigen::AlignedBox<float, 3> getBoundingBox() const;
|
||||
void transform(const Eigen::Vector3f& translation, float scale);
|
||||
|
||||
const void* getVertexData() const { return vertices; }
|
||||
unsigned int getVertexCount() const { return nVertices; }
|
||||
unsigned int getVertexStride() const { return vertexDesc.stride; }
|
||||
unsigned int getPrimitiveCount() const;
|
||||
|
||||
static PrimitiveGroupType parsePrimitiveGroupType(const std::string&);
|
||||
static VertexAttributeSemantic parseVertexAttributeSemantic(const std::string&);
|
||||
static VertexAttributeFormat parseVertexAttributeFormat(const std::string&);
|
||||
static Material::TextureSemantic parseTextureSemantic(const std::string&);
|
||||
static unsigned int getVertexAttributeSize(VertexAttributeFormat);
|
||||
|
||||
private:
|
||||
void recomputeBoundingBox();
|
||||
|
||||
private:
|
||||
VertexDescription vertexDesc;
|
||||
|
||||
unsigned int nVertices;
|
||||
void* vertices;
|
||||
mutable BufferResource* vbResource;
|
||||
|
||||
std::vector<PrimitiveGroup*> groups;
|
||||
|
||||
std::string name;
|
||||
};
|
||||
|
||||
} // namespace cmod
|
||||
|
||||
#endif // !_CELMESH_MESH_H_
|
||||
|
|
@ -0,0 +1,456 @@
|
|||
// model.cpp
|
||||
//
|
||||
// Copyright (C) 2004-2010, Celestia Development Team
|
||||
// Original version by Chris Laurel <claurel@gmail.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
|
||||
#include "model.h"
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace cmod;
|
||||
using namespace Eigen;
|
||||
using namespace std;
|
||||
|
||||
|
||||
Model::Model() :
|
||||
opaque(true),
|
||||
normalized(false)
|
||||
{
|
||||
for (int i = 0; i < Material::TextureSemanticMax; i++)
|
||||
{
|
||||
textureUsage[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Model::~Model()
|
||||
{
|
||||
for (vector<Mesh*>::iterator iter = meshes.begin(); iter != meshes.end(); iter++)
|
||||
{
|
||||
delete *iter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const Material*
|
||||
Model::getMaterial(unsigned int index) const
|
||||
{
|
||||
if (index < materials.size())
|
||||
return materials[index];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
Model::addMaterial(const Material* m)
|
||||
{
|
||||
// Update the texture map usage information for the model. Since
|
||||
// the material being added isn't necessarily used by a mesh within
|
||||
// the model, we could potentially end up with false positives--this
|
||||
// won't cause any rendering troubles, but could hurt performance
|
||||
// if it forces multipass rendering when it's not required.
|
||||
for (int i = 0; i < Material::TextureSemanticMax; i++)
|
||||
{
|
||||
if (m->maps[i])
|
||||
{
|
||||
textureUsage[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
materials.push_back(m);
|
||||
return materials.size();
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
Model::getMaterialCount() const
|
||||
{
|
||||
return materials.size();
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
Model::getVertexCount() const
|
||||
{
|
||||
unsigned int count = 0;
|
||||
|
||||
for (vector<Mesh*>::const_iterator iter = meshes.begin();
|
||||
iter != meshes.end(); iter++)
|
||||
{
|
||||
count += (*iter)->getVertexCount();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
Model::getPrimitiveCount() const
|
||||
{
|
||||
unsigned int count = 0;
|
||||
|
||||
for (vector<Mesh*>::const_iterator iter = meshes.begin();
|
||||
iter != meshes.end(); iter++)
|
||||
{
|
||||
count += (*iter)->getPrimitiveCount();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
Model::getMeshCount() const
|
||||
{
|
||||
return meshes.size();
|
||||
}
|
||||
|
||||
|
||||
Mesh*
|
||||
Model::getMesh(unsigned int index) const
|
||||
{
|
||||
if (index < meshes.size())
|
||||
return meshes[index];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
Model::addMesh(Mesh* m)
|
||||
{
|
||||
meshes.push_back(m);
|
||||
return meshes.size();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Model::pick(const Vector3d& rayOrigin, const Vector3d& rayDirection, double& distance) const
|
||||
{
|
||||
double maxDistance = 1.0e30;
|
||||
double closest = maxDistance;
|
||||
|
||||
for (vector<Mesh*>::const_iterator iter = meshes.begin();
|
||||
iter != meshes.end(); iter++)
|
||||
{
|
||||
double d = maxDistance;
|
||||
if ((*iter)->pick(rayOrigin, rayDirection, d) && d < closest)
|
||||
{
|
||||
closest = d;
|
||||
}
|
||||
}
|
||||
|
||||
if (closest != maxDistance)
|
||||
{
|
||||
distance = closest;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! Translate and scale a model. The transformation applied to
|
||||
* each vertex in the model is:
|
||||
* v' = (v + translation) * scale
|
||||
*/
|
||||
void
|
||||
Model::transform(const Vector3f& translation, float scale)
|
||||
{
|
||||
for (vector<Mesh*>::const_iterator iter = meshes.begin(); iter != meshes.end(); iter++)
|
||||
(*iter)->transform(translation, scale);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Model::normalize(const Vector3f& centerOffset)
|
||||
{
|
||||
AlignedBox<float, 3> bbox;
|
||||
|
||||
for (vector<Mesh*>::const_iterator iter = meshes.begin(); iter != meshes.end(); iter++)
|
||||
bbox.extend((*iter)->getBoundingBox());
|
||||
|
||||
Vector3f center = (bbox.min() + bbox.max()) * 0.5f + centerOffset;
|
||||
Vector3f extents = bbox.max() - bbox.min();
|
||||
float maxExtent = extents.maxCoeff();
|
||||
|
||||
transform(-center, 2.0f / maxExtent);
|
||||
|
||||
normalized = true;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
operator<(const Material::Color& c0, const Material::Color& c1)
|
||||
{
|
||||
if (c0.red() < c1.red())
|
||||
return true;
|
||||
else if (c0.red() > c1.red())
|
||||
return false;
|
||||
|
||||
if (c0.green() < c1.green())
|
||||
return true;
|
||||
else if (c0.green() > c1.green())
|
||||
return false;
|
||||
|
||||
return c0.blue() < c1.blue();
|
||||
}
|
||||
|
||||
|
||||
// Define an ordering for materials; required for elimination of duplicate
|
||||
// materials.
|
||||
static bool
|
||||
operator<(const Material& m0, const Material& m1)
|
||||
{
|
||||
// Checking opacity first and doing it backwards is deliberate. It means
|
||||
// that after sorting, translucent materials will end up with higher
|
||||
// material indices than opaque ones. Ultimately, after sorting
|
||||
// mesh primitive groups by material, translucent groups will end up
|
||||
// rendered after opaque ones.
|
||||
if (m0.opacity < m1.opacity)
|
||||
return true;
|
||||
else if (m0.opacity > m1.opacity)
|
||||
return false;
|
||||
|
||||
// Reverse sense of comparison here--additive blending is 1, normal
|
||||
// blending is 0, and we'd prefer to render additively blended submeshes
|
||||
// last.
|
||||
if (m0.blend > m1.blend)
|
||||
return true;
|
||||
else if (m0.blend < m1.blend)
|
||||
return false;
|
||||
|
||||
if (m0.diffuse < m1.diffuse)
|
||||
return true;
|
||||
else if (m1.diffuse < m0.diffuse)
|
||||
return false;
|
||||
|
||||
if (m0.emissive < m1.emissive)
|
||||
return true;
|
||||
else if (m1.emissive < m0.emissive)
|
||||
return false;
|
||||
|
||||
if (m0.specular < m1.specular)
|
||||
return true;
|
||||
else if (m1.specular < m0.specular)
|
||||
return false;
|
||||
|
||||
if (m0.specularPower < m1.specularPower)
|
||||
return true;
|
||||
else if (m0.specularPower > m1.specularPower)
|
||||
return false;
|
||||
|
||||
for (unsigned int i = 0; i < Material::TextureSemanticMax; i++)
|
||||
{
|
||||
if (m0.maps[i] < m1.maps[i])
|
||||
return true;
|
||||
else if (m0.maps[i] > m1.maps[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
// Materials are identical
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Simple struct that pairs an index with a material; the index is used to
|
||||
// keep track of the original material index after sorting.
|
||||
struct IndexedMaterial
|
||||
{
|
||||
int originalIndex;
|
||||
const Material* material;
|
||||
};
|
||||
|
||||
|
||||
static bool
|
||||
operator<(const IndexedMaterial& im0, const IndexedMaterial& im1)
|
||||
{
|
||||
return *(im0.material) < *(im1.material);
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
operator!=(const IndexedMaterial& im0, const IndexedMaterial& im1)
|
||||
{
|
||||
return im0 < im1 || im1 < im0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Model::uniquifyMaterials()
|
||||
{
|
||||
// No work to do if there's just a single material
|
||||
if (materials.size() <= 1)
|
||||
return;
|
||||
|
||||
// Create an array of materials with the indices attached
|
||||
vector<IndexedMaterial> indexedMaterials;
|
||||
unsigned int i;
|
||||
for (i = 0; i < materials.size(); i++)
|
||||
{
|
||||
IndexedMaterial im;
|
||||
im.originalIndex = i;
|
||||
im.material = materials[i];
|
||||
indexedMaterials.push_back(im);
|
||||
}
|
||||
|
||||
// Sort the indexed materials so that we can uniquify them
|
||||
sort(indexedMaterials.begin(), indexedMaterials.end());
|
||||
|
||||
vector<const Material*> uniqueMaterials;
|
||||
vector<unsigned int> materialMap(materials.size());
|
||||
vector<unsigned int> duplicateMaterials;
|
||||
|
||||
// From the sorted material list construct the list of unique materials
|
||||
// and a map to convert old material indices into indices that can be
|
||||
// used with the uniquified material list.
|
||||
unsigned int uniqueMaterialCount = 0;
|
||||
for (i = 0; i < indexedMaterials.size(); i++)
|
||||
{
|
||||
if (i == 0 || indexedMaterials[i] != indexedMaterials[i - 1])
|
||||
{
|
||||
uniqueMaterialCount++;
|
||||
uniqueMaterials.push_back(indexedMaterials[i].material);
|
||||
}
|
||||
else
|
||||
{
|
||||
duplicateMaterials.push_back(i);
|
||||
}
|
||||
materialMap[indexedMaterials[i].originalIndex] = uniqueMaterialCount - 1;
|
||||
}
|
||||
|
||||
// Remap all the material indices in the model. Even if no materials have
|
||||
// been eliminated we've still sorted them by opacity, which is useful
|
||||
// when reordering meshes so that translucent ones are rendered last.
|
||||
for (vector<Mesh*>::iterator iter = meshes.begin();
|
||||
iter != meshes.end(); iter++)
|
||||
{
|
||||
(*iter)->remapMaterials(materialMap);
|
||||
}
|
||||
|
||||
vector<unsigned int>::const_iterator dupIter;
|
||||
for (dupIter = duplicateMaterials.begin();
|
||||
dupIter != duplicateMaterials.end(); ++dupIter)
|
||||
{
|
||||
delete indexedMaterials[*dupIter].material;
|
||||
}
|
||||
|
||||
materials = uniqueMaterials;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Model::determineOpacity()
|
||||
{
|
||||
for (unsigned int i = 0; i < materials.size(); i++)
|
||||
{
|
||||
if ((materials[i]->opacity > 0.01f && materials[i]->opacity < 1.0f) ||
|
||||
materials[i]->blend == Material::AdditiveBlend)
|
||||
{
|
||||
opaque = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
opaque = true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Model::usesTextureType(Material::TextureSemantic t) const
|
||||
{
|
||||
return textureUsage[static_cast<int>(t)];
|
||||
}
|
||||
|
||||
|
||||
class MeshComparatorAdapter : public std::binary_function<const Mesh*, const Mesh*, bool>
|
||||
{
|
||||
public:
|
||||
MeshComparatorAdapter(const Model::MeshComparator& c) :
|
||||
comparator(c)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator()(const Mesh* a, const Mesh* b) const
|
||||
{
|
||||
return comparator(*a, *b);
|
||||
}
|
||||
|
||||
private:
|
||||
const Model::MeshComparator& comparator;
|
||||
};
|
||||
|
||||
|
||||
Model::OpacityComparator::OpacityComparator()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// Look at the material used by last primitive group in the mesh for the
|
||||
// opacity of the whole model. This is a very crude way to check the opacity
|
||||
// of a mesh and misses many cases.
|
||||
static unsigned int
|
||||
getMeshMaterialIndex(const Mesh& mesh)
|
||||
{
|
||||
if (mesh.getGroupCount() == 0)
|
||||
return 0;
|
||||
else
|
||||
return mesh.getGroup(mesh.getGroupCount() - 1)->materialIndex;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool
|
||||
Model::OpacityComparator::operator()(const Mesh& a, const Mesh& b) const
|
||||
{
|
||||
// Because materials are sorted by opacity, we can just compare
|
||||
// the material index.
|
||||
return getMeshMaterialIndex(a) > getMeshMaterialIndex(b);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Model::sortMeshes(const MeshComparator& comparator)
|
||||
{
|
||||
// Sort submeshes by material; if materials have been uniquified,
|
||||
// then the submeshes will be ordered so that opaque ones are first.
|
||||
for (vector<Mesh*>::const_iterator iter = meshes.begin();
|
||||
iter != meshes.end(); iter++)
|
||||
{
|
||||
(*iter)->aggregateByMaterial();
|
||||
}
|
||||
|
||||
// Sort the meshes so that completely opaque ones are first
|
||||
sort(meshes.begin(), meshes.end(), MeshComparatorAdapter(comparator));
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
void
|
||||
Model::loadTextures()
|
||||
{
|
||||
for (vector<const Material*>::const_iterator iter = materials.begin();
|
||||
iter != materials.end(); iter++)
|
||||
{
|
||||
const Mesh::Material* m = *iter;
|
||||
|
||||
if (m->maps[Material::DiffuseMap] != InvalidResource)
|
||||
GetTextureManager()->find(m->maps[Material::DiffuseMap]);
|
||||
if (m->maps[Material::NormalMap] != InvalidResource)
|
||||
GetTextureManager()->find(m->maps[Material::NormalMap]);
|
||||
if (m->maps[Material::SpecularMap] != InvalidResource)
|
||||
GetTextureManager()->find(m->maps[Material::SpecularMap]);
|
||||
if (m->maps[Material::EmissiveMap] != InvalidResource)
|
||||
GetTextureManager()->find(m->maps[Material::EmissiveMap]);
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,159 @@
|
|||
// model.h
|
||||
//
|
||||
// Copyright (C) 2004-2010, Celestia Development Team
|
||||
// Original version by Chris Laurel <claurel@gmail.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
|
||||
#ifndef _CELMODEL_MODEL_H_
|
||||
#define _CELMODEL_MODEL_H_
|
||||
|
||||
#include "mesh.h"
|
||||
|
||||
|
||||
namespace cmod
|
||||
{
|
||||
|
||||
/*!
|
||||
* Model is the standard geometry object in Celestia. A Model
|
||||
* consists of a library of materials together with a list of
|
||||
* meshes. Each mesh object contains a pool of vertices and a
|
||||
* set of primitive groups. A primitive groups consists of a
|
||||
* a primitive group type and a list of vertex indices. This
|
||||
* structure is exactly the one used in Celestia model (.cmod)
|
||||
* files.
|
||||
*/
|
||||
class Model
|
||||
{
|
||||
public:
|
||||
Model();
|
||||
~Model();
|
||||
|
||||
/*! Return the material with the specified index, or NULL if
|
||||
* the index is out of range.
|
||||
*/
|
||||
const Material* getMaterial(unsigned int index) const;
|
||||
|
||||
/*! Add a new material to the model's material library; the
|
||||
* return value is the number of materials in the model.
|
||||
*/
|
||||
unsigned int addMaterial(const Material* material);
|
||||
|
||||
/*! Return the number of materials in the model
|
||||
*/
|
||||
unsigned int getMaterialCount() const;
|
||||
|
||||
/*! Return the total number of vertices in the model
|
||||
*/
|
||||
unsigned int getVertexCount() const;
|
||||
|
||||
/*! Return the total number of primitives in the model
|
||||
*/
|
||||
unsigned int getPrimitiveCount() const;
|
||||
|
||||
/*! Return the mesh with the specified index, or NULL if the
|
||||
* index is out of range.
|
||||
*/
|
||||
Mesh* getMesh(unsigned int index) const;
|
||||
|
||||
/*! Return the total number of meshes withing the model.
|
||||
*/
|
||||
unsigned int getMeshCount() const;
|
||||
|
||||
/*! Add a new mesh to the model; the return value is the
|
||||
* total number of meshes in the model.
|
||||
*/
|
||||
unsigned int addMesh(Mesh* mesh);
|
||||
|
||||
/*! Find the closest intersection between the ray (given
|
||||
* by origin and direction) and the model. If the ray
|
||||
* intersects the model, return true and set distance;
|
||||
* otherwise return false and leave distance unmodified.
|
||||
*/
|
||||
bool pick(const Eigen::Vector3d& rayOrigin,
|
||||
const Eigen::Vector3d& rayDirection,
|
||||
double& distance) const;
|
||||
|
||||
void transform(const Eigen::Vector3f& translation, float scale);
|
||||
|
||||
/** Apply a uniform scale to the model so that it fits into
|
||||
* a box with a center at centerOffset and a maximum side
|
||||
* length of one.
|
||||
*/
|
||||
void normalize(const Eigen::Vector3f& centerOffset);
|
||||
|
||||
/** Return true if the specified texture map type is used at
|
||||
* all within a mesh. This information is used to decide
|
||||
* if multiple rendering passes are required.
|
||||
*/
|
||||
virtual bool usesTextureType(Material::TextureSemantic) const;
|
||||
|
||||
/** Return true if the model has no translucent components. */
|
||||
virtual bool isOpaque() const
|
||||
{
|
||||
return opaque;
|
||||
}
|
||||
|
||||
virtual bool isNormalized() const
|
||||
{
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/*! Set the opacity flag based on material usage within the model */
|
||||
void determineOpacity();
|
||||
|
||||
|
||||
class MeshComparator
|
||||
{
|
||||
public:
|
||||
virtual ~MeshComparator() {};
|
||||
|
||||
virtual bool operator()(const Mesh&, const Mesh&) const = 0;
|
||||
};
|
||||
|
||||
/*! Sort the model's meshes in place. */
|
||||
void sortMeshes(const MeshComparator&);
|
||||
|
||||
/*! Optimize the model by eliminating all duplicated materials */
|
||||
void uniquifyMaterials();
|
||||
|
||||
/*! This comparator will roughly sort the model's meshes by
|
||||
* opacity so that transparent meshes are rendered last. It's far
|
||||
* from perfect, but covers a lot of cases. A better method of
|
||||
* opacity sorting would operate at the primitive group level, or
|
||||
* even better at the triangle level.
|
||||
*
|
||||
* Standard usage for this class is:
|
||||
* model->sortMeshes(Model::OpacityComparator());
|
||||
*
|
||||
* uniquifyMaterials() should be used before sortMeshes(), since
|
||||
* the opacity comparison depends on material indices being ordered
|
||||
* by opacity.
|
||||
*/
|
||||
class OpacityComparator : public MeshComparator
|
||||
{
|
||||
public:
|
||||
OpacityComparator();
|
||||
virtual ~OpacityComparator() {};
|
||||
|
||||
virtual bool operator()(const Mesh&, const Mesh&) const;
|
||||
|
||||
private:
|
||||
int unused;
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<const Material*> materials;
|
||||
std::vector<Mesh*> meshes;
|
||||
|
||||
bool textureUsage[Material::TextureSemanticMax];
|
||||
bool opaque;
|
||||
bool normalized;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // !_CELMODEL_MODEL_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,109 @@
|
|||
// modelfile.h
|
||||
//
|
||||
// Copyright (C) 2004-2010, Celestia Development Team
|
||||
// Original version by Chris Laurel <claurel@gmail.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
|
||||
#ifndef _CELMODEL_MODELFILE_H_
|
||||
#define _CELMODEL_MODELFILE_H_
|
||||
|
||||
#include "model.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#define CEL_MODEL_HEADER_LENGTH 16
|
||||
#define CEL_MODEL_HEADER_ASCII "#celmodel__ascii"
|
||||
#define CEL_MODEL_HEADER_BINARY "#celmodel_binary"
|
||||
|
||||
|
||||
namespace cmod
|
||||
{
|
||||
|
||||
/** Texture loading interface. Applications which want custom behavor for
|
||||
* texture loading should pass an instance of a TextureLoader subclass to
|
||||
* one of the model loading functions.
|
||||
*/
|
||||
class TextureLoader
|
||||
{
|
||||
public:
|
||||
virtual ~TextureLoader() {};
|
||||
virtual Material::TextureResource* loadTexture(const std::string& name) = 0;
|
||||
};
|
||||
|
||||
|
||||
class ModelLoader
|
||||
{
|
||||
public:
|
||||
ModelLoader();
|
||||
virtual ~ModelLoader();
|
||||
|
||||
virtual Model* load() = 0;
|
||||
|
||||
const std::string& getErrorMessage() const;
|
||||
TextureLoader* getTextureLoader() const;
|
||||
void setTextureLoader(TextureLoader* _textureLoader);
|
||||
|
||||
static ModelLoader* OpenModel(std::istream& in);
|
||||
|
||||
protected:
|
||||
virtual void reportError(const std::string&);
|
||||
|
||||
private:
|
||||
std::string errorMessage;
|
||||
TextureLoader* textureLoader;
|
||||
};
|
||||
|
||||
|
||||
class ModelWriter
|
||||
{
|
||||
public:
|
||||
virtual ~ModelWriter() {};
|
||||
|
||||
virtual bool write(const Model&) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Model* LoadModel(std::istream& in, TextureLoader* textureLoader = NULL);
|
||||
|
||||
bool SaveModelAscii(const Model* model, std::ostream& out);
|
||||
bool SaveModelBinary(const Model* model, std::ostream& out);
|
||||
|
||||
|
||||
// Binary file tokens
|
||||
enum ModelFileToken
|
||||
{
|
||||
CMOD_Material = 1001,
|
||||
CMOD_EndMaterial = 1002,
|
||||
CMOD_Diffuse = 1003,
|
||||
CMOD_Specular = 1004,
|
||||
CMOD_SpecularPower = 1005,
|
||||
CMOD_Opacity = 1006,
|
||||
CMOD_Texture = 1007,
|
||||
CMOD_Mesh = 1009,
|
||||
CMOD_EndMesh = 1010,
|
||||
CMOD_VertexDesc = 1011,
|
||||
CMOD_EndVertexDesc = 1012,
|
||||
CMOD_Vertices = 1013,
|
||||
CMOD_Emissive = 1014,
|
||||
CMOD_Blend = 1015,
|
||||
};
|
||||
|
||||
enum ModelFileType
|
||||
{
|
||||
CMOD_Float1 = 1,
|
||||
CMOD_Float2 = 2,
|
||||
CMOD_Float3 = 3,
|
||||
CMOD_Float4 = 4,
|
||||
CMOD_String = 5,
|
||||
CMOD_Uint32 = 6,
|
||||
CMOD_Color = 7,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // !_CELMODEL_MODELFILE_H_
|
Loading…
Reference in New Issue