Added celmodel module.

sensor-dev
Chris Laurel 2010-02-05 20:10:32 +00:00
parent 31a31d4e2f
commit ef146f24aa
6 changed files with 4204 additions and 0 deletions

View File

@ -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;
}
}

201
src/celmodel/mesh.h 100644
View File

@ -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_

View File

@ -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

View File

@ -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

View File

@ -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_