FreeCAD

Форк
0
/
WriterOBJ.cpp 
294 строки · 10.7 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2022 Werner Mayer <wmayer[at]users.sourceforge.net>     *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
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.      *
10
 *                                                                         *
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.                  *
15
 *                                                                         *
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                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23
#include "PreCompiled.h"
24

25
#include "Core/Iterator.h"
26
#include <Base/Console.h>
27
#include <Base/Sequencer.h>
28
#include <Base/Tools.h>
29

30
#include "WriterOBJ.h"
31

32

33
using namespace MeshCore;
34

35
struct WriterOBJ::Color_Less
36
{
37
    bool operator()(const App::Color& x, const App::Color& y) const
38
    {
39
        if (x.r != y.r) {
40
            return x.r < y.r;
41
        }
42
        if (x.g != y.g) {
43
            return x.g < y.g;
44
        }
45
        if (x.b != y.b) {
46
            return x.b < y.b;
47
        }
48
        return false;  // equal colors
49
    }
50
};
51

52
WriterOBJ::WriterOBJ(const MeshKernel& kernel, const Material* material)
53
    : _kernel(kernel)
54
    , _material(material)
55
{}
56

57
void WriterOBJ::SetGroups(const std::vector<Group>& g)
58
{
59
    _groups = g;
60
}
61

62
void WriterOBJ::SetTransform(const Base::Matrix4D& mat)
63
{
64
    _transform = mat;
65
    if (mat != Base::Matrix4D()) {
66
        apply_transform = true;
67
    }
68
}
69

70
bool WriterOBJ::Save(std::ostream& out)
71
{
72
    const MeshPointArray& rPoints = _kernel.GetPoints();
73
    const MeshFacetArray& rFacets = _kernel.GetFacets();
74

75
    if (!out || out.bad()) {
76
        return false;
77
    }
78

79
    Base::SequencerLauncher seq("saving...", _kernel.CountPoints() + _kernel.CountFacets());
80
    bool exportColorPerVertex = false;
81
    bool exportColorPerFace = false;
82

83
    if (_material) {
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");
88
            }
89
            else {
90
                exportColorPerFace = true;
91
            }
92
        }
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");
97
            }
98
            else {
99
                exportColorPerVertex = true;
100
            }
101
        }
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");
106
            }
107
            else {
108
                exportColorPerVertex = true;
109
            }
110
        }
111
    }
112

113
    // Header
114
    out << "# Created by FreeCAD <https://www.freecad.org>\n";
115
    if (exportColorPerFace) {
116
        out << "mtllib " << _material->library << '\n';
117
    }
118

119
    out.precision(6);
120
    out.setf(std::ios::fixed | std::ios::showpoint);
121

122
    // vertices
123
    Base::Vector3f pt;
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;
128
        }
129
        else {
130
            pt.Set(it->x, it->y, it->z);
131
        }
132

133
        if (exportColorPerVertex) {
134
            App::Color c;
135
            if (_material->binding == MeshIO::PER_VERTEX) {
136
                c = _material->diffuseColor[index];
137
            }
138
            else {
139
                c = _material->diffuseColor.front();
140
            }
141

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);
145

146
            out << "v " << pt.x << " " << pt.y << " " << pt.z << " " << r << " " << g << " " << b
147
                << '\n';
148
        }
149
        else {
150
            out << "v " << pt.x << " " << pt.y << " " << pt.z << '\n';
151
        }
152
        seq.next(true);  // allow to cancel
153
    }
154
    // Export normals
155
    MeshFacetIterator clIter(_kernel), clEnd(_kernel);
156
    const MeshGeomFacet* pclFacet {};
157

158
    clIter.Begin();
159
    clEnd.End();
160

161
    while (clIter < clEnd) {
162
        pclFacet = &(*clIter);
163
        out << "vn " << pclFacet->GetNormal().x << " " << pclFacet->GetNormal().y << " "
164
            << pclFacet->GetNormal().z << '\n';
165
        ++clIter;
166
        seq.next(true);  // allow to cancel
167
    }
168

169
    if (_groups.empty()) {
170
        if (exportColorPerFace) {
171
            // facet indices (no texture and normal indices)
172

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());
177

178
            std::size_t index = 0;
179
            App::Color prev;
180
            int faceIdx = 1;
181
            const std::vector<App::Color>& Kd = _material->diffuseColor;
182
            for (MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end();
183
                 ++it, index++) {
184
                if (index == 0 || prev != Kd[index]) {
185
                    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';
190
                    }
191
                }
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
196
                faceIdx++;
197
            }
198
        }
199
        else {
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
207
                faceIdx++;
208
            }
209
        }
210
    }
211
    else {
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());
217

218
            bool first = true;
219
            App::Color prev;
220
            const std::vector<App::Color>& Kd = _material->diffuseColor;
221

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]) {
227
                        first = false;
228
                        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';
233
                        }
234
                    }
235

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
240
                }
241
            }
242
        }
243
        else {
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
252
                }
253
            }
254
        }
255
    }
256

257
    return true;
258
}
259

260
bool WriterOBJ::SaveMaterial(std::ostream& out)
261
{
262
    if (!out || out.bad()) {
263
        return false;
264
    }
265

266
    if (_material) {
267
        if (_material->binding == MeshIO::PER_FACE) {
268

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());
272

273
            out.precision(6);
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';
277

278
            for (std::size_t i = 0; i < Kd.size(); i++) {
279
                out << '\n';
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';
287
            }
288

289
            return true;
290
        }
291
    }
292

293
    return false;
294
}
295

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.