Updated cmodtools:

- Removed normal generation code from convert3ds.cpp; use the (better) normal
generator in cmodops.cpp instead.
- Removed nonfunctional code to use smoothing groups
- Added background color chooser for cmodview
- Enabled automatic uniquification and better normal generation when converting
3ds to cmod in cmodview and 3dstocmod utilities.
sensor-dev
Chris Laurel 2010-02-17 00:48:17 +00:00
parent faedefec0e
commit e8928c86cc
8 changed files with 229 additions and 229 deletions

View File

@ -10,6 +10,7 @@
// Convert a 3DS file to a Celestia mesh (.cmod) file
#include "convert3ds.h"
#include "cmodops.h"
#include <celmodel/modelfile.h>
#include <cel3ds/3dsread.h>
#include <cstring>
@ -53,6 +54,29 @@ int main(int argc, char* argv[])
return 1;
}
// Generate normals for the model
double smoothAngle = 45.0; // degrees
double weldTolerance = 1.0e-6;
bool weldVertices = true;
Model* newModel = GenerateModelNormals(*model, float(smoothAngle * 3.14159265 / 180.0), weldVertices, weldTolerance);
delete model;
if (!newModel)
{
cerr << "Ran out of memory while generating surface normals.\n";
return 1;
}
// Automatically uniquify vertices
for (unsigned int i = 0; newModel->getMesh(i) != NULL; i++)
{
Mesh* mesh = newModel->getMesh(i);
UniquifyVertices(*mesh);
}
model = newModel;
#if 0
// Print information about primitive groups
for (uint32 i = 0; model->getMesh(i); i++)

View File

@ -1236,6 +1236,72 @@ MergeModelMeshes(const Model& model)
}
static Material*
cloneMaterial(const Material* other)
{
Material* material = new Material();
material->diffuse = other->diffuse;
material->specular = other->specular;
material->emissive = other->emissive;
material->specularPower = other->specularPower;
material->opacity = other->opacity;
material->blend = other->blend;
for (int i = 0; i < Material::TextureSemanticMax; ++i)
{
if (other->maps[i])
{
material->maps[i] = new Material::DefaultTextureResource(other->maps[i]->source());
}
}
return material;
}
/*! Generate normals for an entire model. Return the new model, or null if
* normal generation failed due to an out of memory error.
*/
Model*
GenerateModelNormals(const Model& model, float smoothAngle, bool weldVertices, float weldTolerance)
{
Model* newModel = new Model();
// Copy materials
for (unsigned int i = 0; model.getMaterial(i) != NULL; i++)
{
newModel->addMaterial(cloneMaterial(model.getMaterial(i)));
}
bool ok = true;
for (unsigned int i = 0; model.getMesh(i) != NULL; i++)
{
Mesh* mesh = model.getMesh(i);
Mesh* newMesh = NULL;
newMesh = GenerateNormals(*mesh, smoothAngle, weldVertices, weldTolerance);
if (newMesh == NULL)
{
ok = false;
}
else
{
newModel->addMesh(newMesh);
}
}
if (!ok)
{
// If all of the meshes weren't processed due to an out of memory error,
// delete the new model and return NULL rather than a partially processed
// model.
delete newModel;
newModel = NULL;
}
return newModel;
}
#ifdef TRISTRIP
bool
convertToStrips(Mesh& mesh)

View File

