improve obj export of mesh with colors

This commit is contained in:
wmayer 2016-09-06 17:00:34 +02:00
parent cc167bd2c8
commit 6313528886
6 changed files with 241 additions and 63 deletions

View File

@ -117,6 +117,26 @@ struct NODE {float x, y, z;};
struct TRIA {int iV[3];};
struct QUAD {int iV[4];};
namespace MeshCore {
struct Color_Less : public std::binary_function<const App::Color&,
const App::Color&, bool>
{
bool operator()(const App::Color& x,
const App::Color& y) const
{
if (x.r != y.r)
return x.r < y.r;
if (x.g != y.g)
return x.g < y.g;
if (x.b != y.b)
return x.b < y.b;
return false; // equal colors
}
};
}
// --------------------------------------------------------------
bool MeshInput::LoadAny(const char* FileName)
@ -1569,6 +1589,50 @@ void MeshOutput::Transform(const Base::Matrix4D& mat)
apply_transform = true;
}
MeshIO::Format MeshOutput::GetFormat(const char* FileName)
{
Base::FileInfo file(FileName);
if (file.hasExtension("bms")) {
return MeshIO::BMS;
}
else if (file.hasExtension("stl")) {
return MeshIO::BSTL;
}
else if (file.hasExtension("ast")) {
return MeshIO::ASTL;
}
else if (file.hasExtension("obj")) {
return MeshIO::OBJ;
}
else if (file.hasExtension("off")) {
return MeshIO::OFF;
}
else if (file.hasExtension("ply")) {
return MeshIO::PLY;
}
else if (file.hasExtension("iv")) {
return MeshIO::IV;
}
else if (file.hasExtension("x3d")) {
return MeshIO::X3D;
}
else if (file.hasExtension("py")) {
return MeshIO::PY;
}
else if (file.hasExtension("wrl") || file.hasExtension("vrml")) {
return MeshIO::VRML;
}
else if (file.hasExtension("wrz")) {
return MeshIO::WRZ;
}
else if (file.hasExtension("nas") || file.hasExtension("bdf")) {
return MeshIO::NAS;
}
else {
return MeshIO::Undefined;
}
}
/// Save in a file, format is decided by the extension if not explicitly given
bool MeshOutput::SaveAny(const char* FileName, MeshIO::Format format) const
{
@ -1580,42 +1644,7 @@ bool MeshOutput::SaveAny(const char* FileName, MeshIO::Format format) const
MeshIO::Format fileformat = format;
if (fileformat == MeshIO::Undefined) {
if (file.hasExtension("bms")) {
fileformat = MeshIO::BMS;
}
else if (file.hasExtension("stl")) {
fileformat = MeshIO::BSTL;
}
else if (file.hasExtension("ast")) {
fileformat = MeshIO::ASTL;
}
else if (file.hasExtension("obj")) {
fileformat = MeshIO::OBJ;
}
else if (file.hasExtension("off")) {
fileformat = MeshIO::OFF;
}
else if (file.hasExtension("ply")) {
fileformat = MeshIO::PLY;
}
else if (file.hasExtension("iv")) {
fileformat = MeshIO::IV;
}
else if (file.hasExtension("x3d")) {
fileformat = MeshIO::X3D;
}
else if (file.hasExtension("py")) {
fileformat = MeshIO::PY;
}
else if (file.hasExtension("wrl") || file.hasExtension("vrml")) {
fileformat = MeshIO::VRML;
}
else if (file.hasExtension("wrz")) {
fileformat = MeshIO::WRZ;
}
else if (file.hasExtension("nas") || file.hasExtension("bdf")) {
fileformat = MeshIO::NAS;
}
fileformat = GetFormat(FileName);
}
Base::ofstream str(file, std::ios::out | std::ios::binary);
@ -1856,17 +1885,24 @@ bool MeshOutput::SaveOBJ (std::ostream &out) const
return false;
Base::SequencerLauncher seq("saving...", _rclMesh.CountPoints() + _rclMesh.CountFacets());
bool exportColor = false;
bool exportColorPerVertex = false;
bool exportColorPerFace = false;
if (_material) {
if (_material->binding == MeshIO::PER_FACE) {
Base::Console().Warning("Cannot export color information because it's defined per face");
if (_material->diffuseColor.size() != rFacets.size()) {
Base::Console().Warning("Cannot export color information because there is a different number of faces and colors");
}
else {
exportColorPerFace = true;
}
}
else if (_material->binding == MeshIO::PER_VERTEX) {
if (_material->diffuseColor.size() != rPoints.size()) {
Base::Console().Warning("Cannot export color information because there is a different number of points and colors");
}
else {
exportColor = true;
exportColorPerVertex = true;
}
}
else if (_material->binding == MeshIO::OVERALL) {
@ -1874,13 +1910,17 @@ bool MeshOutput::SaveOBJ (std::ostream &out) const
Base::Console().Warning("Cannot export color information because there is no color defined");
}
else {
exportColor = true;
exportColorPerVertex = true;
}
}
}
// Header
out << "# Created by FreeCAD <http://www.freecadweb.org>" << std::endl;
if (exportColorPerFace) {
out << "mtllib " << _material->library << std::endl;
}
out.precision(6);
out.setf(std::ios::fixed | std::ios::showpoint);
@ -1895,7 +1935,7 @@ bool MeshOutput::SaveOBJ (std::ostream &out) const
pt.Set(it->x, it->y, it->z);
}
if (exportColor) {
if (exportColorPerVertex) {
App::Color c;
if (_material->binding == MeshIO::PER_VERTEX) {
c = _material->diffuseColor[index];
@ -1917,23 +1957,83 @@ bool MeshOutput::SaveOBJ (std::ostream &out) const
}
if (_groups.empty()) {
// facet indices (no texture and normal indices)
for (MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end(); ++it) {
out << "f " << it->_aulPoints[0]+1 << " "
<< it->_aulPoints[1]+1 << " "
<< it->_aulPoints[2]+1 << std::endl;
seq.next(true); // allow to cancel
if (exportColorPerFace) {
// facet indices (no texture and normal indices)
// make sure to use the 'usemtl' statement as less often as possible
std::vector<App::Color> colors = _material->diffuseColor;
std::sort(colors.begin(), colors.end(), Color_Less());
colors.erase(std::unique(colors.begin(), colors.end()), colors.end());
std::size_t index = 0;
App::Color prev;
const std::vector<App::Color>& Kd = _material->diffuseColor;
for (MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end(); ++it, index++) {
if (index == 0 || prev != Kd[index]) {
prev = Kd[index];
std::vector<App::Color>::iterator c_it = std::find(colors.begin(), colors.end(), prev);
if (c_it != colors.end()) {
out << "usemtl material_" << (c_it - colors.begin()) << std::endl;
}
}
out << "f " << it->_aulPoints[0]+1 << " "
<< it->_aulPoints[1]+1 << " "
<< it->_aulPoints[2]+1 << std::endl;
seq.next(true); // allow to cancel
}
}
else {
// facet indices (no texture and normal indices)
for (MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end(); ++it) {
out << "f " << it->_aulPoints[0]+1 << " "
<< it->_aulPoints[1]+1 << " "
<< it->_aulPoints[2]+1 << std::endl;
seq.next(true); // allow to cancel
}
}
}
else {
for (std::vector<Group>::const_iterator gt = _groups.begin(); gt != _groups.end(); ++gt) {
out << "g " << Base::Tools::escapedUnicodeFromUtf8(gt->name.c_str()) << std::endl;
for (std::vector<unsigned long>::const_iterator it = gt->indices.begin(); it != gt->indices.end(); ++it) {
const MeshFacet& f = rFacets[*it];
out << "f " << f._aulPoints[0]+1 << " "
<< f._aulPoints[1]+1 << " "
<< f._aulPoints[2]+1 << std::endl;
seq.next(true); // allow to cancel
if (exportColorPerFace) {
// make sure to use the 'usemtl' statement as less often as possible
std::vector<App::Color> colors = _material->diffuseColor;
std::sort(colors.begin(), colors.end(), Color_Less());
colors.erase(std::unique(colors.begin(), colors.end()), colors.end());
bool first = true;
App::Color prev;
const std::vector<App::Color>& Kd = _material->diffuseColor;
for (std::vector<Group>::const_iterator gt = _groups.begin(); gt != _groups.end(); ++gt) {
out << "g " << Base::Tools::escapedUnicodeFromUtf8(gt->name.c_str()) << std::endl;
for (std::vector<unsigned long>::const_iterator it = gt->indices.begin(); it != gt->indices.end(); ++it) {
const MeshFacet& f = rFacets[*it];
if (first || prev != Kd[*it]) {
first = false;
prev = Kd[*it];
std::vector<App::Color>::iterator c_it = std::find(colors.begin(), colors.end(), prev);
if (c_it != colors.end()) {
out << "usemtl material_" << (c_it - colors.begin()) << std::endl;
}
}
out << "f " << f._aulPoints[0]+1 << " "
<< f._aulPoints[1]+1 << " "
<< f._aulPoints[2]+1 << std::endl;
seq.next(true); // allow to cancel
}
}
}
else {
for (std::vector<Group>::const_iterator gt = _groups.begin(); gt != _groups.end(); ++gt) {
out << "g " << Base::Tools::escapedUnicodeFromUtf8(gt->name.c_str()) << std::endl;
for (std::vector<unsigned long>::const_iterator it = gt->indices.begin(); it != gt->indices.end(); ++it) {
const MeshFacet& f = rFacets[*it];
out << "f " << f._aulPoints[0]+1 << " "
<< f._aulPoints[1]+1 << " "
<< f._aulPoints[2]+1 << std::endl;
seq.next(true); // allow to cancel
}
}
}
}
@ -1941,6 +2041,39 @@ bool MeshOutput::SaveOBJ (std::ostream &out) const
return true;
}
bool MeshOutput::SaveMTL(std::ostream &out) const
{
if (!out || out.bad())
return false;
if (_material) {
if (_material->binding == MeshIO::PER_FACE) {
out.precision(6);
out.setf(std::ios::fixed | std::ios::showpoint);
out << "# Created by FreeCAD <http://www.freecadweb.org>: 'None'" << std::endl;
out << "# Material Count: " << _material->diffuseColor.size() << std::endl;
std::vector<App::Color> Kd = _material->diffuseColor;
std::sort(Kd.begin(), Kd.end(), Color_Less());
Kd.erase(std::unique(Kd.begin(), Kd.end()), Kd.end());
for (std::size_t i=0; i<Kd.size(); i++) {
out << std::endl;
out << "newmtl material_" << i << std::endl;
out << " Ns 10.000000" << std::endl;
out << " Ni 1.000000" << std::endl;
out << " d 1.000000" << std::endl;
out << " illum 2" << std::endl;
out << " Kd " << Kd[i].r << " " << Kd[i].g << " " << Kd[i].b << std::endl;
}
return true;
}
}
return false;
}
/** Saves an OFF file. */
bool MeshOutput::SaveOFF (std::ostream &out) const
{

View File

@ -66,6 +66,7 @@ struct MeshExport Material
{
Material() : binding(MeshIO::OVERALL) {}
MeshIO::Binding binding;
std::string library;
std::vector<App::Color> diffuseColor;
};
@ -151,6 +152,8 @@ public:
* automatically filled up with spaces.
*/
static void SetSTLHeaderData(const std::string&);
/// Determine the mesh format by file extension
static MeshIO::Format GetFormat(const char* FileName);
/// Saves the file, decided by extension if not explicitly given
bool SaveAny(const char* FileName, MeshIO::Format f=MeshIO::Undefined) const;
/// Saves to a stream and the given format
@ -162,6 +165,8 @@ public:
bool SaveBinarySTL (std::ostream &rstrOut) const;
/** Saves the mesh object into an OBJ file. */
bool SaveOBJ (std::ostream &rstrOut) const;
/** Saves the materials of an OBJ file. */
bool SaveMTL(std::ostream &rstrOut) const;
/** Saves the mesh object into an OFF file. */
bool SaveOFF (std::ostream &rstrOut) const;
/** Saves the mesh object into a binary PLY file. */

View File

@ -354,9 +354,22 @@ void MeshObject::save(const char* file, MeshCore::MeshIO::Format f,
}
}
aWriter.SetGroups(groups);
if (mat && mat->library.empty()) {
Base::FileInfo fi(file);
const_cast<MeshCore::Material*>(mat)->library = fi.fileNamePure() + ".mtl";
}
aWriter.Transform(this->_Mtrx);
aWriter.SaveAny(file, f);
if (aWriter.SaveAny(file, f)) {
if (mat && f == MeshCore::MeshIO::OBJ) {
Base::FileInfo fi(file);
std::string fn = fi.dirPath() + "/" + mat->library;
fi.setFile(fn);
Base::ofstream str(fi, std::ios::out | std::ios::binary);
aWriter.SaveMTL(str);
str.close();
}
}
}
void MeshObject::save(std::ostream& str, MeshCore::MeshIO::Format f,

View File

@ -476,13 +476,10 @@ void CmdMeshExport::activated(int iMsg)
}
}
//openCommand("Export Mesh");
doCommand(Doc,"FreeCAD.ActiveDocument.getObject(\"%s\").Mesh.write(\"%s\",\"%s\",\"%s\")",
docObj->getNameInDocument(),
(const char*)fn.toUtf8(),
(const char*)extension,
docObj->Label.getValue());
//commitCommand();
MeshGui::ViewProviderMesh* vp = dynamic_cast<MeshGui::ViewProviderMesh*>(Gui::Application::Instance->getViewProvider(docObj));
if (vp) {
vp->exportMesh((const char*)fn.toUtf8(), (const char*)extension);
}
}
}

