Panda3D
Loading...
Searching...
No Matches
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
26using std::ostream;
27using std::string;
28
29/**
30 *
31 */
32EggToObjConverter::
33EggToObjConverter() {
34}
35
36/**
37 *
38 */
39EggToObjConverter::
40EggToObjConverter(const EggToObjConverter &copy) :
42{
43}
44
45/**
46 *
47 */
48EggToObjConverter::
49~EggToObjConverter() {
50}
51
52/**
53 * Allocates and returns a new copy of the converter.
54 */
59
60
61/**
62 * Returns the English name of the file type this converter supports.
63 */
65get_name() const {
66 return "obj";
67}
68
69/**
70 * Returns the common extension of the file type this converter supports.
71 */
73get_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 */
82supports_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 */
91write_file(const Filename &filename) {
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 */
107bool EggToObjConverter::
108process(const Filename &filename) {
109 _egg_data->flatten_transforms();
110 collect_vertices(_egg_data);
111
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 */
151void EggToObjConverter::
152collect_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 */
174void EggToObjConverter::
175write_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 */
246void EggToObjConverter::
247write_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 */
268void EggToObjConverter::
269get_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 */
295void EggToObjConverter::
296record_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 */
330int EggToObjConverter::
331record_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 */
344int EggToObjConverter::
345record_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 */
354int EggToObjConverter::
355record_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 */
364int EggToObjConverter::
365record_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 */
373void EggToObjConverter::
374write_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 */
403EggToObjConverter::VertexDef::
404VertexDef() :
405 _vert3_index(-1),
406 _vert4_index(-1),
407 _uv2_index(-1),
408 _uv3_index(-1),
409 _norm_index(-1)
410{
411}
A base class for nodes in the hierarchy that are not leaf nodes.
A base class for things that may be directly added into the egg hierarchy.
Definition eggNode.h:36
A base class for any of a number of kinds of geometry primitives: polygons, point lights,...
Convert an obj file to egg data.
virtual std::string get_extension() const
Returns the common extension of the file type this converter supports.
virtual bool write_file(const Filename &filename)
Handles the conversion of the internal EggData to the target file format, written to the specified fi...
virtual bool supports_compressed() const
Returns true if this file type can transparently save compressed files (with a .pz extension),...
virtual EggToSomethingConverter * make_copy()
Allocates and returns a new copy of the converter.
virtual std::string get_name() const
Returns the English name of the file type this converter supports.
This is a base class for a family of converter classes that manage a conversion from egg format to so...
void clear_error()
Resets the error flag to the no-error state.
bool had_error() const
Returns true if an error was detected during the conversion process, false otherwise.
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
Definition eggVertex.h:39
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 ...
bool has_uv() const
Returns true if the vertex has an unnamed UV coordinate pair, false otherwise.
Definition eggVertex.I:158
LTexCoordd get_uv() const
Returns the unnamed UV coordinate pair on the vertex.
Definition eggVertex.I:179
const LTexCoord3d & get_uvw(const std::string &name) const
Returns the named UV coordinate triple on the vertex.
LPoint2d get_pos2() const
Only valid if get_num_dimensions() returns 2.
Definition eggVertex.I:120
int get_num_dimensions() const
Returns the number of dimensions the vertex uses.
Definition eggVertex.I:99
double get_pos1() const
Only valid if get_num_dimensions() returns 1.
Definition eggVertex.I:109
LVertexd get_pos3() const
Valid if get_num_dimensions() returns 3 or 4.
Definition eggVertex.I:131
LPoint4d get_pos4() const
This is always valid, regardless of the value of get_num_dimensions.
Definition eggVertex.I:145
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition typedObject.I:28
A hierarchy of directories and files that appears to be one continuous file system,...
static void close_write_file(std::ostream *stream)
Closes a file opened by a previous call to 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,...
bool delete_file(const Filename &filename)
Attempts to delete the indicated file or directory.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
string trim(const string &str)
Returns a new string representing the contents of the given string with both leading and trailing whi...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.