From 7db604c791b74fd09f32c3d7caf076d13712d9ad Mon Sep 17 00:00:00 2001 From: David Rose Date: Sun, 26 Feb 2012 00:43:21 +0000 Subject: [PATCH] egg2obj --- pandatool/src/objegg/objToEggConverter.cxx | 2 +- pandatool/src/objprogs/Sources.pp | 9 + pandatool/src/objprogs/eggToObj.cxx | 385 +++++++++++++++++++++ pandatool/src/objprogs/eggToObj.h | 71 ++++ pandatool/src/objprogs/objToEgg.cxx | 2 +- 5 files changed, 467 insertions(+), 2 deletions(-) create mode 100644 pandatool/src/objprogs/eggToObj.cxx create mode 100644 pandatool/src/objprogs/eggToObj.h diff --git a/pandatool/src/objegg/objToEggConverter.cxx b/pandatool/src/objegg/objToEggConverter.cxx index d64100f6e9..efffa5ca81 100755 --- a/pandatool/src/objegg/objToEggConverter.cxx +++ b/pandatool/src/objegg/objToEggConverter.cxx @@ -354,7 +354,7 @@ process_g(vector_string &words) { // Thus, iterate from the back to the front. size_t i = words.size(); - while (i != 0) { + while (i > 1) { --i; EggNode *child = group->find_child(words[i]); if (child == NULL || !child->is_of_type(EggGroup::get_class_type())) { diff --git a/pandatool/src/objprogs/Sources.pp b/pandatool/src/objprogs/Sources.pp index 39db2412a2..2f06393e50 100755 --- a/pandatool/src/objprogs/Sources.pp +++ b/pandatool/src/objprogs/Sources.pp @@ -19,3 +19,12 @@ objToEgg.cxx objToEgg.h #end bin_target + +#begin bin_target + #define TARGET egg2obj + #define LOCAL_LIBS p3eggbase p3progbase + + #define SOURCES \ + eggToObj.cxx eggToObj.h + +#end bin_target diff --git a/pandatool/src/objprogs/eggToObj.cxx b/pandatool/src/objprogs/eggToObj.cxx new file mode 100644 index 0000000000..9549d08ea3 --- /dev/null +++ b/pandatool/src/objprogs/eggToObj.cxx @@ -0,0 +1,385 @@ +// Filename: eggToObj.cxx +// Created by: drose (28Feb12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "eggToObj.h" +#include "pystub.h" +#include "eggPolygon.h" +#include "eggGroupNode.h" +#include "dcast.h" +#include "string_utils.h" + +//////////////////////////////////////////////////////////////////// +// Function: EggToObj::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggToObj:: +EggToObj() : + EggToSomething("Obj", ".obj", true, false) +{ + set_program_description + ("This program converts egg files to obj. It " + "only converts polygon data, with no fancy tricks. " + "Very bare-bones at the moment; not even texture maps are supported."); + + redescribe_option + ("cs", + "Specify the coordinate system of the resulting " + _format_name + + " file. Normally, this is z-up."); + + _coordinate_system = CS_zup_right; + _got_coordinate_system = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObj::run +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void EggToObj:: +run() { + _data->flatten_transforms(); + collect_vertices(_data); + + ostream &out = get_output(); + _current_group = NULL; + + out << "\n#\n" + << "# obj file generated by the following command:\n" + << "# " << get_exec_command() << "\n" + << "#\n\n"; + + write_vertices(out, "v", 3, _unique_vert3); + write_vertices(out, "v", 4, _unique_vert4); + write_vertices(out, "vt", 2, _unique_uv2); + write_vertices(out, "vt", 3, _unique_uv3); + write_vertices(out, "vn", 3, _unique_norm); + + write_faces(out, _data); + + if (!out) { + nout << "An error occurred while writing.\n"; + exit(1); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObj::handle_args +// Access: Protected, Virtual +// Description: Does something with the additional arguments on the +// command line (after all the -options have been +// parsed). Returns true if the arguments are good, +// false otherwise. +//////////////////////////////////////////////////////////////////// +bool EggToObj:: +handle_args(ProgramBase::Args &args) { + return EggToSomething::handle_args(args); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObj::collect_vertices +// Access: Private +// Description: Recursively walks the egg structure, looking for +// vertices referenced by polygons. Any such vertices +// are added to the vertex tables for writing to the obj +// file. +//////////////////////////////////////////////////////////////////// +void EggToObj:: +collect_vertices(EggNode *egg_node) { + if (egg_node->is_of_type(EggPolygon::get_class_type())) { + EggPolygon *egg_poly = DCAST(EggPolygon, egg_node); + EggPolygon::iterator pi; + for (pi = egg_poly->begin(); pi != egg_poly->end(); ++pi) { + record_vertex(*pi); + } + + } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) { + EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node); + + EggGroupNode::iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + collect_vertices(*ci); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObj::write_faces +// Access: Private +// Description: Recursively walks the egg structure again, this time +// writing out the face records for any polygons +// encountered. +//////////////////////////////////////////////////////////////////// +void EggToObj:: +write_faces(ostream &out, EggNode *egg_node) { + if (egg_node->is_of_type(EggPolygon::get_class_type())) { + write_group_reference(out, egg_node); + + EggPolygon *egg_poly = DCAST(EggPolygon, egg_node); + + out << "f"; + EggPolygon::iterator pi; + for (pi = egg_poly->begin(); pi != egg_poly->end(); ++pi) { + VertexDef &vdef = _vmap[(*pi)]; + int vert_index = -1; + int uv_index = -1; + int norm_index = -1; + + if (vdef._vert3_index != -1) { + vert_index = vdef._vert3_index + 1; + } else if (vdef._vert4_index != -1) { + vert_index = vdef._vert4_index + 1 + (int)_unique_vert3.size(); + } + + if (vdef._uv2_index != -1) { + uv_index = vdef._uv2_index + 1; + } else if (vdef._uv3_index != -1) { + uv_index = vdef._uv3_index + 1 + (int)_unique_uv2.size(); + } + + if (vdef._norm_index != -1) { + norm_index = vdef._norm_index + 1; + } + + if (vert_index == -1) { + continue; + } + + if (norm_index != -1) { + if (uv_index != -1) { + out << " " << vert_index << "/" << uv_index << "/" << norm_index; + } else { + out << " " << vert_index << "//" << norm_index; + } + } else if (uv_index != -1) { + out << " " << vert_index << "/" << uv_index; + } else { + out << " " << vert_index; + } + } + out << "\n"; + + } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) { + EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node); + + EggGroupNode::iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + write_faces(out, *ci); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObj::write_group_reference +// Access: Private +// Description: Writes the "g" tag to describe this polygon's group, +// if needed. +//////////////////////////////////////////////////////////////////// +void EggToObj:: +write_group_reference(ostream &out, EggNode *egg_node) { + EggGroupNode *egg_group = egg_node->get_parent(); + if (egg_group == _current_group) { + // Same group we wrote last time. + return; + } + + string group_name; + get_group_name(group_name, egg_group); + if (group_name.empty()) { + out << "g default\n"; + } else { + out << "g" << group_name << "\n"; + } + _current_group = egg_group; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObj::get_group_name +// Access: Private +// Description: Recursively determines the appropriate string to +// write for the "g" tag to describe a particular +// EggGroupNode. +//////////////////////////////////////////////////////////////////// +void EggToObj:: +get_group_name(string &group_name, EggGroupNode *egg_group) { + string name = trim(egg_group->get_name()); + if (!name.empty()) { + group_name += ' '; + + // Remove nonstandard characters. + for (string::const_iterator ni = name.begin(); ni != name.end(); ++ni) { + char c = (*ni); + if (!isalnum(c)) { + c = '_'; + } + group_name += c; + } + } + + // Now recurse. + EggGroupNode *egg_parent = egg_group->get_parent(); + if (egg_parent != NULL) { + get_group_name(group_name, egg_parent); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObj::record_vertex +// Access: Private +// Description: Adds the indicated EggVertex to the unique vertex +// tables, for writing later by write_vertices(). +//////////////////////////////////////////////////////////////////// +void EggToObj:: +record_vertex(EggVertex *vertex) { + VertexDef &vdef = _vmap[vertex]; + + switch (vertex->get_num_dimensions()) { + case 1: + vdef._vert3_index = record_unique(_unique_vert3, vertex->get_pos1()); + break; + case 2: + vdef._vert3_index = record_unique(_unique_vert3, vertex->get_pos2()); + break; + case 3: + vdef._vert3_index = record_unique(_unique_vert3, vertex->get_pos3()); + break; + case 4: + vdef._vert4_index = record_unique(_unique_vert4, vertex->get_pos4()); + break; + } + + if (vertex->has_uv("")) { + vdef._uv2_index = record_unique(_unique_uv2, vertex->get_uv("")); + } else if (vertex->has_uvw("")) { + vdef._uv3_index = record_unique(_unique_uv3, vertex->get_uvw("")); + } + + if (vertex->has_normal()) { + vdef._norm_index = record_unique(_unique_norm, vertex->get_normal()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObj::record_unique +// Access: Private +// Description: Records the indicated vertex value, returning the +// shared index if this value already appears elsewhere +// in the table, or the new unique index if this is the +// first time this value appears. +//////////////////////////////////////////////////////////////////// +int EggToObj:: +record_unique(UniqueVertices &unique, const LVecBase4d &vec) { + // We record a zero-based index. Note that we will actually write + // out a one-based index to the obj file, as required by the + // standard. + int index = unique.size(); + UniqueVertices::iterator ui = unique.insert(UniqueVertices::value_type(vec, index)).first; + return (*ui).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObj::record_unique +// Access: Private +// Description: Records the indicated vertex value, returning the +// shared index if this value already appears elsewhere +// in the table, or the new unique index if this is the +// first time this value appears. +//////////////////////////////////////////////////////////////////// +int EggToObj:: +record_unique(UniqueVertices &unique, const LVecBase3d &vec) { + return record_unique(unique, LVecBase4d(vec[0], vec[1], vec[2], 0.0)); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObj::record_unique +// Access: Private +// Description: Records the indicated vertex value, returning the +// shared index if this value already appears elsewhere +// in the table, or the new unique index if this is the +// first time this value appears. +//////////////////////////////////////////////////////////////////// +int EggToObj:: +record_unique(UniqueVertices &unique, const LVecBase2d &vec) { + return record_unique(unique, LVecBase4d(vec[0], vec[1], 0.0, 0.0)); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObj::record_unique +// Access: Private +// Description: Records the indicated vertex value, returning the +// shared index if this value already appears elsewhere +// in the table, or the new unique index if this is the +// first time this value appears. +//////////////////////////////////////////////////////////////////// +int EggToObj:: +record_unique(UniqueVertices &unique, double pos) { + return record_unique(unique, LVecBase4d(pos, 0.0, 0.0, 0.0)); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObj::write_vertices +// Access: Private +// Description: Actually writes the vertex values recorded in the +// indicated table to the obj output stream. +//////////////////////////////////////////////////////////////////// +void EggToObj:: +write_vertices(ostream &out, const string &prefix, int num_components, + const UniqueVertices &unique) { + // First, sort the list into numeric order. + int num_vertices = (int)unique.size(); + const LVecBase4d **vertices = (const LVecBase4d **)alloca(num_vertices * sizeof(LVecBase4d *)); + memset(vertices, 0, num_vertices * sizeof(LVecBase4d *)); + UniqueVertices::const_iterator ui; + for (ui = unique.begin(); ui != unique.end(); ++ui) { + int index = (*ui).second; + const LVecBase4d &vec = (*ui).first; + nassertv(index >= 0 && index < num_vertices); + nassertv(vertices[index] == NULL); + vertices[index] = &vec; + } + + for (int i = 0; i < num_vertices; ++i) { + out << prefix; + const LVecBase4d &vec = *(vertices[i]); + for (int ci = 0; ci < num_components; ++ci) { + out << " " << vec[ci]; + } + out << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObj::VertexDef::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggToObj::VertexDef:: +VertexDef() : + _vert3_index(-1), + _vert4_index(-1), + _uv2_index(-1), + _uv3_index(-1), + _norm_index(-1) +{ +} + +int main(int argc, char *argv[]) { + // A call to pystub() to force libpystub.so to be linked in. + pystub(); + + EggToObj prog; + prog.parse_command_line(argc, argv); + prog.run(); + return 0; +} diff --git a/pandatool/src/objprogs/eggToObj.h b/pandatool/src/objprogs/eggToObj.h new file mode 100644 index 0000000000..d1d11a364c --- /dev/null +++ b/pandatool/src/objprogs/eggToObj.h @@ -0,0 +1,71 @@ +// Filename: eggToObj.h +// Created by: drose (25Feb12) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGTOOBJ_H +#define EGGTOOBJ_H + +#include "pandatoolbase.h" +#include "eggToSomething.h" +#include "pmap.h" + +class EggNode; +class EggVertex; + +//////////////////////////////////////////////////////////////////// +// Class : EggToObj +// Description : +//////////////////////////////////////////////////////////////////// +class EggToObj : public EggToSomething { +public: + EggToObj(); + + void run(); + +protected: + virtual bool handle_args(Args &args); + +private: + typedef pmap UniqueVertices; + class VertexDef { + public: + VertexDef(); + int _vert3_index; + int _vert4_index; + int _uv2_index; + int _uv3_index; + int _norm_index; + }; + typedef pmap VertexMap; + + void collect_vertices(EggNode *egg_node); + void write_faces(ostream &out, EggNode *egg_node); + void write_group_reference(ostream &out, EggNode *egg_node); + void get_group_name(string &group_name, EggGroupNode *egg_group); + + void record_vertex(EggVertex *vertex); + int record_unique(UniqueVertices &unique, const LVecBase4d &vec); + int record_unique(UniqueVertices &unique, const LVecBase3d &vec); + int record_unique(UniqueVertices &unique, const LVecBase2d &vec); + int record_unique(UniqueVertices &unique, double pos); + + void write_vertices(ostream &out, const string &prefix, int num_components, + const UniqueVertices &unique); + +private: + UniqueVertices _unique_vert3, _unique_vert4, _unique_uv2, _unique_uv3, _unique_norm; + VertexMap _vmap; + EggGroupNode *_current_group; +}; + +#endif diff --git a/pandatool/src/objprogs/objToEgg.cxx b/pandatool/src/objprogs/objToEgg.cxx index 1e175b7a82..cc1261afbb 100755 --- a/pandatool/src/objprogs/objToEgg.cxx +++ b/pandatool/src/objprogs/objToEgg.cxx @@ -34,7 +34,7 @@ ObjToEgg() : set_program_description ("This program converts obj files to egg. It " "only converts polygon data, with no fancy tricks. " - "Very bare-bones at the moment, not even texture maps are supported."); + "Very bare-bones at the moment; not even texture maps are supported."); redescribe_option ("cs",