440 lines
16 KiB
C++

/****************************************************************************
* MeshLab o o *
* A versatile mesh processing toolbox o o *
* _ O _ *
* Copyright(C) 2005 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* 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. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log$
Revision 1.5 2008/02/15 08:27:44 cignoni
- '>> 'changed into '> >'
- Used HasPerFaceSomething(M) instead of M.HasPerFaceSomething() that is deprecated.
- many unsigned warning removed
- added const to some functions parameters (FindDEF, FindAndReplaceUSE ...)
Revision 1.4 2008/02/15 01:09:06 gianpaolopalma
Added control to check if face is deleted
Revision 1.3 2008/02/14 13:09:53 gianpaolopalma
Code refactoring to reduce time to save the mesh in X3D files
Revision 1.2 2008/02/13 15:25:08 gianpaolopalma
First working version
Revision 1.1 2008/02/02 13:52:54 gianpaolopalma
Defined X3D exporter interface
*****************************************************************************/
#ifndef EXPORTERX3D
#define EXPORTERX3D
#include<util_x3d.h>
namespace vcg {
namespace tri {
namespace io {
template<typename SaveMeshType>
class ExporterX3D : public UtilX3D
{
private:
typedef vcg::Point3f x3dPointType;
typedef vcg::Color4b x3dColorType;
typedef vcg::TexCoord2<> x3dTexCoordType;
/*Generate the following xml code:
<X3D profile='Immersive' version='3.1' xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation='http://www.web3d.org/specifications/x3d-3.1.xsd'>
<head>
<meta content='*enter filename' name='title'/>
<meta content='Generated from Meshlab X3D Exported' name='description'/>
<meta content='*enter date' name='created'/>
<meta content='Meshlab X3D Exported, http://meshlab.sourceforge.net' name='generator'/>
</head>
<Scene>
...
</Scene>
</X3D>
*/
inline static QDomElement createHeader(QDomDocument& doc, const char * filename)
{
QDomProcessingInstruction proc = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\"");
doc.appendChild(proc);
QString metaName[] = {"title", "description", "created", "generator"};
QString metaContent[] = {QFileInfo(filename).fileName(), "Generated from Meshlab X3D Exported", QDate::currentDate().toString("d MMMM yyyy"), "Meshlab X3D Exported, http://meshlab.sourceforge.net"};
QDomElement root = doc.createElement("X3D");
root.setAttribute("profile", "Immersive");
root.setAttribute("version", "3.1");
root.setAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema-instance");
root.setAttribute("xsd:noNamespaceSchemaLocation", "http://www.web3d.org/specifications/x3d-3.1.xsd");
doc.appendChild(root);
QDomElement head = doc.createElement("head");
for (int i = 0; i < 4; i++)
{
QDomElement meta = doc.createElement("meta");
meta.setAttribute("name", metaName[i]);
meta.setAttribute("content", metaContent[i]);
head.appendChild(meta);
}
root.appendChild(head);
QDomElement scene = doc.createElement("Scene");
root.appendChild(scene);
return scene;
}
/*If separator is true create the following string:
list[0] + " " + list[1] + " " + list[2] + " -1 " + ...+ " -1 " +list[i] + " " + list[i+1] + " " + list[i+2] + " -1"
othewise:
list[0] + " " + list[1] + " " + ... + list[i]
*/
inline static void getString(const std::vector<QString>& list, QString& ret, bool separator = true)
{
if (list.size() > 0)
{
ret.reserve(list.size() * (list[0].length() + 2));
ret.append(list[0]);
for (size_t i = 1; i < list.size(); i++)
{
ret.append(" " + list[i]);
if (separator && ((i + 1) % 3) == 0)
ret.append(" " + QString::number(-1));
}
ret.squeeze();
}
}
//Create the following string from a point: p[0] + " " + p[1] + " " + p[2]
inline static QString pointToString(const x3dPointType& p)
{
QString str;
for (int j = 0; j < 3; j++)
str.append(QString::number(p[j]) + " ");
str.remove(str.size()-1, 1);
return str;
}
//Create the following string from a color: c[0] + " " + c[1] + " " + c[2] + " " + c[3]
inline static QString colorToString(const x3dColorType& c)
{
QString str;
vcg::Color4f color;
color.Import(c);
for (int j = 0; j < 4; j++)
str.append(QString::number(color[j]) + " ");
str.remove(str.size()-1, 1);
return str;
}
//Create the following string: t.U()+ " " + t.V()
inline static QString texCoordToString(const x3dTexCoordType& t)
{
QString str;
str.append(QString::number(t.U()) + " ");
str.append(QString::number(t.V()));
return str;
}
public:
static int Save(SaveMeshType& m, const char * filename, const int mask, CallBackPos *cb=0)
{
QFile file(filename);
if (!file.open(QIODevice::WriteOnly))
return E_CANTOPEN;
int nFace = 0;
bool bHasPerWedgeTexCoord = (mask & MeshModel::IOM_WEDGTEXCOORD) && HasPerWedgeTexCoord(m);
bool bHasPerWedgeNormal = (mask & MeshModel::IOM_WEDGNORMAL) && HasPerWedgeNormal(m);
bool bHasPerWedgeColor = (mask & MeshModel::IOM_WEDGCOLOR) && HasPerWedgeColor(m);
bool bHasPerVertexColor = (mask & MeshModel::IOM_VERTCOLOR) && m.HasPerVertexColor();
bool bHasPerVertexNormal = (mask & MeshModel::IOM_VERTNORMAL) && m.HasPerVertexNormal();
bool bHasPerVertexTexCoord = (mask & MeshModel::IOM_VERTTEXCOORD) && m.HasPerVertexTexCoord();
bool bHasPerFaceColor = (mask & MeshModel::IOM_FACECOLOR) && HasPerFaceColor(m);
bool bHasPerFaceNormal = (mask & MeshModel::IOM_FACENORMAL) && HasPerFaceNormal(m);
if (bHasPerWedgeTexCoord && bHasPerVertexTexCoord)
return E_INVALIDSAVETEXCOORD;
if ((bHasPerVertexColor && (bHasPerWedgeColor || bHasPerFaceColor)) || (bHasPerWedgeColor && bHasPerFaceColor))
return E_INVALIDSAVECOLOR;
if ((bHasPerVertexNormal && (bHasPerWedgeNormal || bHasPerFaceNormal)) || (bHasPerWedgeNormal && bHasPerFaceNormal))
return E_INVALIDSAVENORMAL;
if (m.vert.size() == 0)
return E_NOGEOMETRY;
QDomDocument doc("X3D PUBLIC \"ISO//Web3D//DTD X3D 3.1//EN\" \"http://www.web3d.org/specifications/x3d-3.1.dtd\"");
QDomElement scene = createHeader(doc, filename);
int indexTexture = -2;
std::vector<typename SaveMeshType::FaceIterator> object;
typename SaveMeshType::FaceIterator fi;
if (bHasPerWedgeTexCoord || bHasPerVertexTexCoord)
{
//Search objects in the mesh(an object is a portion of mesh with the same texture index)
for (fi = m.face.begin(); fi != m.face.end(); fi++)
{
if (!fi->IsD())
{
int n;
if (bHasPerVertexTexCoord)
n = fi->cV(0)->T().N();
else if (bHasPerWedgeTexCoord)
n = fi->cWT(0).N();
if (n != indexTexture)
{
indexTexture = n;
object.push_back(fi);
}
}
}
}
else
object.push_back(m.face.begin());
object.push_back(m.face.end());
std::set<typename SaveMeshType::VertexType::CoordType> vertexSet;
if (cb !=NULL) (*cb)(10, "Saving X3D File...");
//Create a Shape element for each object
for (size_t i = 0; i < object.size() - 1; i++)
{
std::vector<QString> vertexIndex, colorIndex, normalIndex, textureCoordIndex;
std::vector<QString> vertexValue, colorValue, normalValue, textureCoordValue;
std::map<x3dPointType, int> vertexMap, normalMap;
std::map<x3dColorType, int> colorMap;
std::map<x3dTexCoordType, int> texCoordMap;
QDomElement shape = doc.createElement("Shape");
int indexTexture = -1;
if (bHasPerVertexTexCoord)
indexTexture = object[i]->cV(0)->T().N();
else if (bHasPerWedgeTexCoord)
indexTexture = object[i]->cWT(0).N();
if (indexTexture > -1 && indexTexture < int( m.textures.size()))
{
QDomElement appearance = doc.createElement("Appearance");
QDomElement imageTexture = doc.createElement("ImageTexture");
imageTexture.setAttribute("url", m.textures[indexTexture].c_str());
appearance.appendChild(imageTexture);
shape.appendChild(appearance);
}
QDomElement geometry = doc.createElement("IndexedFaceSet");
geometry.setAttribute("solid", "false");
int indexVertex = 0;
int indexColor = 0;
int indexNormal = 0;
int indexTexCoord = 0;
for (fi = object[i]; fi != object[i+1]; fi++)
{
nFace++;
if(!fi->IsD())
{
for (int tt = 0; tt < 3; tt++)
{
bool newVertex = false;
std::map<x3dPointType, int>::iterator pointIter = vertexMap.find(fi->P(tt));
if (pointIter == vertexMap.end())
{
vertexIndex.push_back(QString::number(indexVertex));
vertexValue.push_back(pointToString(fi->P(tt)));
vertexMap[fi->P(tt)] = indexVertex++;
vertexSet.insert(fi->P(tt));
newVertex = true;
}
else
vertexIndex.push_back(QString::number(pointIter->second));
if (bHasPerWedgeColor)
{
std::map<x3dColorType, int>::iterator colorIter = colorMap.find(fi->WC(tt));
if (colorIter == colorMap.end())
{
colorIndex.push_back(QString::number(indexColor));
colorValue.push_back(colorToString(fi->WC(tt)));
colorMap[fi->WC(tt)] = indexColor++;
}
else
colorIndex.push_back(QString::number(colorIter->second));
}
if (bHasPerWedgeNormal)
{
std::map<x3dPointType, int>::iterator normalIter = normalMap.find(fi->WN(tt));
if (normalIter == normalMap.end())
{
normalIndex.push_back(QString::number(indexNormal));
normalValue.push_back(pointToString(fi->WN(tt)));
normalMap[fi->WN(tt)] = indexNormal++;
}
else
normalIndex.push_back(QString::number(normalIter->second));
}
if (bHasPerWedgeTexCoord)
{
std::map<x3dTexCoordType, int>::iterator texIter = texCoordMap.find(fi->WT(tt));
if (texIter == texCoordMap.end())
{
textureCoordIndex.push_back(QString::number(indexTexCoord));
textureCoordValue.push_back(texCoordToString(fi->WT(tt)));
texCoordMap[fi->WT(tt)] = indexTexCoord++;
}
else
textureCoordIndex.push_back(QString::number(texIter->second));
}
if (newVertex)
{
if (bHasPerVertexColor)
colorValue.push_back(colorToString(fi->cV(tt)->C()));
if (bHasPerVertexNormal)
normalValue.push_back(pointToString(fi->cV(tt)->cN()));
if (bHasPerVertexTexCoord)
textureCoordValue.push_back(texCoordToString(fi->cV(tt)->T()));
}
}
if (bHasPerFaceColor)
colorValue.push_back(colorToString(fi->C()));
if (bHasPerFaceNormal)
normalValue.push_back(pointToString(fi->N()));
if (cb !=NULL && nFace%1000 == 0) (*cb)(10 + 60*nFace/m.face.size(), "Saving X3D File...");
}
}
QString vertIndexStr, vertCoordStr;
getString(vertexIndex, vertIndexStr);
getString(vertexValue, vertCoordStr, false);
QDomElement coordinate = doc.createElement("Coordinate");
coordinate.setAttribute("point", vertCoordStr);
geometry.appendChild(coordinate);
geometry.setAttribute("coordIndex", vertIndexStr);
if (bHasPerWedgeColor || bHasPerVertexColor || bHasPerFaceColor)
{
QString colorValueStr;
getString(colorValue, colorValueStr, false);
QDomElement color = doc.createElement("ColorRGBA");
color.setAttribute("color", colorValueStr);
if (bHasPerFaceColor)
geometry.setAttribute("colorPerVertex", "false");
if (bHasPerWedgeColor)
{
QString colorIndexStr;
getString(colorIndex, colorIndexStr);
geometry.setAttribute("colorIndex", colorIndexStr);
}
geometry.appendChild(color);
}
if (bHasPerWedgeNormal || bHasPerVertexNormal || bHasPerFaceNormal)
{
QString normalValueStr;
getString(normalValue, normalValueStr, false);
QDomElement normal = doc.createElement("Normal");
normal.setAttribute("vector", normalValueStr);
if (bHasPerFaceNormal)
geometry.setAttribute("normalPerVertex", "false");
if (bHasPerWedgeNormal)
{
QString normalIndexStr;
getString(normalIndex, normalIndexStr);
geometry.setAttribute("normalIndex", normalIndexStr);
}
geometry.appendChild(normal);
}
if (indexTexture != -1 && (bHasPerWedgeTexCoord || bHasPerVertexTexCoord))
{
QString texCoordValueStr;
getString(textureCoordValue, texCoordValueStr, false);
QDomElement textureCoord = doc.createElement("TextureCoordinate");
textureCoord.setAttribute("point", texCoordValueStr);
if (bHasPerWedgeTexCoord)
{
QString texCoordIndexStr;
getString(textureCoordIndex, texCoordIndexStr);
geometry.setAttribute("texCoordIndex", texCoordIndexStr);
}
geometry.appendChild(textureCoord);
}
shape.appendChild(geometry);
scene.appendChild(shape);
}
typename SaveMeshType::VertexIterator vi;
std::vector<QString> pointVect;
std::vector<QString> colorVect;
//Create a PoinrSet element for all isolated vertex
for (vi = m.vert.begin(); vi != m.vert.end(); vi++)
{
if (!vi->IsD() && vertexSet.find(vi->P()) == vertexSet.end())
{
pointVect.push_back(pointToString(vi->P()));
if (bHasPerVertexColor)
colorVect.push_back(colorToString(vi->C()));
}
if (cb !=NULL && ((vi - m.vert.begin())%1000 == 0)) (*cb)(70 + 25*(vi - m.vert.begin())/m.vert.size(), "Saving X3D File...");
}
if (pointVect.size() != 0)
{
QDomElement shape = doc.createElement("Shape");
QDomElement pointSet = doc.createElement("PointSet");
QDomElement coord = doc.createElement("Coordinate");
QString coordValueStr;
getString(pointVect, coordValueStr, false);
coord.setAttribute("point", coordValueStr);
pointSet.appendChild(coord);
if (colorVect.size() != 0)
{
QDomElement color = doc.createElement("ColorRGBA");
QString colorValueStr;
getString(colorVect, colorValueStr, false);
color.setAttribute("color", colorValueStr);
pointSet.appendChild(color);
}
shape.appendChild(pointSet);
scene.appendChild(shape);
}
//Print the file
QTextStream out(&file);
out << doc.toString();
file.close();
return E_NOERROR;
}
static int GetExportMaskCapability()
{
int capability = 0;
//vert
capability |= MeshModel::IOM_VERTNORMAL;
capability |= MeshModel::IOM_VERTTEXCOORD;
capability |= MeshModel::IOM_VERTCOLOR;
//face
capability |= MeshModel::IOM_FACECOLOR;
capability |= MeshModel::IOM_FACENORMAL;
//wedg
capability |= MeshModel::IOM_WEDGTEXCOORD;
capability |= MeshModel::IOM_WEDGNORMAL;
capability |= MeshModel::IOM_WEDGCOLOR;
return capability;
}
};
}
}
}
#endif