FreeCAD / src /Mod /Mesh /App /Core /IO /WriterOBJ.cpp
AbdulElahGwaith's picture
Upload folder using huggingface_hub
985c397 verified
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2022 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library 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 Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "Core/Iterator.h"
#include <Base/Console.h>
#include <Base/Sequencer.h>
#include <Base/Tools.h>
#include "WriterOBJ.h"
using namespace MeshCore;
struct WriterOBJ::Color_Less
{
bool operator()(const Base::Color& x, const Base::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
}
};
WriterOBJ::WriterOBJ(const MeshKernel& kernel, const Material* material)
: _kernel(kernel)
, _material(material)
{}
void WriterOBJ::SetGroups(const std::vector<Group>& g)
{
_groups = g;
}
void WriterOBJ::SetTransform(const Base::Matrix4D& mat)
{
_transform = mat;
if (mat != Base::Matrix4D()) {
apply_transform = true;
}
}
bool WriterOBJ::Save(std::ostream& out)
{
const MeshPointArray& rPoints = _kernel.GetPoints();
const MeshFacetArray& rFacets = _kernel.GetFacets();
if (!out || out.bad()) {
return false;
}
Base::SequencerLauncher seq("saving...", _kernel.CountPoints() + _kernel.CountFacets());
bool exportColorPerVertex = false;
bool exportColorPerFace = false;
if (_material) {
if (_material->binding == MeshIO::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 {
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 {
exportColorPerVertex = true;
}
}
}
// Header
out << "# Created by FreeCAD <https://www.freecad.org>\n";
if (exportColorPerFace) {
out << "mtllib " << _material->library << '\n';
}
out.precision(6);
out.setf(std::ios::fixed | std::ios::showpoint);
// vertices
Base::Vector3f pt;
std::size_t index = 0;
for (auto it = rPoints.begin(); it != rPoints.end(); ++it, ++index) {
if (this->apply_transform) {
pt = this->_transform * *it;
}
else {
pt.Set(it->x, it->y, it->z);
}
if (exportColorPerVertex) {
Base::Color c;
if (_material->binding == MeshIO::PER_VERTEX) {
c = _material->diffuseColor[index];
}
else {
c = _material->diffuseColor.front();
}
int r = static_cast<int>(c.r * 255.0F);
int g = static_cast<int>(c.g * 255.0F);
int b = static_cast<int>(c.b * 255.0F);
out << "v " << pt.x << " " << pt.y << " " << pt.z << " " << r << " " << g << " " << b
<< '\n';
}
else {
out << "v " << pt.x << " " << pt.y << " " << pt.z << '\n';
}
seq.next(true); // allow one to cancel
}
// Export normals
MeshFacetIterator clIter(_kernel), clEnd(_kernel);
const MeshGeomFacet* pclFacet {};
clIter.Begin();
clEnd.End();
while (clIter < clEnd) {
pclFacet = &(*clIter);
out << "vn " << pclFacet->GetNormal().x << " " << pclFacet->GetNormal().y << " "
<< pclFacet->GetNormal().z << '\n';
++clIter;
seq.next(true); // allow one to cancel
}
if (_groups.empty()) {
if (exportColorPerFace) {
// facet indices (no texture and normal indices)
// make sure to use the 'usemtl' statement as less often as possible
std::vector<Base::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;
Base::Color prev;
int faceIdx = 1;
const std::vector<Base::Color>& Kd = _material->diffuseColor;
for (auto it = rFacets.begin(); it != rFacets.end(); ++it, index++) {
if (index == 0 || prev != Kd[index]) {
prev = Kd[index];
if (auto c_it = std::ranges::find(colors, prev); c_it != colors.end()) {
out << "usemtl material_" << (c_it - colors.begin()) << '\n';
}
}
out << "f " << it->_aulPoints[0] + 1 << "//" << faceIdx << " "
<< it->_aulPoints[1] + 1 << "//" << faceIdx << " " << it->_aulPoints[2] + 1
<< "//" << faceIdx << '\n';
seq.next(true); // allow one to cancel
faceIdx++;
}
}
else {
// facet indices (no texture and normal indices)
std::size_t faceIdx = 1;
for (const auto& it : rFacets) {
out << "f " << it._aulPoints[0] + 1 << "//" << faceIdx << " " << it._aulPoints[1] + 1
<< "//" << faceIdx << " " << it._aulPoints[2] + 1 << "//" << faceIdx << '\n';
seq.next(true); // allow one to cancel
faceIdx++;
}
}
}
else {
if (exportColorPerFace) {
// make sure to use the 'usemtl' statement as less often as possible
std::vector<Base::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;
Base::Color prev;
const std::vector<Base::Color>& Kd = _material->diffuseColor;
for (const auto& gt : _groups) {
out << "g " << Base::Tools::escapedUnicodeFromUtf8(gt.name.c_str()) << '\n';
for (FacetIndex it : gt.indices) {
const MeshFacet& f = rFacets[it];
if (first || prev != Kd[it]) {
first = false;
prev = Kd[it];
if (auto c_it = std::ranges::find(colors, prev); c_it != colors.end()) {
out << "usemtl material_" << (c_it - colors.begin()) << '\n';
}
}
out << "f " << f._aulPoints[0] + 1 << "//" << it + 1 << " " << f._aulPoints[1] + 1
<< "//" << it + 1 << " " << f._aulPoints[2] + 1 << "//" << it + 1 << '\n';
seq.next(true); // allow one to cancel
}
}
}
else {
for (const auto& gt : _groups) {
out << "g " << Base::Tools::escapedUnicodeFromUtf8(gt.name.c_str()) << '\n';
for (FacetIndex it : gt.indices) {
const MeshFacet& f = rFacets[it];
out << "f " << f._aulPoints[0] + 1 << "//" << it + 1 << " " << f._aulPoints[1] + 1
<< "//" << it + 1 << " " << f._aulPoints[2] + 1 << "//" << it + 1 << '\n';
seq.next(true); // allow one to cancel
}
}
}
}
return true;
}
bool WriterOBJ::SaveMaterial(std::ostream& out)
{
if (!out || out.bad()) {
return false;
}
if (_material) {
if (_material->binding == MeshIO::PER_FACE) {
std::vector<Base::Color> Kd = _material->diffuseColor;
std::sort(Kd.begin(), Kd.end(), Color_Less());
Kd.erase(std::unique(Kd.begin(), Kd.end()), Kd.end());
out.precision(6);
out.setf(std::ios::fixed | std::ios::showpoint);
out << "# Created by FreeCAD <https://www.freecad.org>: 'None'\n";
out << "# Material Count: " << Kd.size() << '\n';
for (std::size_t i = 0; i < Kd.size(); i++) {
out << '\n';
out << "newmtl material_" << i << '\n';
out << " Ka 0.200000 0.200000 0.200000\n";
out << " Kd " << Kd[i].r << " " << Kd[i].g << " " << Kd[i].b << '\n';
out << " Ks 1.000000 1.000000 1.000000\n";
out << " d 1.000000" << '\n';
out << " illum 2" << '\n';
out << " Ns 0.000000" << '\n';
}
return true;
}
}
return false;
}