1
/***************************************************************************
2
* Copyright (c) 2022 Werner Mayer <wmayer[at]users.sourceforge.net> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
25
#include "Core/Iterator.h"
26
#include <Base/Console.h>
27
#include <Base/Sequencer.h>
28
#include <Base/Tools.h>
33
using namespace MeshCore;
35
struct WriterOBJ::Color_Less
37
bool operator()(const App::Color& x, const App::Color& y) const
48
return false; // equal colors
52
WriterOBJ::WriterOBJ(const MeshKernel& kernel, const Material* material)
57
void WriterOBJ::SetGroups(const std::vector<Group>& g)
62
void WriterOBJ::SetTransform(const Base::Matrix4D& mat)
65
if (mat != Base::Matrix4D()) {
66
apply_transform = true;
70
bool WriterOBJ::Save(std::ostream& out)
72
const MeshPointArray& rPoints = _kernel.GetPoints();
73
const MeshFacetArray& rFacets = _kernel.GetFacets();
75
if (!out || out.bad()) {
79
Base::SequencerLauncher seq("saving...", _kernel.CountPoints() + _kernel.CountFacets());
80
bool exportColorPerVertex = false;
81
bool exportColorPerFace = false;
84
if (_material->binding == MeshIO::PER_FACE) {
85
if (_material->diffuseColor.size() != rFacets.size()) {
86
Base::Console().Warning("Cannot export color information because there is a "
87
"different number of faces and colors");
90
exportColorPerFace = true;
93
else if (_material->binding == MeshIO::PER_VERTEX) {
94
if (_material->diffuseColor.size() != rPoints.size()) {
95
Base::Console().Warning("Cannot export color information because there is a "
96
"different number of points and colors");
99
exportColorPerVertex = true;
102
else if (_material->binding == MeshIO::OVERALL) {
103
if (_material->diffuseColor.empty()) {
104
Base::Console().Warning(
105
"Cannot export color information because there is no color defined");
108
exportColorPerVertex = true;
114
out << "# Created by FreeCAD <https://www.freecad.org>\n";
115
if (exportColorPerFace) {
116
out << "mtllib " << _material->library << '\n';
120
out.setf(std::ios::fixed | std::ios::showpoint);
124
std::size_t index = 0;
125
for (MeshPointArray::_TConstIterator it = rPoints.begin(); it != rPoints.end(); ++it, ++index) {
126
if (this->apply_transform) {
127
pt = this->_transform * *it;
130
pt.Set(it->x, it->y, it->z);
133
if (exportColorPerVertex) {
135
if (_material->binding == MeshIO::PER_VERTEX) {
136
c = _material->diffuseColor[index];
139
c = _material->diffuseColor.front();
142
int r = static_cast<int>(c.r * 255.0f);
143
int g = static_cast<int>(c.g * 255.0f);
144
int b = static_cast<int>(c.b * 255.0f);
146
out << "v " << pt.x << " " << pt.y << " " << pt.z << " " << r << " " << g << " " << b
150
out << "v " << pt.x << " " << pt.y << " " << pt.z << '\n';
152
seq.next(true); // allow to cancel
155
MeshFacetIterator clIter(_kernel), clEnd(_kernel);
156
const MeshGeomFacet* pclFacet {};
161
while (clIter < clEnd) {
162
pclFacet = &(*clIter);
163
out << "vn " << pclFacet->GetNormal().x << " " << pclFacet->GetNormal().y << " "
164
<< pclFacet->GetNormal().z << '\n';
166
seq.next(true); // allow to cancel
169
if (_groups.empty()) {
170
if (exportColorPerFace) {
171
// facet indices (no texture and normal indices)
173
// make sure to use the 'usemtl' statement as less often as possible
174
std::vector<App::Color> colors = _material->diffuseColor;
175
std::sort(colors.begin(), colors.end(), Color_Less());
176
colors.erase(std::unique(colors.begin(), colors.end()), colors.end());
178
std::size_t index = 0;
181
const std::vector<App::Color>& Kd = _material->diffuseColor;
182
for (MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end();
184
if (index == 0 || prev != Kd[index]) {
186
std::vector<App::Color>::iterator c_it =
187
std::find(colors.begin(), colors.end(), prev);
188
if (c_it != colors.end()) {
189
out << "usemtl material_" << (c_it - colors.begin()) << '\n';
192
out << "f " << it->_aulPoints[0] + 1 << "//" << faceIdx << " "
193
<< it->_aulPoints[1] + 1 << "//" << faceIdx << " " << it->_aulPoints[2] + 1
194
<< "//" << faceIdx << '\n';
195
seq.next(true); // allow to cancel
200
// facet indices (no texture and normal indices)
201
std::size_t faceIdx = 1;
202
for (const auto& it : rFacets) {
203
out << "f " << it._aulPoints[0] + 1 << "//" << faceIdx << " "
204
<< it._aulPoints[1] + 1 << "//" << faceIdx << " " << it._aulPoints[2] + 1
205
<< "//" << faceIdx << '\n';
206
seq.next(true); // allow to cancel
212
if (exportColorPerFace) {
213
// make sure to use the 'usemtl' statement as less often as possible
214
std::vector<App::Color> colors = _material->diffuseColor;
215
std::sort(colors.begin(), colors.end(), Color_Less());
216
colors.erase(std::unique(colors.begin(), colors.end()), colors.end());
220
const std::vector<App::Color>& Kd = _material->diffuseColor;
222
for (const auto& gt : _groups) {
223
out << "g " << Base::Tools::escapedUnicodeFromUtf8(gt.name.c_str()) << '\n';
224
for (FacetIndex it : gt.indices) {
225
const MeshFacet& f = rFacets[it];
226
if (first || prev != Kd[it]) {
229
std::vector<App::Color>::iterator c_it =
230
std::find(colors.begin(), colors.end(), prev);
231
if (c_it != colors.end()) {
232
out << "usemtl material_" << (c_it - colors.begin()) << '\n';
236
out << "f " << f._aulPoints[0] + 1 << "//" << it + 1 << " "
237
<< f._aulPoints[1] + 1 << "//" << it + 1 << " " << f._aulPoints[2] + 1
238
<< "//" << it + 1 << '\n';
239
seq.next(true); // allow to cancel
244
for (const auto& gt : _groups) {
245
out << "g " << Base::Tools::escapedUnicodeFromUtf8(gt.name.c_str()) << '\n';
246
for (FacetIndex it : gt.indices) {
247
const MeshFacet& f = rFacets[it];
248
out << "f " << f._aulPoints[0] + 1 << "//" << it + 1 << " "
249
<< f._aulPoints[1] + 1 << "//" << it + 1 << " " << f._aulPoints[2] + 1
250
<< "//" << it + 1 << '\n';
251
seq.next(true); // allow to cancel
260
bool WriterOBJ::SaveMaterial(std::ostream& out)
262
if (!out || out.bad()) {
267
if (_material->binding == MeshIO::PER_FACE) {
269
std::vector<App::Color> Kd = _material->diffuseColor;
270
std::sort(Kd.begin(), Kd.end(), Color_Less());
271
Kd.erase(std::unique(Kd.begin(), Kd.end()), Kd.end());
274
out.setf(std::ios::fixed | std::ios::showpoint);
275
out << "# Created by FreeCAD <https://www.freecad.org>: 'None'\n";
276
out << "# Material Count: " << Kd.size() << '\n';
278
for (std::size_t i = 0; i < Kd.size(); i++) {
280
out << "newmtl material_" << i << '\n';
281
out << " Ka 0.200000 0.200000 0.200000\n";
282
out << " Kd " << Kd[i].r << " " << Kd[i].g << " " << Kd[i].b << '\n';
283
out << " Ks 1.000000 1.000000 1.000000\n";
284
out << " d 1.000000" << '\n';
285
out << " illum 2" << '\n';
286
out << " Ns 0.000000" << '\n';