15 #include "objToEggConverter.h" 16 #include "config_objegg.h" 18 #include "string_utils.h" 19 #include "streamReader.h" 20 #include "virtualFileSystem.h" 21 #include "eggPolygon.h" 23 #include "geomTriangles.h" 24 #include "geomPoints.h" 25 #include "colorAttrib.h" 26 #include "shadeModelAttrib.h" 28 #include "triangulator3.h" 29 #include "config_egg2pg.h" 57 ~ObjToEggConverter() {
132 if (_egg_data->get_coordinate_system() == CS_default) {
133 _egg_data->set_coordinate_system(CS_zup_right);
136 if (!process(filename)) {
156 _current_vertex_data =
new VertexData(_root_node,
"root");
158 if (!process_node(filename)) {
162 _current_vertex_data->close_geom(
this);
163 delete _current_vertex_data;
177 bool ObjToEggConverter::
183 <<
"Couldn't read " << filename <<
"\n";
192 _ref_plane_res.set(1.0, 1.0);
198 _egg_data->add_child(_vpool);
200 _egg_data->add_child(_root_group);
201 _current_group = _root_group;
206 while (!line.empty()) {
213 while (line[line.length() - 1] ==
'\\') {
220 line = line.substr(0, line.length() - 1) + trim(line2);
223 if (line.substr(0, 15) ==
"#_ref_plane_res") {
224 process_ref_plane_res(line);
229 if (line[0] ==
'#') {
234 if (!process_line(line)) {
249 bool ObjToEggConverter::
250 process_line(
const string &line) {
252 tokenize(line, words,
" \t",
true);
253 nassertr(!words.empty(),
false);
255 string tag = words[0];
257 return process_v(words);
258 }
else if (tag ==
"vt") {
259 return process_vt(words);
260 }
else if (tag ==
"xvt") {
261 return process_xvt(words);
262 }
else if (tag ==
"xvc") {
263 return process_xvc(words);
264 }
else if (tag ==
"vn") {
265 return process_vn(words);
266 }
else if (tag ==
"f") {
267 return process_f(words);
268 }
else if (tag ==
"g") {
269 return process_g(words);
271 bool inserted = _ignored_tags.insert(tag).second;
274 <<
"Ignoring tag " << tag <<
"\n";
286 bool ObjToEggConverter::
287 process_ref_plane_res(
const string &line) {
293 tokenize(line, words,
" \t",
true);
294 nassertr(!words.empty(),
false);
296 if (words.size() != 3) {
298 <<
"Wrong number of tokens at line " << _line_number <<
"\n";
304 okflag &= string_to_stdfloat(words[1], _ref_plane_res[0]);
305 okflag &= string_to_stdfloat(words[2], _ref_plane_res[1]);
309 <<
"Invalid number at line " << _line_number <<
":\n";
321 bool ObjToEggConverter::
322 process_v(vector_string &words) {
323 if (words.size() != 4 && words.size() != 5 &&
324 words.size() != 7 && words.size() != 8) {
326 <<
"Wrong number of tokens at line " << _line_number <<
"\n";
332 okflag &= string_to_stdfloat(words[1], pos[0]);
333 okflag &= string_to_stdfloat(words[2], pos[1]);
334 okflag &= string_to_stdfloat(words[3], pos[2]);
335 if (words.size() == 5 || words.size() == 8) {
336 okflag &= string_to_stdfloat(words[4], pos[3]);
344 <<
"Invalid number at line " << _line_number <<
"\n";
348 _v_table.push_back(pos);
352 if (words.size() == 7 && words.size() == 8) {
353 size_t si = words.size();
355 okflag &= string_to_stdfloat(words[si - 3], rgb[0]);
356 okflag &= string_to_stdfloat(words[si - 2], rgb[1]);
357 okflag &= string_to_stdfloat(words[si - 1], rgb[2]);
361 <<
"Invalid number at line " << _line_number <<
"\n";
364 while (_rgb_table.size() + 1 < _v_table.size()) {
365 _rgb_table.push_back(
LVecBase3(1.0, 1.0, 1.0));
367 _rgb_table.push_back(rgb);
378 bool ObjToEggConverter::
379 process_vt(vector_string &words) {
380 if (words.size() != 3 && words.size() != 4) {
382 <<
"Wrong number of tokens at line " << _line_number <<
"\n";
388 okflag &= string_to_stdfloat(words[1], uvw[0]);
389 okflag &= string_to_stdfloat(words[2], uvw[1]);
390 if (words.size() == 4) {
391 okflag &= string_to_stdfloat(words[3], uvw[2]);
399 <<
"Invalid number at line " << _line_number <<
"\n";
403 _vt_table.push_back(uvw);
416 bool ObjToEggConverter::
417 process_xvt(vector_string &words) {
418 if (words.size() < 3) {
420 <<
"Wrong number of tokens at line " << _line_number <<
"\n";
426 okflag &= string_to_stdfloat(words[1], uv[0]);
427 okflag &= string_to_stdfloat(words[2], uv[1]);
431 <<
"Invalid number at line " << _line_number <<
"\n";
435 uv[0] /= _ref_plane_res[0];
436 uv[1] = 1.0 - uv[1] / _ref_plane_res[1];
438 _xvt_table.push_back(uv);
449 bool ObjToEggConverter::
450 process_xvc(vector_string &words) {
459 bool ObjToEggConverter::
460 process_vn(vector_string &words) {
461 if (words.size() != 4) {
463 <<
"Wrong number of tokens at line " << _line_number <<
"\n";
469 okflag &= string_to_stdfloat(words[1], normal[0]);
470 okflag &= string_to_stdfloat(words[2], normal[1]);
471 okflag &= string_to_stdfloat(words[3], normal[2]);
475 <<
"Invalid number at line " << _line_number <<
"\n";
480 _vn_table.push_back(normal);
490 bool ObjToEggConverter::
491 process_f(vector_string &words) {
495 for (
size_t i = 1; i < words.size(); ++i) {
496 EggVertex *vertex = get_face_vertex(words[i]);
497 if (vertex == NULL) {
500 poly->add_vertex(vertex);
512 bool ObjToEggConverter::
513 process_g(vector_string &words) {
521 size_t i = words.size();
525 if (child == NULL || !child->
is_of_type(EggGroup::get_class_type())) {
532 _current_group = group;
543 get_face_vertex(
const string &reference) {
544 VertexEntry entry(
this, reference);
549 if (entry._vi != 0) {
551 synth.
set_pos(LCAST(
double, _v_table[entry._vi - 1]));
553 LPoint4 pos = _v_table[entry._vi - 1];
557 if (entry._vi - 1 < (
int)_rgb_table.size()) {
559 LRGBColor rgb = _rgb_table[entry._vi - 1];
560 LColor rgba(rgb[0], rgb[1], rgb[2], 1.0);
561 synth.set_color(rgba);
565 if (entry._vti != 0) {
568 synth.
set_uvw(
"", LCAST(
double, _vt_table[entry._vti - 1]));
573 }
else if (entry._vi - 1 < (
int)_xvt_table.size()) {
575 synth.
set_uv(
"", LCAST(
double, _xvt_table[entry._vi - 1]));
578 if (entry._vni != 0) {
580 synth.set_normal(LCAST(
double, _vn_table[entry._vni - 1]));
583 return _vpool->create_unique_vertex(synth);
592 bool ObjToEggConverter::
593 process_node(
const Filename &filename) {
598 <<
"Couldn't read " << filename <<
"\n";
607 _ref_plane_res.set(1.0, 1.0);
615 while (!line.empty()) {
622 if (line.substr(0, 15) ==
"#_ref_plane_res") {
623 process_ref_plane_res(line);
628 if (line[0] ==
'#') {
633 if (!process_line_node(line)) {
652 bool ObjToEggConverter::
653 process_line_node(
const string &line) {
655 tokenize(line, words,
" \t",
true);
656 nassertr(!words.empty(),
false);
658 string tag = words[0];
660 return process_v(words);
661 }
else if (tag ==
"vt") {
662 return process_vt(words);
663 }
else if (tag ==
"xvt") {
664 return process_xvt(words);
665 }
else if (tag ==
"xvc") {
666 return process_xvc(words);
667 }
else if (tag ==
"vn") {
668 return process_vn(words);
669 }
else if (tag ==
"f") {
670 return process_f_node(words);
671 }
else if (tag ==
"g") {
672 return process_g_node(words);
674 bool inserted = _ignored_tags.insert(tag).second;
677 <<
"Ignoring tag " << tag <<
"\n";
689 bool ObjToEggConverter::
690 process_f_node(vector_string &words) {
694 int non_vn_index = -1;
697 verts.reserve(words.size() - 1);
698 for (
size_t i = 1; i < words.size(); ++i) {
699 VertexEntry entry(
this, words[i]);
700 verts.push_back(entry);
701 if (entry._vni == 0) {
707 if (verts.size() < 3) {
710 <<
"Degenerate face at " << _line_number <<
"\n";
718 for (
size_t i = 0; i < verts.size(); ++i) {
719 int vi0 = verts[i]._vi;
720 int vi1 = verts[(i + 1) % verts.size()]._vi;
721 if (vi0 == 0 || vi1 == 0) {
727 normal[0] += p0[1] * p1[2] - p0[2] * p1[1];
728 normal[1] += p0[2] * p1[0] - p0[0] * p1[2];
729 normal[2] += p0[0] * p1[1] - p0[1] * p1[0];
732 synth_vni = add_synth_normal(normal);
738 if (verts.size() != 3) {
740 for (
size_t i = 0; i < verts.size(); ++i) {
741 const LVecBase4 &p = _v_table[verts[i]._vi - 1];
750 if (_current_vertex_data->_prim->get_num_vertices() + 3 * num_tris > egg_max_indices ||
751 _current_vertex_data->_entries.size() + verts.size() > egg_max_vertices) {
753 _current_vertex_data->close_geom(
this);
756 if (verts.size() == 3) {
758 _current_vertex_data->add_triangle(
this, verts[0], verts[1], verts[2], synth_vni);
762 for (
int ti = 0; ti < num_tris; ++ti) {
766 _current_vertex_data->add_triangle(
this, verts[i0], verts[i1], verts[i2], synth_vni);
778 bool ObjToEggConverter::
779 process_g_node(vector_string &words) {
780 _current_vertex_data->close_geom(
this);
781 delete _current_vertex_data;
782 _current_vertex_data = NULL;
791 size_t i = words.size();
808 _current_vertex_data =
new VertexData(np.
node(), name);
820 void ObjToEggConverter::
822 CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3();
824 vdata->set_num_rows(_v_table.size());
827 for (
size_t vi = 0; vi < _v_table.size(); ++vi) {
829 vertex_writer.
add_data3(p[0], p[1], p[2]);
833 prim->add_next_vertices(_v_table.size());
834 prim->close_primitive();
837 geom->add_primitive(prim);
840 geom_node->add_geom(geom);
841 _root_node->add_child(geom_node);
851 int ObjToEggConverter::
852 add_synth_normal(
const LVecBase3 &normal) {
853 pair<UniqueVec3Table::iterator, bool> result = _unique_synth_vn_table.insert(UniqueVec3Table::value_type(normal, _unique_synth_vn_table.size()));
854 UniqueVec3Table::iterator ni = result.first;
855 int index = (*ni).second;
860 _synth_vn_table.push_back(normal);
872 ObjToEggConverter::VertexEntry::
880 tokenize(obj_vertex, words,
"/",
false);
881 nassertv(!words.empty());
883 for (
size_t i = 0; i < words.size(); ++i) {
885 if (trim(words[i]).empty()) {
888 if (!string_to_int(words[i], index)) {
897 _vi = (int)converter->_v_table.size() + _vi;
899 if (_vi < 0 || _vi - 1 >= (
int)converter->_v_table.size()) {
907 _vti = (int)converter->_vt_table.size() + _vti;
909 if (_vti < 0 || _vti - 1 >= (
int)converter->_vt_table.size()) {
917 _vni = (int)converter->_vn_table.size() + _vni;
919 if (_vni < 0 || _vni - 1 >= (
int)converter->_vn_table.size()) {
932 ObjToEggConverter::VertexData::
933 VertexData(
PandaNode *parent,
const string &name) :
934 _parent(parent), _name(name)
954 int ObjToEggConverter::VertexData::
956 pair<UniqueVertexEntries::iterator, bool> result;
957 UniqueVertexEntries::iterator ni;
960 if (entry._vni != 0 || entry._synth_vni != 0) {
963 VertexEntry no_normal(entry);
965 no_normal._synth_vni = 0;
966 ni = _unique_entries.find(no_normal);
967 if (ni != _unique_entries.end()) {
970 index = (*ni).second;
971 _unique_entries.erase(ni);
972 result = _unique_entries.insert(UniqueVertexEntries::value_type(entry, index));
973 nassertr(result.second, index);
974 nassertr(_entries[index] == no_normal, index);
975 _entries[index]._vni = entry._vni;
976 _entries[index]._synth_vni = entry._synth_vni;
979 }
else if (entry._vni == 0 && entry._synth_vni == 0) {
982 ni = _unique_entries.lower_bound(entry);
983 if (ni != _unique_entries.end() && (*ni).first.matches_except_normal(entry)) {
985 index = (*ni).second;
992 result = _unique_entries.insert(UniqueVertexEntries::value_type(entry, _entries.size()));
994 index = (*ni).second;
999 _entries.push_back(entry);
1001 if (converter->_v4_given) {
1004 if (converter->_vt3_given) {
1007 if (entry._vti != 0) {
1009 }
else if (entry._vi - 1 < (
int)converter->_xvt_table.size()) {
1013 if (entry._vi - 1 < (
int)converter->_rgb_table.size()) {
1017 if (entry._vni != 0) {
1033 void ObjToEggConverter::VertexData::
1035 const VertexEntry &v1,
const VertexEntry &v2,
1039 v0i = add_vertex(converter, v0);
1040 v1i = add_vertex(converter, v1);
1042 if (synth_vni != 0) {
1043 VertexEntry v2n(v2);
1044 v2n._synth_vni = synth_vni;
1045 v2i = add_vertex(converter, v2n);
1047 v2i = add_vertex(converter, v2);
1050 _prim->add_vertices(v0i, v1i, v2i);
1051 _prim->close_primitive();
1060 void ObjToEggConverter::VertexData::
1062 if (_prim->get_num_vertices() != 0) {
1065 PT(GeomVertexArrayFormat) aformat =
new GeomVertexArrayFormat;
1067 aformat->add_column(InternalName::get_vertex(), 4,
1068 GeomEnums::NT_stdfloat, GeomEnums::C_point);
1070 aformat->add_column(InternalName::get_vertex(), 3,
1071 GeomEnums::NT_stdfloat, GeomEnums::C_point);
1076 aformat->add_column(InternalName::get_normal(), 3,
1077 GeomEnums::NT_stdfloat, GeomEnums::C_vector);
1081 aformat->add_column(InternalName::get_texcoord(), 3,
1082 GeomEnums::NT_stdfloat, GeomEnums::C_texcoord);
1084 aformat->add_column(InternalName::get_texcoord(), 2,
1085 GeomEnums::NT_stdfloat, GeomEnums::C_texcoord);
1090 aformat->add_column(InternalName::get_color(), 4,
1091 GeomEnums::NT_uint8, GeomEnums::C_color);
1094 CPT(GeomVertexFormat) format = GeomVertexFormat::register_format(aformat);
1103 for (
size_t i = 0; i < _entries.size(); ++i) {
1104 const VertexEntry &entry = _entries[i];
1106 if (entry._vi != 0) {
1108 vertex_writer.
add_data4(converter->_v_table[entry._vi - 1]);
1110 if (entry._vti != 0) {
1112 texcoord_writer.
add_data3(converter->_vt_table[entry._vti - 1]);
1113 }
else if (entry._vi - 1 < (
int)converter->_xvt_table.size()) {
1116 texcoord_writer.
add_data2(converter->_xvt_table[entry._vi - 1]);
1118 if (entry._vni != 0) {
1120 normal_writer.
add_data3(converter->_vn_table[entry._vni - 1]);
1121 }
else if (entry._synth_vni != 0) {
1123 normal_writer.
add_data3(converter->_synth_vn_table[entry._synth_vni - 1]);
1131 if (entry._vi - 1 < (
int)converter->_rgb_table.size()) {
1133 color_writer.
add_data3(converter->_rgb_table[entry._vi - 1]);
1142 CPT(
RenderState) state = RenderState::make_empty();
1144 state = state->add_attrib(ColorAttrib::make_vertex());
1146 state = state->add_attrib(ColorAttrib::make_flat(
LColor(1, 1, 1, 1)));
1151 state = state->add_attrib(ShadeModelAttrib::make(ShadeModelAttrib::M_flat));
1152 _prim->set_shade_model(GeomEnums::SM_flat_last_vertex);
1156 geom->add_primitive(_prim);
1158 if (_geom_node == NULL) {
1160 _parent->add_child(_geom_node);
1163 _geom_node->add_geom(geom, state);
1168 _unique_entries.clear();
void set_uvw(const string &name, const LTexCoord3d &texCoord)
Sets the indicated UV coordinate triple on the vertex.
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...
This is the base class for all three-component vectors and points.
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, even though the files may originate from several different sources that may not be related to the actual OS's file system.
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...
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...
void add_data2(PN_stdfloat x, PN_stdfloat y)
Sets the write row to a particular 2-component value, and advances the write row. ...
static const LVector3f & zero()
Returns a zero-length vector.
virtual string get_extension() const
Returns the common extension of the file type this converter supports.
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
This is a two-component point in space.
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
int get_triangle_v2(int n) const
Returns vertex 2 of the nth triangle generated by the previous call to triangulate().
virtual 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.
void add_data4(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z, PN_stdfloat w)
Sets the write row to a particular 4-component value, and advances the write row. ...
static const LMatrix4f & convert_mat(CoordinateSystem from, CoordinateSystem to)
Returns a matrix that transforms from the indicated coordinate system to the indicated coordinate sys...
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...
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.
NodePath find(const string &path) const
Searches for a node below the referenced node that matches the indicated string.
void clear_error()
Resets the error flag to the no-error state.
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...
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal...
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.
void add_data3(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Sets the write row to a particular 3-component value, and advances the write row. ...
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.
This is the base class for all three-component vectors and points.
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
PandaNode * node() const
Returns the referenced node of the path.
This is a four-component point in space.
A base class for things that may be directly added into the egg hierarchy.
Defines a series of disconnected triangles.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
EggNode * find_child(const string &name) const
Returns the child of this node whose name is the indicated string, or NULL if there is no child of th...
void set_row(int row)
Sets the start row to the indicated value.
This is a two-component point in space.
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.
bool normalize()
Normalizes the vector in place.
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.
A node that holds Geom objects, renderable pieces of geometry.
string readline()
Assumes the stream represents a text file, and extracts one line up to and including the trailing new...