Panda3D

eggToObjConverter.cxx
Go to the documentation of this file.
1 /**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file eggToObjConverter.cxx
10 * @author drose
11 * @date 2012-12-19
12 */
13
14 #include "eggToObjConverter.h"
15 #include "config_objegg.h"
16 #include "config_egg.h"
17 #include "eggData.h"
18 #include "string_utils.h"
19 #include "streamReader.h"
20 #include "virtualFileSystem.h"
21 #include "eggPolygon.h"
22 #include "eggPoint.h"
23 #include "eggLine.h"
24 #include "dcast.h"
25
26 using std::ostream;
27 using std::string;
28
29 /**
30 *
31 */
32 EggToObjConverter::
33 EggToObjConverter() {
34 }
35
36 /**
37 *
38 */
39 EggToObjConverter::
40 EggToObjConverter(const EggToObjConverter &copy) :
41 EggToSomethingConverter(copy)
42 {
43 }
44
45 /**
46 *
47 */
48 EggToObjConverter::
49 ~EggToObjConverter() {
50 }
51
52 /**
53 * Allocates and returns a new copy of the converter.
54 */
55 EggToSomethingConverter *EggToObjConverter::
56 make_copy() {
57 return new EggToObjConverter(*this);
58 }
59
60
61 /**
62 * Returns the English name of the file type this converter supports.
63 */
64 string EggToObjConverter::
65 get_name() const {
66 return "obj";
67 }
68
69 /**
70 * Returns the common extension of the file type this converter supports.
71 */
72 string EggToObjConverter::
73 get_extension() const {
74 return "obj";
75 }
76
77 /**
78 * Returns true if this file type can transparently save compressed files
79 * (with a .pz extension), false otherwise.
80 */
81 bool EggToObjConverter::
82 supports_compressed() const {
83 return true;
84 }
85
86 /**
87 * Handles the conversion of the internal EggData to the target file format,
88 * written to the specified filename.
89 */
90 bool EggToObjConverter::
91 write_file(const Filename &filename) {
92 clear_error();
93
94 if (_egg_data->get_coordinate_system() == CS_default) {
95 _egg_data->set_coordinate_system(CS_zup_right);
96 }
97
98 if (!process(filename)) {
99 _error = true;
100 }
101 return !had_error();
102 }
103
104 /**
105 *
106 */
107 bool EggToObjConverter::
108 process(const Filename &filename) {
109 _egg_data->flatten_transforms();
110 collect_vertices(_egg_data);
111
112 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
113 Filename obj_filename = Filename::text_filename(filename);
114 vfs->delete_file(obj_filename);
115 ostream *file = vfs->open_write_file(obj_filename, true, true);
116 if (file == nullptr) {
117 return false;
118 }
119 if (egg_precision != 0) {
120 file->precision(egg_precision);
121 }
122
123 _current_group = nullptr;
124
125 /*
126 (*file) << "\n#\n"
127 << "# obj file generated by the following command:\n"
128 << "# " << get_exec_command() << "\n"
129 << "#\n\n";
130 */
131
132 write_vertices(*file, "v", 3, _unique_vert3);
133 write_vertices(*file, "v", 4, _unique_vert4);
134 write_vertices(*file, "vt", 2, _unique_uv2);
135 write_vertices(*file, "vt", 3, _unique_uv3);
136 write_vertices(*file, "vn", 3, _unique_norm);
137
138 write_faces(*file, _egg_data);
139
140 bool success = (file != nullptr);
141 vfs->close_write_file(file);
142
143 return success;
144 }
145
146 /**
147 * Recursively walks the egg structure, looking for vertices referenced by
148 * polygons or points. Any such vertices are added to the vertex tables for
149 * writing to the obj file.
150 */
151 void EggToObjConverter::
152 collect_vertices(EggNode *egg_node) {
153 if (egg_node->is_of_type(EggPrimitive::get_class_type())) {
154 EggPrimitive *egg_prim = DCAST(EggPrimitive, egg_node);
155 EggPrimitive::iterator pi;
156 for (pi = egg_prim->begin(); pi != egg_prim->end(); ++pi) {
157 record_vertex(*pi);
158 }
159
160 } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
161 EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node);
162
163 EggGroupNode::iterator ci;
164 for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
165 collect_vertices(*ci);
166 }
167 }
168 }
169
170 /**
171 * Recursively walks the egg structure again, this time writing out the face
172 * records for any polygons, points, or lines encountered.
173 */
174 void EggToObjConverter::
175 write_faces(ostream &out, EggNode *egg_node) {
176 if (egg_node->is_of_type(EggPrimitive::get_class_type())) {
177 const char *prim_type = nullptr;
178 if (egg_node->is_of_type(EggPolygon::get_class_type())) {
179 prim_type = "f";
180 } else if (egg_node->is_of_type(EggPoint::get_class_type())) {
181 prim_type = "p";
182 } else if (egg_node->is_of_type(EggLine::get_class_type())) {
183 prim_type = "l";
184 }
185
186 if (prim_type != nullptr) {
187 write_group_reference(out, egg_node);
188
189 EggPrimitive *egg_prim = DCAST(EggPrimitive, egg_node);
190
191 out << prim_type;
192 EggPrimitive::iterator pi;
193 for (pi = egg_prim->begin(); pi != egg_prim->end(); ++pi) {
194 VertexDef &vdef = _vmap[(*pi)];
195 int vert_index = -1;
196 int uv_index = -1;
197 int norm_index = -1;
198
199 if (vdef._vert3_index != -1) {
200 vert_index = vdef._vert3_index + 1;
201 } else if (vdef._vert4_index != -1) {
202 vert_index = vdef._vert4_index + 1 + (int)_unique_vert3.size();
203 }
204
205 if (vdef._uv2_index != -1) {
206 uv_index = vdef._uv2_index + 1;
207 } else if (vdef._uv3_index != -1) {
208 uv_index = vdef._uv3_index + 1 + (int)_unique_uv2.size();
209 }
210
211 if (vdef._norm_index != -1) {
212 norm_index = vdef._norm_index + 1;
213 }
214
215 if (vert_index == -1) {
216 continue;
217 }
218
219 if (norm_index != -1) {
220 if (uv_index != -1) {
221 out << " " << vert_index << "/" << uv_index << "/" << norm_index;
222 } else {
223 out << " " << vert_index << "//" << norm_index;
224 }
225 } else if (uv_index != -1) {
226 out << " " << vert_index << "/" << uv_index;
227 } else {
228 out << " " << vert_index;
229 }
230 }
231 out << "\n";
232 }
233 } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
234 EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node);
235
236 EggGroupNode::iterator ci;
237 for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
238 write_faces(out, *ci);
239 }
240 }
241 }
242
243 /**
244 * Writes the "g" tag to describe this polygon's group, if needed.
245 */
246 void EggToObjConverter::
247 write_group_reference(ostream &out, EggNode *egg_node) {
248 EggGroupNode *egg_group = egg_node->get_parent();
249 if (egg_group == _current_group) {
250 // Same group we wrote last time.
251 return;
252 }
253
254 string group_name;
255 get_group_name(group_name, egg_group);
256 if (group_name.empty()) {
257 out << "g default\n";
258 } else {
259 out << "g" << group_name << "\n";
260 }
261 _current_group = egg_group;
262 }
263
264 /**
265 * Recursively determines the appropriate string to write for the "g" tag to
266 * describe a particular EggGroupNode.
267 */
268 void EggToObjConverter::
269 get_group_name(string &group_name, EggGroupNode *egg_group) {
270 string name = trim(egg_group->get_name());
271 if (!name.empty()) {
272 group_name += ' ';
273
274 // Remove nonstandard characters.
275 for (string::const_iterator ni = name.begin(); ni != name.end(); ++ni) {
276 char c = (*ni);
277 if (!isalnum(c)) {
278 c = '_';
279 }
280 group_name += c;
281 }
282 }
283
284 // Now recurse.
285 EggGroupNode *egg_parent = egg_group->get_parent();
286 if (egg_parent != nullptr) {
287 get_group_name(group_name, egg_parent);
288 }
289 }
290
291 /**
292 * Adds the indicated EggVertex to the unique vertex tables, for writing later
293 * by write_vertices().
294 */
295 void EggToObjConverter::
296 record_vertex(EggVertex *vertex) {
297 VertexDef &vdef = _vmap[vertex];
298
299 switch (vertex->get_num_dimensions()) {
300 case 1:
301 vdef._vert3_index = record_unique(_unique_vert3, vertex->get_pos1());
302 break;
303 case 2:
304 vdef._vert3_index = record_unique(_unique_vert3, vertex->get_pos2());
305 break;
306 case 3:
307 vdef._vert3_index = record_unique(_unique_vert3, vertex->get_pos3());
308 break;
309 case 4:
310 vdef._vert4_index = record_unique(_unique_vert4, vertex->get_pos4());
311 break;
312 }
313
314 if (vertex->has_uv("")) {
315 vdef._uv2_index = record_unique(_unique_uv2, vertex->get_uv(""));
316 } else if (vertex->has_uvw("")) {
317 vdef._uv3_index = record_unique(_unique_uv3, vertex->get_uvw(""));
318 }
319
320 if (vertex->has_normal()) {
321 vdef._norm_index = record_unique(_unique_norm, vertex->get_normal());
322 }
323 }
324
325 /**
326 * Records the indicated vertex value, returning the shared index if this
327 * value already appears elsewhere in the table, or the new unique index if
328 * this is the first time this value appears.
329 */
330 int EggToObjConverter::
331 record_unique(UniqueVertices &unique, const LVecBase4d &vec) {
332 // We record a zero-based index. Note that we will actually write out a
333 // one-based index to the obj file, as required by the standard.
334 int index = unique.size();
335 UniqueVertices::iterator ui = unique.insert(UniqueVertices::value_type(vec, index)).first;
336 return (*ui).second;
337 }
338
339 /**
340 * Records the indicated vertex value, returning the shared index if this
341 * value already appears elsewhere in the table, or the new unique index if
342 * this is the first time this value appears.
343 */
344 int EggToObjConverter::
345 record_unique(UniqueVertices &unique, const LVecBase3d &vec) {
346 return record_unique(unique, LVecBase4d(vec[0], vec[1], vec[2], 0.0));
347 }
348
349 /**
350 * Records the indicated vertex value, returning the shared index if this
351 * value already appears elsewhere in the table, or the new unique index if
352 * this is the first time this value appears.
353 */
354 int EggToObjConverter::
355 record_unique(UniqueVertices &unique, const LVecBase2d &vec) {
356 return record_unique(unique, LVecBase4d(vec[0], vec[1], 0.0, 0.0));
357 }
358
359 /**
360 * Records the indicated vertex value, returning the shared index if this
361 * value already appears elsewhere in the table, or the new unique index if
362 * this is the first time this value appears.
363 */
364 int EggToObjConverter::
365 record_unique(UniqueVertices &unique, double pos) {
366 return record_unique(unique, LVecBase4d(pos, 0.0, 0.0, 0.0));
367 }
368
369 /**
370 * Actually writes the vertex values recorded in the indicated table to the
371 * obj output stream.
372 */
373 void EggToObjConverter::
374 write_vertices(ostream &out, const string &prefix, int num_components,
375 const UniqueVertices &unique) {
376 // First, sort the list into numeric order.
377 int num_vertices = (int)unique.size();
378 const LVecBase4d **vertices = (const LVecBase4d **)PANDA_MALLOC_ARRAY(num_vertices * sizeof(LVecBase4d *));
379 memset(vertices, 0, num_vertices * sizeof(LVecBase4d *));
380 UniqueVertices::const_iterator ui;
381 for (ui = unique.begin(); ui != unique.end(); ++ui) {
382 int index = (*ui).second;
383 const LVecBase4d &vec = (*ui).first;
384 nassertv(index >= 0 && index < num_vertices);
385 nassertv(vertices[index] == nullptr);
386 vertices[index] = &vec;
387 }
388
389 for (int i = 0; i < num_vertices; ++i) {
390 out << prefix;
391 const LVecBase4d &vec = *(vertices[i]);
392 for (int ci = 0; ci < num_components; ++ci) {
393 out << " " << vec[ci];
394 }
395 out << "\n";
396 }
397 PANDA_FREE_ARRAY(vertices);
398 }
399
400 /**
401 *
402 */
403 EggToObjConverter::VertexDef::
404 VertexDef() :
405 _vert3_index(-1),
406 _vert4_index(-1),
407 _uv2_index(-1),
408 _uv3_index(-1),
409 _norm_index(-1)
410 {
411 }
EggGroupNode
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
EggNode
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:36
EggPrimitive
A base class for any of a number of kinds of geometry primitives: polygons, point lights,...
Definition: eggPrimitive.h:49
EggToObjConverter
Convert an obj file to egg data.
Definition: eggToObjConverter.h:26
EggToObjConverter::get_extension
virtual std::string get_extension() const
Returns the common extension of the file type this converter supports.
Definition: eggToObjConverter.cxx:73
EggToObjConverter::write_file
virtual bool write_file(const Filename &filename)
Handles the conversion of the internal EggData to the target file format, written to the specified fi...
Definition: eggToObjConverter.cxx:91
EggToObjConverter::supports_compressed
virtual bool supports_compressed() const
Returns true if this file type can transparently save compressed files (with a .pz extension),...
Definition: eggToObjConverter.cxx:82
EggToObjConverter::make_copy
virtual EggToSomethingConverter * make_copy()
Allocates and returns a new copy of the converter.
Definition: eggToObjConverter.cxx:56
EggToObjConverter::get_name
virtual std::string get_name() const
Returns the English name of the file type this converter supports.
Definition: eggToObjConverter.cxx:65
EggToSomethingConverter
This is a base class for a family of converter classes that manage a conversion from egg format to so...
Definition: eggToSomethingConverter.h:34
EggToSomethingConverter::clear_error
void clear_error()
Resets the error flag to the no-error state.
Definition: eggToSomethingConverter.I:19
EggToSomethingConverter::had_error
bool had_error() const
Returns true if an error was detected during the conversion process, false otherwise.
Definition: eggToSomethingConverter.I:28
EggVertex
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
Definition: eggVertex.h:39
EggVertex::has_uvw
bool has_uvw(const std::string &name) const
Returns true if the vertex has the named UV coordinate triple, and the named UV coordinate triple is ...
Definition: eggVertex.cxx:129
EggVertex::has_uv
bool has_uv() const
Returns true if the vertex has an unnamed UV coordinate pair, false otherwise.
Definition: eggVertex.I:158
EggVertex::get_uv
LTexCoordd get_uv() const
Returns the unnamed UV coordinate pair on the vertex.
Definition: eggVertex.I:179
EggVertex::get_uvw
const LTexCoord3d & get_uvw(const std::string &name) const
Returns the named UV coordinate triple on the vertex.
Definition: eggVertex.cxx:163
EggVertex::get_pos2
LPoint2d get_pos2() const
Only valid if get_num_dimensions() returns 2.
Definition: eggVertex.I:120
EggVertex::get_num_dimensions
int get_num_dimensions() const
Returns the number of dimensions the vertex uses.
Definition: eggVertex.I:99
EggVertex::get_pos1
double get_pos1() const
Only valid if get_num_dimensions() returns 1.
Definition: eggVertex.I:109
EggVertex::get_pos3
LVertexd get_pos3() const
Valid if get_num_dimensions() returns 3 or 4.
Definition: eggVertex.I:131
EggVertex::get_pos4
LPoint4d get_pos4() const
This is always valid, regardless of the value of get_num_dimensions.
Definition: eggVertex.I:145
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
TypedObject::is_of_type
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
VirtualFileSystem
A hierarchy of directories and files that appears to be one continuous file system,...
Definition: virtualFileSystem.h:40
VirtualFileSystem::close_write_file
static void close_write_file(std::ostream *stream)
Closes a file opened by a previous call to open_write_file().
Definition: virtualFileSystem.cxx:929
VirtualFileSystem::open_write_file
std::ostream * open_write_file(const Filename &filename, bool auto_wrap, bool truncate)
Convenience function; returns a newly allocated ostream if the file exists and can be written,...
Definition: virtualFileSystem.cxx:890
VirtualFileSystem::delete_file
bool delete_file(const Filename &filename)
Attempts to delete the indicated file or directory.
Definition: virtualFileSystem.cxx:575
VirtualFileSystem::get_global_ptr
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
Definition: virtualFileSystem.cxx:741
config_egg.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_objegg.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
dcast.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggLine.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggPoint.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggPolygon.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggToObjConverter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
streamReader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
trim
string trim(const string &str)
Returns a new string representing the contents of the given string with both leading and trailing whi...
Definition: string_utils.cxx:281
string_utils.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtualFileSystem.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.