@ -39,10 +39,15 @@ struct Face
};
// Mesh operations
extern cmod::Mesh* GenerateNormals(const cmod::Mesh& mesh, float smoothAngle, bool weld, float weldTolerance = 0.0f);
extern cmod::Mesh* GenerateTangents(const cmod::Mesh& mesh, bool weld);
extern bool UniquifyVertices(cmod::Mesh& mesh);
// Model operations
extern cmod::Model* MergeModelMeshes(const cmod::Model& model);
extern cmod::Model* GenerateModelNormals(const cmod::Model& model, float smoothAngle, bool weldVertices, float weldTolerance);
template<typename T, typename U> void
JoinVertices(std::vector<Face>& faces,
@ -86,10 +91,10 @@ JoinVertices(std::vector<Face>& faces,
uint32 uniqueCount = 0;
for (uint32 i = 0; i < nVertices; i++)
{
if (i == 0 || !equivalencePredicate(vertices[i - 1], vertices[i]))
{
if (i == 0 || !equivalencePredicate(vertices[i - 1], vertices[i]))
{
lastUnique = i;
uniqueCount++;
uniqueCount++;
}
mergeMap[vertices[i].index] = vertices[lastUnique].index;
@ -99,7 +104,9 @@ JoinVertices(std::vector<Face>& faces,
for (f = 0; f < faces.size(); f++)
{
for (uint32 k= 0; k < 3; k++)
{
faces[f].vi[k] = mergeMap[faces[f].i[k]];
}
}
}

View File

@ -55,178 +55,33 @@ Convert3DSMesh(Model& model,
const M3DScene& scene,
const string& meshName)
{
int nFaces = mesh3ds.getFaceCount();
int nVertices = mesh3ds.getVertexCount();
int nTexCoords = mesh3ds.getTexCoordCount();
bool smooth = (mesh3ds.getSmoothingGroupCount() == nFaces);
bool hasTexCoords = (nTexCoords == nVertices);
int vertexSize = hasTexCoords ? 8 : 6;
int i;
Vector3f* faceNormals = new Vector3f[nFaces];
Vector3f* vertexNormals = new Vector3f[nFaces * 3];
int* faceCounts = new int[nVertices];
int** vertexFaces = new int*[nVertices];
int nOutputVertices = nFaces * 3;
float* vertices = new float[nOutputVertices * vertexSize];
for (i = 0; i < nVertices; i++)
bool hasTexCoords = (nTexCoords >= nVertices);
int vertexSize = 3;
if (hasTexCoords)
{
faceCounts[i] = 0;
vertexFaces[i] = NULL;
vertexSize += 2;
}
// generate face normals
for (i = 0; i < nFaces; i++)
float* vertices = new float[mesh3ds.getVertexCount() * vertexSize];
// Build the vertex list
for (int i = 0; i < mesh3ds.getVertexCount(); ++i)
{
uint16 v0, v1, v2;
mesh3ds.getFace(i, v0, v1, v2);
int k = i * vertexSize;
Vector3f pos = mesh3ds.getVertex(i);
vertices[k + 0] = pos.x();
vertices[k + 1] = pos.y();
vertices[k + 2] = pos.z();
faceCounts[v0]++;
faceCounts[v1]++;
faceCounts[v2]++;
Vector3f p0 = mesh3ds.getVertex(v0);
Vector3f p1 = mesh3ds.getVertex(v1);
Vector3f p2 = mesh3ds.getVertex(v2);
faceNormals[i] = (p1 - p0).cross(p2 - p1);
faceNormals[i].normalize();
}
if (!smooth && 0)
{
for (i = 0; i < nFaces; i++)
if (hasTexCoords)
{
vertexNormals[i * 3] = faceNormals[i];
vertexNormals[i * 3 + 1] = faceNormals[i];
vertexNormals[i * 3 + 2] = faceNormals[i];
Vector2f texCoord = mesh3ds.getTexCoord(i);
vertices[k + 3] = texCoord.x();
vertices[k + 4] = texCoord.y();
}
}
else
{
// allocate space for vertex face indices
for (i = 0; i < nVertices; i++)
{
vertexFaces[i] = new int[faceCounts[i] + 1];
vertexFaces[i][0] = faceCounts[i];
}
for (i = 0; i < nFaces; i++)
{
uint16 v0, v1, v2;
mesh3ds.getFace(i, v0, v1, v2);
vertexFaces[v0][faceCounts[v0]--] = i;
vertexFaces[v1][faceCounts[v1]--] = i;
vertexFaces[v2][faceCounts[v2]--] = i;
}
// average face normals to compute the vertex normals
for (i = 0; i < nFaces; i++)
{
uint16 v0, v1, v2;
mesh3ds.getFace(i, v0, v1, v2);
// uint32 smoothingGroups = mesh3ds.getSmoothingGroups(i);
int j;
Vector3f v = Vector3f(0, 0, 0);
for (j = 1; j <= vertexFaces[v0][0]; j++)
{
int k = vertexFaces[v0][j];
// if (k == i || (smoothingGroups & mesh3ds.getSmoothingGroups(k)) != 0)
if (faceNormals[i].dot(faceNormals[k]) > 0.5f)
{
v += faceNormals[k];
}
}
if (v.squaredNorm() == 0.0f)
{
v = Vector3f::UnitX();
}
v.normalize();
vertexNormals[i * 3] = v;
v = Vector3f(0, 0, 0);
for (j = 1; j <= vertexFaces[v1][0]; j++)
{
int k = vertexFaces[v1][j];
// if (k == i || (smoothingGroups & mesh3ds.getSmoothingGroups(k)) != 0)
if (faceNormals[i].dot(faceNormals[k]) > 0.5f)
{
v += faceNormals[k];
}
}
if (v.squaredNorm() == 0.0f)
{
v = Vector3f::UnitX();
}
v.normalize();
vertexNormals[i * 3 + 1] = v;
v = Vector3f(0, 0, 0);
for (j = 1; j <= vertexFaces[v2][0]; j++)
{
int k = vertexFaces[v2][j];
// if (k == i || (smoothingGroups & mesh3ds.getSmoothingGroups(k)) != 0)
if (faceNormals[i].dot(faceNormals[k]) > 0.5f)
{
v += faceNormals[k];
}
}
if (v.squaredNorm() == 0.0f)
{
v = Vector3f::UnitX();
}
v.normalize();
vertexNormals[i * 3 + 2] = v;
}
}
// build the triangle list
for (i = 0; i < nFaces; i++)
{
uint16 triVert[3];
mesh3ds.getFace(i, triVert[0], triVert[1], triVert[2]);
for (int j = 0; j < 3; j++)
{
Vector3f pos = mesh3ds.getVertex(triVert[j]);
Vector3f norm = vertexNormals[i * 3 + j];
int k = (i * 3 + j) * vertexSize;
vertices[k + 0] = pos.x();
vertices[k + 1] = pos.y();
vertices[k + 2] = pos.z();
vertices[k + 3] = norm.x();
vertices[k + 4] = norm.y();
vertices[k + 5] = norm.z();
if (hasTexCoords)
{
vertices[k + 6] = mesh3ds.getTexCoord(triVert[j]).x();
vertices[k + 7] = mesh3ds.getTexCoord(triVert[j]).y();
}
}
}
// clean up
if (faceNormals != NULL)
delete[] faceNormals;
if (vertexNormals != NULL)
delete[] vertexNormals;
if (faceCounts != NULL)
delete[] faceCounts;
if (vertexFaces != NULL)
{
for (i = 0; i < nVertices; i++)
{
if (vertexFaces[i] != NULL)
delete[] vertexFaces[i];
}
delete[] vertexFaces;
}
Mesh::VertexAttribute attributes[8];
uint32 nAttributes = 0;
@ -237,11 +92,6 @@ Convert3DSMesh(Model& model,
nAttributes++;
offset += 12;
// Normal attribute is always present
attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Normal, Mesh::Float3, offset);
nAttributes++;
offset += 12;
if (hasTexCoords)
{
attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Texture0, Mesh::Float2, offset);
@ -252,7 +102,7 @@ Convert3DSMesh(Model& model,
// Create the Celestia mesh
Mesh* mesh = new Mesh();
mesh->setVertexDescription(Mesh::VertexDescription(offset, nAttributes, attributes));
mesh->setVertices(nOutputVertices, vertices);
mesh->setVertices(nVertices, vertices);
mesh->setName(meshName);
@ -265,9 +115,11 @@ Convert3DSMesh(Model& model,
for (unsigned int i = 0; i < faceCount; i++)
{
indices[i * 3 + 0] = i * 3 + 0;
indices[i * 3 + 1] = i * 3 + 1;
indices[i * 3 + 2] = i * 3 + 2;
uint16 v0 = 0, v1 = 0, v2 = 0;
mesh3ds.getFace(i, v0, v1, v2);
indices[i * 3 + 0] = v0;
indices[i * 3 + 1] = v1;
indices[i * 3 + 2] = v2;
}
mesh->addGroup(Mesh::TriList, ~0, faceCount * 3, indices);
@ -280,21 +132,20 @@ Convert3DSMesh(Model& model,
{
M3DMeshMaterialGroup* matGroup = mesh3ds.getMeshMaterialGroup(groupIndex);
// Vertex lists are not indexed, so the conversion to an indexed format is
// trivial (although much space is wasted storing unnecessary indices.)
uint32 nMatGroupFaces = matGroup->faces.size();
uint32 nMatGroupVertices = nMatGroupFaces * 3;
uint32* indices = new uint32[nMatGroupVertices];
uint32* indices = new uint32[nMatGroupFaces * 3];
for (unsigned int i = 0; i < nMatGroupFaces; i++)
{
uint16 v0 = 0, v1 = 0, v2 = 0;
uint16 faceIndex = matGroup->faces[i];
indices[i * 3 + 0] = faceIndex * 3 + 0;
indices[i * 3 + 1] = faceIndex * 3 + 1;
indices[i * 3 + 2] = faceIndex * 3 + 2;
mesh3ds.getFace(faceIndex, v0, v1, v2);
indices[i * 3 + 0] = v0;
indices[i * 3 + 1] = v1;
indices[i * 3 + 2] = v2;
}
// Convert the 3DS mesh's material
// Get the material index
unsigned int materialIndex = ~0u;
string material3dsName = matGroup->materialName;
if (!material3dsName.empty())
@ -309,7 +160,7 @@ Convert3DSMesh(Model& model,
}
}
mesh->addGroup(Mesh::TriList, materialIndex, nMatGroupVertices, indices);
mesh->addGroup(Mesh::TriList, materialIndex, nMatGroupFaces * 3, indices);
}
}

View File

@ -59,9 +59,12 @@ MainWindow::MainWindow() :
QAction* wireFrameStyleAction = new QAction(tr("&Wireframe"), styleGroup);
wireFrameStyleAction->setCheckable(true);
wireFrameStyleAction->setData((int) ModelViewWidget::WireFrameStyle);
QAction* backgroundColorAction = new QAction(tr("&Background Color..."), this);
styleMenu->addAction(normalStyleAction);
styleMenu->addAction(wireFrameStyleAction);
styleMenu->addSeparator();
styleMenu->addAction(backgroundColorAction);
menuBar->addMenu(styleMenu);
QMenu* operationsMenu = new QMenu(tr("&Operations"));
@ -97,6 +100,7 @@ MainWindow::MainWindow() :
// Connect Style menu
connect(styleGroup, SIGNAL(triggered(QAction*)), this, SLOT(setRenderStyle(QAction*)));
connect(backgroundColorAction, SIGNAL(triggered()), this, SLOT(editBackgroundColor()));
// Connect Operations menu
connect(generateNormalsAction, SIGNAL(triggered()), this, SLOT(generateNormals()));
@ -105,6 +109,33 @@ MainWindow::MainWindow() :
connect(mergeMeshesAction, SIGNAL(triggered()), this, SLOT(mergeMeshes()));
editMaterialAction->setShortcut(QKeySequence("Ctrl+E"));
connect(editMaterialAction, SIGNAL(triggered()), this, SLOT(editMaterial()));
// Apply settings
QSettings settings;
QColor backgroundColor = settings.value("BackgroundColor", QColor(0, 0, 128)).value<QColor>();
m_modelView->setBackgroundColor(backgroundColor);
}
static Material*
cloneMaterial(const Material* other)
{
Material* material = new Material();
material->diffuse = other->diffuse;
material->specular = other->specular;
material->emissive = other->emissive;
material->specularPower = other->specularPower;
material->opacity = other->opacity;
material->blend = other->blend;
for (int i = 0; i < Material::TextureSemanticMax; ++i)
{
if (other->maps[i])
{
material->maps[i] = new Material::DefaultTextureResource(other->maps[i]->source());
}
}
return material;
}
@ -273,7 +304,29 @@ MainWindow::openModel(const QString& fileName)
delete scene;
setModel(fileName, model);
// Generate normals for the model
double smoothAngle = 45.0; // degrees
double weldTolerance = 1.0e-6;
bool weldVertices = true;
Model* newModel = GenerateModelNormals(*model, float(smoothAngle * 3.14159265 / 180.0), weldVertices, weldTolerance);
delete model;
if (!newModel)
{
QMessageBox::warning(this, tr("Mesh Load Error"), tr("Internal error when loading mesh"));
}
else
{
// Automatically uniquify vertices
for (unsigned int i = 0; newModel->getMesh(i) != NULL; i++)
{
Mesh* mesh = newModel->getMesh(i);
UniquifyVertices(*mesh);
}
setModel(fileName, newModel);
}
}
else if (info.suffix().toLower() == "cmod")
{
@ -367,28 +420,6 @@ MainWindow::setRenderStyle(QAction* action)
}
static Material*
cloneMaterial(const Material* other)
{
Material* material = new Material();
material->diffuse = other->diffuse;
material->specular = other->specular;
material->emissive = other->emissive;
material->specularPower = other->specularPower;
material->opacity = other->opacity;
material->blend = other->blend;
for (int i = 0; i < Material::TextureSemanticMax; ++i)
{
if (other->maps[i])
{
material->maps[i] = new Material::DefaultTextureResource(other->maps[i]->source());
}
}
return material;
}
void
MainWindow::generateNormals()
{
@ -436,33 +467,16 @@ MainWindow::generateNormals()
double weldTolerance = toleranceEdit->text().toDouble();
bool weldVertices = true;
Model* newModel = new Model();
// Copy materials
for (unsigned int i = 0; model->getMaterial(i) != NULL; i++)
Model* newModel = GenerateModelNormals(*model, float(smoothAngle * 3.14159265 / 180.0), weldVertices, weldTolerance);
if (!newModel)
{
newModel->addMaterial(cloneMaterial(model->getMaterial(i)));
QMessageBox::warning(this, tr("Internal Error"), tr("Out of memory error during normal generation"));
}
for (unsigned int i = 0; model->getMesh(i) != NULL; i++)
else
{
Mesh* mesh = model->getMesh(i);
Mesh* newMesh = NULL;
newMesh = GenerateNormals(*mesh, float(smoothAngle * 3.14159265 / 180.0), weldVertices, weldTolerance);
if (newMesh == NULL)
{
cerr << "Error generating normals!\n";
// TODO: Clone the mesh and add it to the model
}
else
{
newModel->addMesh(newMesh);
}
setModel(modelFileName(), newModel);
}
setModel(modelFileName(), newModel);
settings.setValue("SmoothAngle", smoothAngle);
settings.setValue("WeldTolerance", weldTolerance);
}
@ -615,3 +629,21 @@ MainWindow::changeCurrentMaterial(const Material& material)
m_modelView->setMaterial(selectedGroup->materialIndex, material);
}
}
void
MainWindow::editBackgroundColor()
{
QColor originalColor = m_modelView->backgroundColor();
QColorDialog dialog(originalColor, this);
connect(&dialog, SIGNAL(currentColorChanged(QColor)), m_modelView, SLOT(setBackgroundColor(QColor)));
if (dialog.exec() == QDialog::Accepted)
{
QSettings settings;
settings.setValue("BackgroundColor", m_modelView->backgroundColor());
}
else
{
m_modelView->setBackgroundColor(originalColor);
}
}

View File

@ -53,6 +53,8 @@ public slots:
void changeCurrentMaterial(const cmod::Material&);
void editBackgroundColor();
private:
ModelViewWidget* m_modelView;
QLabel* m_statusBarLabel;

View File

@ -311,6 +311,14 @@ ModelViewWidget::setMaterial(unsigned int index, const cmod::Material& material)
update();
}
void
ModelViewWidget::setBackgroundColor(const QColor& color)
{
m_backgroundColor = color;
update();
}
void
ModelViewWidget::initializeGL()
{
@ -320,7 +328,7 @@ ModelViewWidget::initializeGL()
void
ModelViewWidget::paintGL()
{
glClearColor(0.0f, 0.0f, 0.5f, 0.0f);
glClearColor(m_backgroundColor.redF(), m_backgroundColor.greenF(), m_backgroundColor.blueF(), 0.0f);
glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

View File

@ -60,10 +60,18 @@ public:
return m_renderStyle;
}
QColor backgroundColor() const
{
return m_backgroundColor;
}
Eigen::Transform3d cameraTransform() const;
void setMaterial(unsigned int index, const cmod::Material& material);
public slots:
void setBackgroundColor(const QColor& color);
protected:
void initializeGL();
void paintGL();
@ -86,6 +94,8 @@ private:
MaterialLibrary* m_materialLibrary;
QSet<cmod::Mesh::PrimitiveGroup*> m_selection;
QColor m_backgroundColor;
};
#endif // _CMODVIEW_MODEL_VIEW_WIDGET_H_