|
|
@@ -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;
|
|
|
+}
|