Skip to content

Commit

Permalink
improve obj export of mesh with colors
Browse files Browse the repository at this point in the history
  • Loading branch information
wwmayer committed Sep 6, 2016
1 parent cc167bd commit 6313528
Show file tree
Hide file tree
Showing 6 changed files with 241 additions and 63 deletions.
243 changes: 188 additions & 55 deletions src/Mod/Mesh/App/Core/MeshIO.cpp
Expand Up @@ -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)
Expand Down Expand Up @@ -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
{
Expand All @@ -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);
Expand Down Expand Up @@ -1856,31 +1885,42 @@ 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) {
if (_material->diffuseColor.empty()) {
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);

Expand All @@ -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];
Expand All @@ -1917,30 +1957,123 @@ 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
}
}
}
}

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
{
Expand Down
5 changes: 5 additions & 0 deletions src/Mod/Mesh/App/Core/MeshIO.h
Expand Up @@ -66,6 +66,7 @@ struct MeshExport Material
{
Material() : binding(MeshIO::OVERALL) {}
MeshIO::Binding binding;
std::string library;
std::vector<App::Color> diffuseColor;
};

Expand Down Expand Up @@ -151,6 +152,8 @@ class MeshExport MeshOutput
* 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
Expand All @@ -162,6 +165,8 @@ class MeshExport MeshOutput
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. */
Expand Down
15 changes: 14 additions & 1 deletion src/Mod/Mesh/App/Mesh.cpp
Expand Up @@ -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,
Expand Down
11 changes: 4 additions & 7 deletions src/Mod/Mesh/Gui/Command.cpp
Expand Up @@ -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);
}
}
}

Expand Down

0 comments on commit 6313528

Please sign in to comment.