52 ~ObjToEggConverter() {
109 if (_egg_data->get_coordinate_system() == CS_default) {
110 _egg_data->set_coordinate_system(CS_zup_right);
113 if (!process(filename)) {
130 _current_vertex_data =
new VertexData(_root_node,
"root");
132 if (!process_node(filename)) {
136 _current_vertex_data->close_geom(
this);
137 delete _current_vertex_data;
149 bool ObjToEggConverter::
153 if (strm ==
nullptr) {
155 <<
"Couldn't read " << filename <<
"\n";
164 _ref_plane_res.set(1.0, 1.0);
170 _egg_data->add_child(_vpool);
172 _egg_data->add_child(_root_group);
173 _current_group = _root_group;
176 string line = sr.readline();
178 while (!line.empty()) {
181 line = sr.readline();
185 while (line[line.length() - 1] ==
'\\') {
187 string line2 = sr.readline();
192 line = line.substr(0, line.length() - 1) +
trim(line2);
195 if (line.substr(0, 15) ==
"#_ref_plane_res") {
196 process_ref_plane_res(line);
197 line = sr.readline();
201 if (line[0] ==
'#') {
202 line = sr.readline();
206 if (!process_line(line)) {
209 line = sr.readline();
214 generate_egg_points();
223 bool ObjToEggConverter::
224 process_line(
const string &line) {
227 nassertr(!words.empty(),
false);
229 string tag = words[0];
231 return process_v(words);
232 }
else if (tag ==
"vt") {
233 return process_vt(words);
234 }
else if (tag ==
"xvt") {
235 return process_xvt(words);
236 }
else if (tag ==
"xvc") {
237 return process_xvc(words);
238 }
else if (tag ==
"vn") {
239 return process_vn(words);
240 }
else if (tag ==
"f") {
241 return process_f(words);
242 }
else if (tag ==
"g") {
243 return process_g(words);
245 bool inserted = _ignored_tags.insert(tag).second;
248 <<
"Ignoring tag " << tag <<
"\n";
258 bool ObjToEggConverter::
259 process_ref_plane_res(
const string &line) {
266 nassertr(!words.empty(),
false);
268 if (words.size() != 3) {
270 <<
"Wrong number of tokens at line " << _line_number <<
"\n";
280 <<
"Invalid number at line " << _line_number <<
":\n";
290 bool ObjToEggConverter::
291 process_v(vector_string &words) {
292 if (words.size() != 4 && words.size() != 5 &&
293 words.size() != 7 && words.size() != 8) {
295 <<
"Wrong number of tokens at line " << _line_number <<
"\n";
304 if (words.size() == 5 || words.size() == 8) {
313 <<
"Invalid number at line " << _line_number <<
"\n";
317 _v_table.push_back(pos);
320 if (words.size() == 7 && words.size() == 8) {
321 size_t si = words.size();
329 <<
"Invalid number at line " << _line_number <<
"\n";
332 while (_rgb_table.size() + 1 < _v_table.size()) {
333 _rgb_table.push_back(LVecBase3d(1.0, 1.0, 1.0));
335 _rgb_table.push_back(rgb);
344 bool ObjToEggConverter::
345 process_vt(vector_string &words) {
346 if (words.size() != 3 && words.size() != 4) {
348 <<
"Wrong number of tokens at line " << _line_number <<
"\n";
356 if (words.size() == 4) {
365 <<
"Invalid number at line " << _line_number <<
"\n";
369 _vt_table.push_back(uvw);
379 bool ObjToEggConverter::
380 process_xvt(vector_string &words) {
381 if (words.size() < 3) {
383 <<
"Wrong number of tokens at line " << _line_number <<
"\n";
394 <<
"Invalid number at line " << _line_number <<
"\n";
398 uv[0] /= _ref_plane_res[0];
399 uv[1] = 1.0 - uv[1] / _ref_plane_res[1];
401 _xvt_table.push_back(uv);
409 bool ObjToEggConverter::
410 process_xvc(vector_string &words) {
417 bool ObjToEggConverter::
418 process_vn(vector_string &words) {
419 if (words.size() != 4) {
421 <<
"Wrong number of tokens at line " << _line_number <<
"\n";
433 <<
"Invalid number at line " << _line_number <<
"\n";
438 _vn_table.push_back(normal);
446 bool ObjToEggConverter::
447 process_f(vector_string &words) {
451 for (
size_t i = 1; i < words.size(); ++i) {
452 EggVertex *vertex = get_face_vertex(words[i]);
453 if (vertex ==
nullptr) {
456 poly->add_vertex(vertex);
466 bool ObjToEggConverter::
467 process_g(vector_string &words) {
475 size_t i = words.size();
479 if (child ==
nullptr || !child->
is_of_type(EggGroup::get_class_type())) {
486 _current_group = group;
495 get_face_vertex(
const string &reference) {
496 VertexEntry entry(
this, reference);
501 if (entry._vi != 0) {
503 synth.
set_pos(LCAST(
double, _v_table[entry._vi - 1]));
505 LPoint4d pos = _v_table[entry._vi - 1];
506 synth.
set_pos(LPoint3d(pos[0], pos[1], pos[2]));
509 if (entry._vi - 1 < (
int)_rgb_table.size()) {
511 LRGBColord rgb = _rgb_table[entry._vi - 1];
512 LColor rgba(rgb[0], rgb[1], rgb[2], 1.0);
513 synth.set_color(rgba);
517 if (entry._vti != 0) {
520 synth.
set_uvw(
"", _vt_table[entry._vti - 1]);
522 LTexCoord3d uvw = _vt_table[entry._vti - 1];
523 synth.
set_uv(
"", LTexCoordd(uvw[0], uvw[1]));
525 }
else if (entry._vi - 1 < (
int)_xvt_table.size()) {
527 synth.
set_uv(
"", _xvt_table[entry._vi - 1]);
530 if (entry._vni != 0) {
532 synth.set_normal(_vn_table[entry._vni - 1]);
535 return _vpool->create_unique_vertex(synth);
542 void ObjToEggConverter::
543 generate_egg_points() {
544 for (
size_t vi = 0; vi < _v_table.size(); ++vi) {
545 const LVecBase4d &p = _v_table[vi];
546 _vpool->make_new_vertex(LVecBase3d(p[0], p[1], p[2]));
554 bool ObjToEggConverter::
555 process_node(
const Filename &filename) {
558 if (strm ==
nullptr) {
560 <<
"Couldn't read " << filename <<
"\n";
569 _ref_plane_res.set(1.0, 1.0);
575 string line = sr.readline();
577 while (!line.empty()) {
580 line = sr.readline();
584 if (line.substr(0, 15) ==
"#_ref_plane_res") {
585 process_ref_plane_res(line);
586 line = sr.readline();
590 if (line[0] ==
'#') {
591 line = sr.readline();
595 if (!process_line_node(line)) {
598 line = sr.readline();
612 bool ObjToEggConverter::
613 process_line_node(
const string &line) {
616 nassertr(!words.empty(),
false);
618 string tag = words[0];
620 return process_v(words);
621 }
else if (tag ==
"vt") {
622 return process_vt(words);
623 }
else if (tag ==
"xvt") {
624 return process_xvt(words);
625 }
else if (tag ==
"xvc") {
626 return process_xvc(words);
627 }
else if (tag ==
"vn") {
628 return process_vn(words);
629 }
else if (tag ==
"f") {
630 return process_f_node(words);
631 }
else if (tag ==
"g") {
632 return process_g_node(words);
634 bool inserted = _ignored_tags.insert(tag).second;
637 <<
"Ignoring tag " << tag <<
"\n";
647 bool ObjToEggConverter::
648 process_f_node(vector_string &words) {
655 verts.reserve(words.size() - 1);
656 for (
size_t i = 1; i < words.size(); ++i) {
657 VertexEntry entry(
this, words[i]);
658 verts.push_back(entry);
659 if (entry._vni == 0) {
665 if (verts.size() < 3) {
668 <<
"Degenerate face at " << _line_number <<
"\n";
675 LNormald normal = LNormald::zero();
676 for (
size_t i = 0; i < verts.size(); ++i) {
677 int vi0 = verts[i]._vi;
678 int vi1 = verts[(i + 1) % verts.size()]._vi;
679 if (vi0 == 0 || vi1 == 0) {
682 const LVecBase4d &p0 = _v_table[vi0 - 1];
683 const LVecBase4d &p1 = _v_table[vi1 - 1];
685 normal[0] += p0[1] * p1[2] - p0[2] * p1[1];
686 normal[1] += p0[2] * p1[0] - p0[0] * p1[2];
687 normal[2] += p0[0] * p1[1] - p0[1] * p1[0];
690 synth_vni = add_synth_normal(normal);
696 if (verts.size() != 3) {
698 for (
size_t i = 0; i < verts.size(); ++i) {
699 const LVecBase4d &p = _v_table[verts[i]._vi - 1];
708 if (_current_vertex_data->_prim->get_num_vertices() + 3 * num_tris > egg_max_indices ||
709 _current_vertex_data->_entries.size() + verts.size() > (size_t)egg_max_vertices) {
712 _current_vertex_data->close_geom(
this);
715 if (verts.size() == 3) {
717 _current_vertex_data->add_triangle(
this, verts[0], verts[1], verts[2], synth_vni);
721 for (
int ti = 0; ti < num_tris; ++ti) {
725 _current_vertex_data->add_triangle(
this, verts[i0], verts[i1], verts[i2], synth_vni);
735 bool ObjToEggConverter::
736 process_g_node(vector_string &words) {
737 _current_vertex_data->close_geom(
this);
738 delete _current_vertex_data;
739 _current_vertex_data =
nullptr;
748 size_t i = words.size();
765 _current_vertex_data =
new VertexData(np.node(), name);
774 void ObjToEggConverter::
778 vdata->set_num_rows(_v_table.size());
781 for (
size_t vi = 0; vi < _v_table.size(); ++vi) {
782 const LVecBase4d &p = _v_table[vi];
783 vertex_writer.add_data3d(p[0], p[1], p[2]);
787 prim->add_next_vertices(_v_table.size());
788 prim->close_primitive();
791 geom->add_primitive(prim);
794 geom_node->add_geom(geom);
795 _root_node->add_child(geom_node);
802 int ObjToEggConverter::
803 add_synth_normal(
const LVecBase3d &normal) {
804 std::pair<UniqueVec3Table::iterator, bool> result = _unique_synth_vn_table.insert(UniqueVec3Table::value_type(normal, _unique_synth_vn_table.size()));
805 UniqueVec3Table::iterator ni = result.first;
806 int index = (*ni).second;
811 _synth_vn_table.push_back(normal);
821 ObjToEggConverter::VertexEntry::
829 tokenize(obj_vertex, words,
"/",
false);
830 nassertv(!words.empty());
832 for (
size_t i = 0; i < words.size(); ++i) {
834 if (
trim(words[i]).empty()) {
846 _vi = (int)converter->_v_table.size() + _vi;
848 if (_vi < 0 || _vi - 1 >= (
int)converter->_v_table.size()) {
856 _vti = (int)converter->_vt_table.size() + _vti;
858 if (_vti < 0 || _vti - 1 >= (
int)converter->_vt_table.size()) {
866 _vni = (int)converter->_vn_table.size() + _vni;
868 if (_vni < 0 || _vni - 1 >= (
int)converter->_vn_table.size()) {
879 ObjToEggConverter::VertexData::
880 VertexData(
PandaNode *parent,
const string &name) :
881 _parent(parent), _name(name)
883 _geom_node =
nullptr;
898 int ObjToEggConverter::VertexData::
900 std::pair<UniqueVertexEntries::iterator, bool> result;
901 UniqueVertexEntries::iterator ni;
904 if (entry._vni != 0 || entry._synth_vni != 0) {
907 VertexEntry no_normal(entry);
909 no_normal._synth_vni = 0;
910 ni = _unique_entries.find(no_normal);
911 if (ni != _unique_entries.end()) {
914 index = (*ni).second;
915 _unique_entries.erase(ni);
916 result = _unique_entries.insert(UniqueVertexEntries::value_type(entry, index));
917 nassertr(result.second, index);
918 nassertr(_entries[index] == no_normal, index);
919 _entries[index]._vni = entry._vni;
920 _entries[index]._synth_vni = entry._synth_vni;
923 }
else if (entry._vni == 0 && entry._synth_vni == 0) {
926 ni = _unique_entries.lower_bound(entry);
927 if (ni != _unique_entries.end() && (*ni).first.matches_except_normal(entry)) {
929 index = (*ni).second;
936 result = _unique_entries.insert(UniqueVertexEntries::value_type(entry, _entries.size()));
938 index = (*ni).second;
943 _entries.push_back(entry);
945 if (converter->_v4_given) {
948 if (converter->_vt3_given) {
951 if (entry._vti != 0) {
953 }
else if (entry._vi - 1 < (
int)converter->_xvt_table.size()) {
957 if (entry._vi - 1 < (
int)converter->_rgb_table.size()) {
961 if (entry._vni != 0) {
974 void ObjToEggConverter::VertexData::
976 const VertexEntry &v1,
const VertexEntry &v2,
980 v0i = add_vertex(converter, v0);
981 v1i = add_vertex(converter, v1);
983 if (synth_vni != 0) {
985 v2n._synth_vni = synth_vni;
986 v2i = add_vertex(converter, v2n);
988 v2i = add_vertex(converter, v2);
991 _prim->add_vertices(v0i, v1i, v2i);
992 _prim->close_primitive();
999 void ObjToEggConverter::VertexData::
1001 if (_prim->get_num_vertices() != 0) {
1005 aformat->
add_column(InternalName::get_vertex(), 4,
1006 GeomEnums::NT_stdfloat, GeomEnums::C_point);
1008 aformat->add_column(InternalName::get_vertex(), 3,
1009 GeomEnums::NT_stdfloat, GeomEnums::C_point);
1014 aformat->add_column(InternalName::get_normal(), 3,
1015 GeomEnums::NT_stdfloat, GeomEnums::C_vector);
1019 aformat->add_column(InternalName::get_texcoord(), 3,
1020 GeomEnums::NT_stdfloat, GeomEnums::C_texcoord);
1022 aformat->add_column(InternalName::get_texcoord(), 2,
1023 GeomEnums::NT_stdfloat, GeomEnums::C_texcoord);
1028 aformat->add_column(InternalName::get_color(), 4,
1029 GeomEnums::NT_uint8, GeomEnums::C_color);
1041 for (
size_t i = 0; i < _entries.size(); ++i) {
1042 const VertexEntry &entry = _entries[i];
1044 if (entry._vi != 0) {
1045 vertex_writer.set_row(i);
1046 vertex_writer.add_data4d(converter->_v_table[entry._vi - 1]);
1048 if (entry._vti != 0) {
1049 texcoord_writer.set_row(i);
1050 texcoord_writer.add_data3d(converter->_vt_table[entry._vti - 1]);
1051 }
else if (entry._vi - 1 < (
int)converter->_xvt_table.size()) {
1053 texcoord_writer.set_row(i);
1054 texcoord_writer.add_data2d(converter->_xvt_table[entry._vi - 1]);
1056 if (entry._vni != 0) {
1057 normal_writer.set_row(i);
1058 normal_writer.add_data3d(converter->_vn_table[entry._vni - 1]);
1059 }
else if (entry._synth_vni != 0) {
1060 normal_writer.set_row(i);
1061 normal_writer.add_data3d(converter->_synth_vn_table[entry._synth_vni - 1]);
1065 normal_writer.set_row(i);
1066 normal_writer.add_data3d(0, 0, 1);
1069 if (entry._vi - 1 < (
int)converter->_rgb_table.size()) {
1070 color_writer.set_row(i);
1071 color_writer.add_data3d(converter->_rgb_table[entry._vi - 1]);
1077 vdata->transform_vertices(LMatrix4::convert_mat(CS_zup_right, CS_default));
1080 CPT(
RenderState) state = RenderState::make_empty();
1082 state = state->add_attrib(ColorAttrib::make_vertex());
1084 state = state->add_attrib(ColorAttrib::make_flat(LColor(1, 1, 1, 1)));
1088 state = state->add_attrib(ShadeModelAttrib::make(ShadeModelAttrib::M_flat));
1089 _prim->set_shade_model(GeomEnums::SM_flat_last_vertex);
1093 geom->add_primitive(_prim);
1095 if (_geom_node ==
nullptr) {
1097 _parent->add_child(_geom_node);
1100 _geom_node->add_geom(geom, state);
1105 _unique_entries.clear();
int string_to_int(const string &str, string &tail)
A string-interface wrapper around the C library strtol().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A basic node of the scene graph or data graph.
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
double string_to_double(const string &str, string &tail)
A string-interface wrapper around the C library strtol().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Defines a series of disconnected points.
virtual SomethingToEggConverter * make_copy()
Allocates and returns a new copy of the converter.
bool had_error() const
Returns true if an error was detected during the conversion process (unless _allow_errors is true),...
void set_pos(double pos)
Sets the vertex position.
Specifies parameters that may be passed to the loader.
A hierarchy of directories and files that appears to be one continuous file system,...
std::istream * open_read_file(const Filename &filename, bool auto_unwrap) const
Convenience function; returns a newly allocated istream if the file exists and can be read,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_triangle_v0(int n) const
Returns vertex 0 of the nth triangle generated by the previous call to triangulate().
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
virtual std::string get_extension() const
Returns the common extension of the file type this converter supports.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_triangle_v2(int n) const
Returns vertex 2 of the nth triangle generated by the previous call to triangulate().
virtual std::string get_name() const
Returns the English name of the file type this converter supports.
virtual bool convert_file(const Filename &filename)
Handles the reading of the input file and converting it to egg.
void triangulate()
Does the work of triangulating the specified polygon.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual bool supports_convert_to_node(const LoaderOptions &options) const
Returns true if this converter can directly convert the model type to internal Panda memory structure...
PT(PandaNode) ObjToEggConverter
Reads the input file and directly produces a ready-to-render model file as a PandaNode.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
int add_vertex(const LPoint3d &point)
Adds a new vertex to the vertex pool.
void clear_error()
Resets the error flag to the no-error state.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The name of a file, such as a texture file or an Egg file.
NodePath attach_new_node(PandaNode *node, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Attaches a new node, with or without existing parents, to the scene graph below the referenced node o...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_uvw(const std::string &name, const LTexCoord3d &texCoord)
Sets the indicated UV coordinate triple on the vertex.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Convert an Obj file to egg data.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
virtual bool supports_compressed() const
Returns true if this file type can transparently load compressed files (with a .pz extension),...
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
A container for geometry primitives.
string trim(const string &str)
Returns a new string representing the contents of the given string with both leading and trailing whi...
int get_num_triangles() const
Returns the number of triangles generated by the previous call to triangulate().
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
void add_polygon_vertex(int index)
Adds the next consecutive vertex of the polygon.
This is an extension of Triangulator to handle polygons with three- dimensional points.
void tokenize(const string &str, vector_string &words, const string &delimiters, bool discard_repeated_delimiters)
Chops the source string up into pieces delimited by any of the characters specified in delimiters.
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A base class for things that may be directly added into the egg hierarchy.
Defines a series of disconnected triangles.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
int get_triangle_v1(int n) const
Returns vertex 1 of the nth triangle generated by the previous call to triangulate().
A class to read sequential binary data directly from an istream.
void set_uv(const LTexCoordd &texCoord)
Replaces the unnamed UV coordinate pair on the vertex with the indicated value.
This is a base class for a family of converter classes that manage a conversion from some file type t...
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
A collection of vertices.
EggNode * find_child(const std::string &name) const
Returns the child of this node whose name is the indicated string, or NULL if there is no child of th...
A node that holds Geom objects, renderable pieces of geometry.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.