View File

@ -665,6 +665,35 @@ bool ViewProviderMesh::exportToVrml(const char* filename, const MeshCore::Materi
return false;
}
void ViewProviderMesh::exportMesh(const char* filename, const char* fmt) const
{
MeshCore::MeshIO::Format format = MeshCore::MeshIO::Undefined;
if (fmt) {
std::string dummy = "meshfile.";
dummy += fmt;
format = MeshCore::MeshOutput::GetFormat(dummy.c_str());
}
MeshCore::Material mat;
int numColors = pcShapeMaterial->diffuseColor.getNum();
const SbColor* colors = pcShapeMaterial->diffuseColor.getValues(0);
mat.diffuseColor.reserve(numColors);
for (int i=0; i<numColors; i++) {
const SbColor& c = colors[i];
mat.diffuseColor.push_back(App::Color(c[0], c[1], c[2]));
}
const Mesh::MeshObject& mesh = static_cast<Mesh::Feature*>(getObject())->Mesh.getValue();
if (mat.diffuseColor.size() == mesh.countPoints())
mat.binding = MeshCore::MeshIO::PER_VERTEX;
else if (mat.diffuseColor.size() == mesh.countFacets())
mat.binding = MeshCore::MeshIO::PER_FACE;
else
mat.binding = MeshCore::MeshIO::OVERALL;
mesh.save(filename, format, &mat, getObject()->Label.getValue());
}
void ViewProviderMesh::setupContextMenu(QMenu* menu, QObject* receiver, const char* member)
{
ViewProviderGeometryObject::setupContextMenu(menu, receiver, member);

View File

@ -133,6 +133,7 @@ public:
/// returns a list of all possible modes
virtual std::vector<std::string> getDisplayModes(void) const;
bool exportToVrml(const char* filename, const MeshCore::Material&, bool binary=false) const;
void exportMesh(const char* filename, const char* fmt=0) const;
void setupContextMenu(QMenu*, QObject*, const char*);
/** @name Editing */