From 0f2f27689a3f3d4998a033323f68e2ee799ac52d Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 8 Nov 2015 02:12:43 +0100 Subject: [PATCH] Convert from DOS back to Unix newlines --- panda/src/egg/config_egg.cxx | 460 ++-- panda/src/egg/config_egg.h | 96 +- panda/src/egg/eggData.cxx | 828 +++---- pandatool/src/objegg/Sources.pp | 50 +- pandatool/src/objegg/eggToObjConverter.cxx | 924 ++++---- pandatool/src/objegg/objToEggConverter.cxx | 2376 ++++++++++---------- pandatool/src/objegg/objToEggConverter.h | 304 +-- 7 files changed, 2519 insertions(+), 2519 deletions(-) diff --git a/panda/src/egg/config_egg.cxx b/panda/src/egg/config_egg.cxx index 0aa50cbcb7..166d6488e0 100644 --- a/panda/src/egg/config_egg.cxx +++ b/panda/src/egg/config_egg.cxx @@ -1,230 +1,230 @@ -// Filename: config_egg.cxx -// Created by: drose (19Mar00) -// -//////////////////////////////////////////////////////////////////// -// -// 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 "config_egg.h" -#include "eggRenderMode.h" -#include "eggAnimData.h" -#include "eggAnimPreload.h" -#include "eggAttributes.h" -#include "eggBin.h" -#include "eggBinMaker.h" -#include "eggComment.h" -#include "eggCompositePrimitive.h" -#include "eggCoordinateSystem.h" -#include "eggCurve.h" -#include "eggExternalReference.h" -#include "eggFilenameNode.h" -#include "eggGroup.h" -#include "eggGroupNode.h" -#include "eggGroupUniquifier.h" -#include "eggLine.h" -#include "eggMaterial.h" -#include "eggNameUniquifier.h" -#include "eggNamedObject.h" -#include "eggNode.h" -#include "eggNurbsCurve.h" -#include "eggNurbsSurface.h" -#include "eggObject.h" -#include "eggPatch.h" -#include "eggPoint.h" -#include "eggPolygon.h" -#include "eggPolysetMaker.h" -#include "eggPoolUniquifier.h" -#include "eggPrimitive.h" -#include "eggSAnimData.h" -#include "eggSurface.h" -#include "eggSwitchCondition.h" -#include "eggTable.h" -#include "eggTexture.h" -#include "eggTriangleFan.h" -#include "eggTriangleStrip.h" -#include "eggUserData.h" -#include "eggVertex.h" -#include "eggVertexPool.h" -#include "eggVertexUV.h" -#include "eggVertexAux.h" -#include "eggXfmAnimData.h" -#include "eggXfmSAnim.h" - -#include "dconfig.h" - -Configure(config_egg); -NotifyCategoryDef(egg, ""); - -ConfigureFn(config_egg) { - init_libegg(); -} - -ConfigVariableBool egg_support_old_anims -("egg-support-old-anims", true, - PRC_DESC("Set this true to support loading of old character animation files, which " - "had the convention that the order \"phr\" implied a reversed roll.")); - -ConfigVariableBool egg_mesh -("egg-mesh", true, - PRC_DESC("Set this true to convert triangles and higher-order polygons " - "into triangle strips and triangle fans when an egg file is " - "loaded or converted to bam. Set this false just to triangulate " - "everything into independent triangles.")); - -ConfigVariableBool egg_retesselate_coplanar -("egg-retesselate-coplanar", false, - PRC_DESC("If this is true, the egg loader may reverse the " - "tesselation direction of a single pair of planar triangles that " - "share the same properties, if that will help get a better " - "triangle strip. In some rare cases, doing so can distort the " - "UV's on a face; turning this off should eliminate that artifact " - "(at the cost of less-effective triangle stripping).")); - -ConfigVariableBool egg_unroll_fans -("egg-unroll-fans", true, - PRC_DESC("Set this true to allow the egg loader to convert weak triangle " - "fans--triangles that share the same vertex but aren't " - "connected enough to justify making a triangle fan primitive " - "from them--into a series of zig-zag triangles that can make " - "a triangle strip that might connect better with its neighbors.")); - -ConfigVariableBool egg_show_tstrips -("egg-show-tstrips", false, - PRC_DESC("Set this true to color each triangle strip a random color, with " - "the leading triangle a little bit darker, so you can visually " - "observe the quality of the triangle stripping algorithm.")); - -ConfigVariableBool egg_show_qsheets -("egg-show-qsheets", false, - PRC_DESC("Set this true to color each quadsheet a random color, so you " - "can visually observe the quadsheet algorithm.")); - -ConfigVariableBool egg_show_quads -("egg-show-quads", false, - PRC_DESC("Set this true to color each detected quad a random color, so " - "you can visually observe the algorithm that unifies pairs of " - "triangles into quads (prior to generating triangle strips).")); - -ConfigVariableBool egg_subdivide_polys -("egg-subdivide-polys", true, - PRC_DESC("This is obsolete. In the old Geom implementation, it used to " - "be true to force higher-order polygons that were not otherwise " - "meshed to be subdivided into triangles. In the new " - "Geom implementation, this happens anyway.")); - -ConfigVariableBool egg_consider_fans -("egg-consider-fans", false, - PRC_DESC("Set this true to enable the egg mesher to consider making " - "triangle fans out of triangles that are connected at a common " - "vertex. This may help if your scene involves lots of such " - "connected triangles, but it can also make the overall stripping " - "less effective (by interfering with triangle strips).")); - -ConfigVariableDouble egg_max_tfan_angle -("egg-max-tfan-angle", 40.0, - PRC_DESC("The maximum average angle per triangle to allow in a triangle " - "fan. If triangles are larger than this--that is, more loosely " - "packed--then we figure a triangle strip is likely to do a " - "more effective job than a triangle fan, and the fan maker leaves " - "it alone.")); - -ConfigVariableInt egg_min_tfan_tris -("egg-min-tfan-tris", 4, - PRC_DESC("The minimum number of triangles that must be involved in order " - "to generate a triangle fan. Fewer than this is just interrupting " - "a triangle strip.")); - -ConfigVariableDouble egg_coplanar_threshold -("egg-coplanar-threshold", 0.01, - PRC_DESC("The numerical threshold below which polygons are considered " - "to be coplanar. Determined empirically.")); - -ConfigVariableInt egg_test_vref_integrity -("egg-test-vref-integrity", 20, - PRC_DESC("The maximum number of vertices a primitive may have before " - "its vertices will no longer be checked for internal integrity. " - "This is meaningful in non-production builds only.")); - -ConfigVariableInt egg_recursion_limit -("egg-recursion-limit", 1000, - PRC_DESC("The maximum number of levels that recursive algorithms within " - "the egg library are allowed to traverse. This is a simple hack " - "to prevent deeply-recursive algorithms from triggering a stack " - "overflow. Set it larger to run more efficiently if your stack " - "allows it; set it lower if you experience stack overflows.")); - -ConfigVariableInt egg_precision -("egg-precision", 15, - PRC_DESC("The number of digits of precision to write out for values in " - "an egg file. Leave this at 0 to use the default setting for the " - "stream.")); - -//////////////////////////////////////////////////////////////////// -// Function: init_libegg -// Description: Initializes the library. This must be called at -// least once before any of the functions or classes in -// this library can be used. Normally it will be -// called by the static initializers and need not be -// called explicitly, but special cases exist. -//////////////////////////////////////////////////////////////////// -void -init_libegg() { - static bool initialized = false; - if (initialized) { - return; - } - initialized = true; - - EggRenderMode::init_type(); - EggAnimData::init_type(); - EggAnimPreload::init_type(); - EggAttributes::init_type(); - EggBin::init_type(); - EggBinMaker::init_type(); - EggComment::init_type(); - EggCompositePrimitive::init_type(); - EggCoordinateSystem::init_type(); - EggCurve::init_type(); - EggData::init_type(); - EggExternalReference::init_type(); - EggFilenameNode::init_type(); - EggGroup::init_type(); - EggGroupNode::init_type(); - EggGroupUniquifier::init_type(); - EggLine::init_type(); - EggMaterial::init_type(); - EggNameUniquifier::init_type(); - EggNamedObject::init_type(); - EggNode::init_type(); - EggNurbsCurve::init_type(); - EggNurbsSurface::init_type(); - EggObject::init_type(); - EggPatch::init_type(); - EggPoint::init_type(); - EggPolygon::init_type(); - EggPolysetMaker::init_type(); - EggPoolUniquifier::init_type(); - EggPrimitive::init_type(); - EggSAnimData::init_type(); - EggSurface::init_type(); - EggSwitchCondition::init_type(); - EggSwitchConditionDistance::init_type(); - EggTable::init_type(); - EggTexture::init_type(); - EggTriangleFan::init_type(); - EggTriangleStrip::init_type(); - EggUserData::init_type(); - EggVertex::init_type(); - EggVertexPool::init_type(); - EggVertexUV::init_type(); - EggVertexAux::init_type(); - EggXfmAnimData::init_type(); - EggXfmSAnim::init_type(); -} +// Filename: config_egg.cxx +// Created by: drose (19Mar00) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "config_egg.h" +#include "eggRenderMode.h" +#include "eggAnimData.h" +#include "eggAnimPreload.h" +#include "eggAttributes.h" +#include "eggBin.h" +#include "eggBinMaker.h" +#include "eggComment.h" +#include "eggCompositePrimitive.h" +#include "eggCoordinateSystem.h" +#include "eggCurve.h" +#include "eggExternalReference.h" +#include "eggFilenameNode.h" +#include "eggGroup.h" +#include "eggGroupNode.h" +#include "eggGroupUniquifier.h" +#include "eggLine.h" +#include "eggMaterial.h" +#include "eggNameUniquifier.h" +#include "eggNamedObject.h" +#include "eggNode.h" +#include "eggNurbsCurve.h" +#include "eggNurbsSurface.h" +#include "eggObject.h" +#include "eggPatch.h" +#include "eggPoint.h" +#include "eggPolygon.h" +#include "eggPolysetMaker.h" +#include "eggPoolUniquifier.h" +#include "eggPrimitive.h" +#include "eggSAnimData.h" +#include "eggSurface.h" +#include "eggSwitchCondition.h" +#include "eggTable.h" +#include "eggTexture.h" +#include "eggTriangleFan.h" +#include "eggTriangleStrip.h" +#include "eggUserData.h" +#include "eggVertex.h" +#include "eggVertexPool.h" +#include "eggVertexUV.h" +#include "eggVertexAux.h" +#include "eggXfmAnimData.h" +#include "eggXfmSAnim.h" + +#include "dconfig.h" + +Configure(config_egg); +NotifyCategoryDef(egg, ""); + +ConfigureFn(config_egg) { + init_libegg(); +} + +ConfigVariableBool egg_support_old_anims +("egg-support-old-anims", true, + PRC_DESC("Set this true to support loading of old character animation files, which " + "had the convention that the order \"phr\" implied a reversed roll.")); + +ConfigVariableBool egg_mesh +("egg-mesh", true, + PRC_DESC("Set this true to convert triangles and higher-order polygons " + "into triangle strips and triangle fans when an egg file is " + "loaded or converted to bam. Set this false just to triangulate " + "everything into independent triangles.")); + +ConfigVariableBool egg_retesselate_coplanar +("egg-retesselate-coplanar", false, + PRC_DESC("If this is true, the egg loader may reverse the " + "tesselation direction of a single pair of planar triangles that " + "share the same properties, if that will help get a better " + "triangle strip. In some rare cases, doing so can distort the " + "UV's on a face; turning this off should eliminate that artifact " + "(at the cost of less-effective triangle stripping).")); + +ConfigVariableBool egg_unroll_fans +("egg-unroll-fans", true, + PRC_DESC("Set this true to allow the egg loader to convert weak triangle " + "fans--triangles that share the same vertex but aren't " + "connected enough to justify making a triangle fan primitive " + "from them--into a series of zig-zag triangles that can make " + "a triangle strip that might connect better with its neighbors.")); + +ConfigVariableBool egg_show_tstrips +("egg-show-tstrips", false, + PRC_DESC("Set this true to color each triangle strip a random color, with " + "the leading triangle a little bit darker, so you can visually " + "observe the quality of the triangle stripping algorithm.")); + +ConfigVariableBool egg_show_qsheets +("egg-show-qsheets", false, + PRC_DESC("Set this true to color each quadsheet a random color, so you " + "can visually observe the quadsheet algorithm.")); + +ConfigVariableBool egg_show_quads +("egg-show-quads", false, + PRC_DESC("Set this true to color each detected quad a random color, so " + "you can visually observe the algorithm that unifies pairs of " + "triangles into quads (prior to generating triangle strips).")); + +ConfigVariableBool egg_subdivide_polys +("egg-subdivide-polys", true, + PRC_DESC("This is obsolete. In the old Geom implementation, it used to " + "be true to force higher-order polygons that were not otherwise " + "meshed to be subdivided into triangles. In the new " + "Geom implementation, this happens anyway.")); + +ConfigVariableBool egg_consider_fans +("egg-consider-fans", false, + PRC_DESC("Set this true to enable the egg mesher to consider making " + "triangle fans out of triangles that are connected at a common " + "vertex. This may help if your scene involves lots of such " + "connected triangles, but it can also make the overall stripping " + "less effective (by interfering with triangle strips).")); + +ConfigVariableDouble egg_max_tfan_angle +("egg-max-tfan-angle", 40.0, + PRC_DESC("The maximum average angle per triangle to allow in a triangle " + "fan. If triangles are larger than this--that is, more loosely " + "packed--then we figure a triangle strip is likely to do a " + "more effective job than a triangle fan, and the fan maker leaves " + "it alone.")); + +ConfigVariableInt egg_min_tfan_tris +("egg-min-tfan-tris", 4, + PRC_DESC("The minimum number of triangles that must be involved in order " + "to generate a triangle fan. Fewer than this is just interrupting " + "a triangle strip.")); + +ConfigVariableDouble egg_coplanar_threshold +("egg-coplanar-threshold", 0.01, + PRC_DESC("The numerical threshold below which polygons are considered " + "to be coplanar. Determined empirically.")); + +ConfigVariableInt egg_test_vref_integrity +("egg-test-vref-integrity", 20, + PRC_DESC("The maximum number of vertices a primitive may have before " + "its vertices will no longer be checked for internal integrity. " + "This is meaningful in non-production builds only.")); + +ConfigVariableInt egg_recursion_limit +("egg-recursion-limit", 1000, + PRC_DESC("The maximum number of levels that recursive algorithms within " + "the egg library are allowed to traverse. This is a simple hack " + "to prevent deeply-recursive algorithms from triggering a stack " + "overflow. Set it larger to run more efficiently if your stack " + "allows it; set it lower if you experience stack overflows.")); + +ConfigVariableInt egg_precision +("egg-precision", 15, + PRC_DESC("The number of digits of precision to write out for values in " + "an egg file. Leave this at 0 to use the default setting for the " + "stream.")); + +//////////////////////////////////////////////////////////////////// +// Function: init_libegg +// Description: Initializes the library. This must be called at +// least once before any of the functions or classes in +// this library can be used. Normally it will be +// called by the static initializers and need not be +// called explicitly, but special cases exist. +//////////////////////////////////////////////////////////////////// +void +init_libegg() { + static bool initialized = false; + if (initialized) { + return; + } + initialized = true; + + EggRenderMode::init_type(); + EggAnimData::init_type(); + EggAnimPreload::init_type(); + EggAttributes::init_type(); + EggBin::init_type(); + EggBinMaker::init_type(); + EggComment::init_type(); + EggCompositePrimitive::init_type(); + EggCoordinateSystem::init_type(); + EggCurve::init_type(); + EggData::init_type(); + EggExternalReference::init_type(); + EggFilenameNode::init_type(); + EggGroup::init_type(); + EggGroupNode::init_type(); + EggGroupUniquifier::init_type(); + EggLine::init_type(); + EggMaterial::init_type(); + EggNameUniquifier::init_type(); + EggNamedObject::init_type(); + EggNode::init_type(); + EggNurbsCurve::init_type(); + EggNurbsSurface::init_type(); + EggObject::init_type(); + EggPatch::init_type(); + EggPoint::init_type(); + EggPolygon::init_type(); + EggPolysetMaker::init_type(); + EggPoolUniquifier::init_type(); + EggPrimitive::init_type(); + EggSAnimData::init_type(); + EggSurface::init_type(); + EggSwitchCondition::init_type(); + EggSwitchConditionDistance::init_type(); + EggTable::init_type(); + EggTexture::init_type(); + EggTriangleFan::init_type(); + EggTriangleStrip::init_type(); + EggUserData::init_type(); + EggVertex::init_type(); + EggVertexPool::init_type(); + EggVertexUV::init_type(); + EggVertexAux::init_type(); + EggXfmAnimData::init_type(); + EggXfmSAnim::init_type(); +} diff --git a/panda/src/egg/config_egg.h b/panda/src/egg/config_egg.h index dbd47ab3b8..20119cd07c 100644 --- a/panda/src/egg/config_egg.h +++ b/panda/src/egg/config_egg.h @@ -1,48 +1,48 @@ -// Filename: config_egg.h -// Created by: drose (19Mar00) -// -//////////////////////////////////////////////////////////////////// -// -// 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 CONFIG_EGG_H -#define CONFIG_EGG_H - -#include "pandabase.h" -#include "notifyCategoryProxy.h" -#include "configVariableSearchPath.h" -#include "configVariableBool.h" -#include "configVariableEnum.h" -#include "configVariableDouble.h" -#include "configVariableInt.h" - -NotifyCategoryDecl(egg, EXPCL_PANDAEGG, EXPTP_PANDAEGG); - -extern ConfigVariableBool egg_support_old_anims; - -extern EXPCL_PANDAEGG ConfigVariableBool egg_mesh; -extern EXPCL_PANDAEGG ConfigVariableBool egg_retesselate_coplanar; -extern EXPCL_PANDAEGG ConfigVariableBool egg_unroll_fans; -extern EXPCL_PANDAEGG ConfigVariableBool egg_show_tstrips; -extern EXPCL_PANDAEGG ConfigVariableBool egg_show_qsheets; -extern EXPCL_PANDAEGG ConfigVariableBool egg_show_quads; -#define egg_false_color (egg_show_tstrips | egg_show_qsheets | egg_show_quads) -extern EXPCL_PANDAEGG ConfigVariableBool egg_subdivide_polys; -extern EXPCL_PANDAEGG ConfigVariableBool egg_consider_fans; -extern EXPCL_PANDAEGG ConfigVariableDouble egg_max_tfan_angle; -extern EXPCL_PANDAEGG ConfigVariableInt egg_min_tfan_tris; -extern EXPCL_PANDAEGG ConfigVariableDouble egg_coplanar_threshold; -extern EXPCL_PANDAEGG ConfigVariableInt egg_test_vref_integrity; -extern EXPCL_PANDAEGG ConfigVariableInt egg_recursion_limit; -extern EXPCL_PANDAEGG ConfigVariableInt egg_precision; - -extern EXPCL_PANDAEGG void init_libegg(); - -#endif +// Filename: config_egg.h +// Created by: drose (19Mar00) +// +//////////////////////////////////////////////////////////////////// +// +// 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 CONFIG_EGG_H +#define CONFIG_EGG_H + +#include "pandabase.h" +#include "notifyCategoryProxy.h" +#include "configVariableSearchPath.h" +#include "configVariableBool.h" +#include "configVariableEnum.h" +#include "configVariableDouble.h" +#include "configVariableInt.h" + +NotifyCategoryDecl(egg, EXPCL_PANDAEGG, EXPTP_PANDAEGG); + +extern ConfigVariableBool egg_support_old_anims; + +extern EXPCL_PANDAEGG ConfigVariableBool egg_mesh; +extern EXPCL_PANDAEGG ConfigVariableBool egg_retesselate_coplanar; +extern EXPCL_PANDAEGG ConfigVariableBool egg_unroll_fans; +extern EXPCL_PANDAEGG ConfigVariableBool egg_show_tstrips; +extern EXPCL_PANDAEGG ConfigVariableBool egg_show_qsheets; +extern EXPCL_PANDAEGG ConfigVariableBool egg_show_quads; +#define egg_false_color (egg_show_tstrips | egg_show_qsheets | egg_show_quads) +extern EXPCL_PANDAEGG ConfigVariableBool egg_subdivide_polys; +extern EXPCL_PANDAEGG ConfigVariableBool egg_consider_fans; +extern EXPCL_PANDAEGG ConfigVariableDouble egg_max_tfan_angle; +extern EXPCL_PANDAEGG ConfigVariableInt egg_min_tfan_tris; +extern EXPCL_PANDAEGG ConfigVariableDouble egg_coplanar_threshold; +extern EXPCL_PANDAEGG ConfigVariableInt egg_test_vref_integrity; +extern EXPCL_PANDAEGG ConfigVariableInt egg_recursion_limit; +extern EXPCL_PANDAEGG ConfigVariableInt egg_precision; + +extern EXPCL_PANDAEGG void init_libegg(); + +#endif diff --git a/panda/src/egg/eggData.cxx b/panda/src/egg/eggData.cxx index 71e6fd1424..da891bfb9e 100644 --- a/panda/src/egg/eggData.cxx +++ b/panda/src/egg/eggData.cxx @@ -1,414 +1,414 @@ -// Filename: eggData.cxx -// Created by: drose (20Jan99) -// -//////////////////////////////////////////////////////////////////// -// -// 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 "eggData.h" -#include "eggCoordinateSystem.h" -#include "eggTextureCollection.h" -#include "eggMaterialCollection.h" -#include "eggComment.h" -#include "eggPoolUniquifier.h" -#include "config_egg.h" -#include "config_util.h" -#include "config_express.h" -#include "string_utils.h" -#include "dSearchPath.h" -#include "virtualFileSystem.h" -#include "lightMutexHolder.h" -#include "zStream.h" - -extern int eggyyparse(); -#include "parserDefs.h" -#include "lexerDefs.h" - -TypeHandle EggData::_type_handle; - -//////////////////////////////////////////////////////////////////// -// Function: EggData::resolve_egg_filename -// Access: Public, Static -// Description: Looks for the indicated filename, first along the -// indicated searchpath, and then along the model_path. -// If found, updates the filename to the full path and -// returns true; otherwise, returns false. -//////////////////////////////////////////////////////////////////// -bool EggData:: -resolve_egg_filename(Filename &egg_filename, const DSearchPath &searchpath) { - VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - - if (egg_filename.is_fully_qualified() && vfs->exists(egg_filename)) { - return true; - } - - vfs->resolve_filename(egg_filename, searchpath, "egg") || - vfs->resolve_filename(egg_filename, get_model_path(), "egg"); - - return vfs->exists(egg_filename); -} - -//////////////////////////////////////////////////////////////////// -// Function: EggData::read -// Access: Public -// Description: Opens the indicated filename and reads the egg data -// contents from it. Returns true if the file was -// successfully opened and read, false if there were -// some errors, in which case the data may be partially -// read. -// -// error is the output stream to which to write error -// messages. -//////////////////////////////////////////////////////////////////// -bool EggData:: -read(Filename filename, string display_name) { - filename.set_text(); - set_egg_filename(filename); - - if (display_name.empty()) { - display_name = filename; - } - - VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - - PT(VirtualFile) vfile = vfs->get_file(filename); - if (vfile == NULL) { - egg_cat.error() << "Could not find " << display_name << "\n"; - return false; - } - set_egg_timestamp(vfile->get_timestamp()); - - istream *file = vfile->open_read_file(true); - if (file == (istream *)NULL) { - egg_cat.error() << "Unable to open " << display_name << "\n"; - return false; - } - - egg_cat.info() - << "Reading " << display_name << "\n"; - - bool read_ok = read(*file); - vfile->close_read_file(file); - return read_ok; -} - - -//////////////////////////////////////////////////////////////////// -// Function: EggData::read -// Access: Public -// Description: Parses the egg syntax contained in the indicated -// input stream. Returns true if the stream was a -// completely valid egg file, false if there were some -// errors, in which case the data may be partially read. -// -// Before you call this routine, you should probably -// call set_egg_filename() to set the name of the egg -// file we're processing, if at all possible. If there -// is no such filename, you may set it to the empty -// string. -//////////////////////////////////////////////////////////////////// -bool EggData:: -read(istream &in) { - // First, dispense with any children we had previously. We will - // replace them with the new data. - clear(); - - // Create a temporary EggData structure to read into. We initialize - // it with a copy of ourselves, so that it will get our _coordsys - // value, if the user set it. - PT(EggData) data = new EggData(*this); - - int error_count; - { - LightMutexHolder holder(egg_lock); - egg_init_parser(in, get_egg_filename(), data, data); - eggyyparse(); - egg_cleanup_parser(); - error_count = egg_error_count(); - } - - data->post_read(); - - steal_children(*data); - (*this) = *data; - - return (error_count == 0); -} - -//////////////////////////////////////////////////////////////////// -// Function: EggData::merge -// Access: Public -// Description: Appends the other egg structure to the end of this -// one. The other egg structure is invalidated. -//////////////////////////////////////////////////////////////////// -void EggData:: -merge(EggData &other) { - if (get_coordinate_system() == CS_default) { - // If we haven't specified a coordinate system yet, we inherit the - // other one's. - set_coordinate_system(other.get_coordinate_system()); - - } else { - // Otherwise, the other one is forced into our coordinate system - // before we merge. - other.set_coordinate_system(get_coordinate_system()); - } - steal_children(other); -} - - -//////////////////////////////////////////////////////////////////// -// Function: EggData::load_externals -// Access: Public -// Description: Loads up all the egg files referenced by -// entries within the egg structure, and inserts their -// contents in place of the entries. Searches -// for files in the searchpath, if not found directly, -// and writes error messages to the indicated output -// stream. Returns true if all externals were loaded -// successfully, false otherwise. -//////////////////////////////////////////////////////////////////// -bool EggData:: -load_externals(const DSearchPath &searchpath) { - return - r_load_externals(searchpath, get_coordinate_system(), NULL); -} - -//////////////////////////////////////////////////////////////////// -// Function: EggData::load_externals -// Access: Public -// Description: Loads up all the egg files referenced by -// entries within the egg structure, and inserts their -// contents in place of the entries. Searches -// for files in the searchpath, if not found directly, -// and writes error messages to the indicated output -// stream. Returns true if all externals were loaded -// successfully, false otherwise. -//////////////////////////////////////////////////////////////////// -bool EggData:: -load_externals(const DSearchPath &searchpath, BamCacheRecord *record) { - return - r_load_externals(searchpath, get_coordinate_system(), record); -} - -//////////////////////////////////////////////////////////////////// -// Function: EggData::collapse_equivalent_textures -// Access: Public -// Description: Removes duplicate references to the same texture -// image with the same properties. Considers two -// texture references with identical properties, but -// different tref names, to be equivalent, and collapses -// them, choosing one tref name to keep arbitrarily. -// Returns the number of textures removed. -//////////////////////////////////////////////////////////////////// -int EggData:: -collapse_equivalent_textures() { - EggTextureCollection textures; - textures.find_used_textures(this); - return - textures.collapse_equivalent_textures(~EggTexture::E_tref_name, this); -} - -//////////////////////////////////////////////////////////////////// -// Function: EggData::collapse_equivalent_materials -// Access: Public -// Description: Removes duplicate references to the same material -// with the same properties. Considers two material -// references with identical properties, but different -// mref names, to be equivalent, and collapses them, -// choosing one mref name to keep arbitrarily. Returns -// the number of materials removed. -//////////////////////////////////////////////////////////////////// -int EggData:: -collapse_equivalent_materials() { - EggMaterialCollection materials; - materials.find_used_materials(this); - return - materials.collapse_equivalent_materials(~EggMaterial::E_mref_name, this); -} - -//////////////////////////////////////////////////////////////////// -// Function: EggData::write_egg -// Access: Public -// Description: The main interface for writing complete egg files. -//////////////////////////////////////////////////////////////////// -bool EggData:: -write_egg(Filename filename) { - VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - filename.set_text(); - vfs->delete_file(filename); - ostream *file = vfs->open_write_file(filename, true, true); - if (file == (ostream *)NULL) { - egg_cat.error() << "Unable to open " << filename << " for writing.\n"; - return false; - } - - bool wrote_ok = write_egg(*file); - vfs->close_write_file(file); - return wrote_ok; -} - -//////////////////////////////////////////////////////////////////// -// Function: EggData::write_egg -// Access: Public -// Description: The main interface for writing complete egg files. -//////////////////////////////////////////////////////////////////// -bool EggData:: -write_egg(ostream &out) { - pre_write(); - - if (egg_precision > 0) { - // Change the egg precision as requested. - streamsize orig_precision = out.precision(); - out.precision((streamsize)egg_precision); - write(out, 0); - out.precision(orig_precision); - } else { - // Use the stream default precision. - write(out, 0); - } - return true; -} - - -//////////////////////////////////////////////////////////////////// -// Function: EggData::set_coordinate_system -// Access: Public -// Description: Changes the coordinate system of the EggData. If the -// coordinate system was previously different, this may -// result in a conversion of the data. -//////////////////////////////////////////////////////////////////// -void EggData:: -set_coordinate_system(CoordinateSystem new_coordsys) { - if (new_coordsys == CS_default) { - new_coordsys = get_default_coordinate_system(); - } - if (new_coordsys != _coordsys && - (_coordsys != CS_default && _coordsys != CS_invalid)) { - // Time to convert the data. - LMatrix4d mat = LMatrix4d::convert_mat(_coordsys, new_coordsys); - LMatrix4d inv = LMatrix4d::convert_mat(new_coordsys, _coordsys); - - r_transform(mat, inv, new_coordsys); - r_transform_vertices(mat); - - // Now we have to update the under_flags to ensure that all the - // cached relative matrices are correct. - update_under(0); - } - - _coordsys = new_coordsys; -} - -//////////////////////////////////////////////////////////////////// -// Function: EggData::write -// Access: Protected, Virtual -// Description: Writes the egg data out to the indicated output -// stream. -//////////////////////////////////////////////////////////////////// -void EggData:: -write(ostream &out, int indent_level) const { - PT(EggCoordinateSystem) ecs = new EggCoordinateSystem(_coordsys); - ecs->write(out, indent_level); - EggGroupNode::write(out, indent_level); - out << flush; -} - - -//////////////////////////////////////////////////////////////////// -// Function: EggData::post_read -// Access: Private -// Description: Does whatever processing is appropriate after reading -// the data in from an egg file. -//////////////////////////////////////////////////////////////////// -void EggData:: -post_read() { - CoordinateSystem old_coordsys = _coordsys; - _coordsys = find_coordsys_entry(); - - if (_coordsys == CS_default) { - // If the egg file didn't contain a entry, - // assume it's Y-up, by convention. - _coordsys = CS_yup_right; - - } else if (_coordsys == CS_invalid) { - egg_cat.warning() - << "Contradictory entries encountered.\n"; - _coordsys = CS_yup_right; - } - - r_mark_coordsys(_coordsys); - - if (old_coordsys != CS_default) { - // Now if we had a previous definition, enforce it. This might - // convert the data to the given coordinate system. - set_coordinate_system(old_coordsys); - } - - // Fill this in before we automatically resolve pathnames. - _had_absolute_pathnames = has_absolute_pathnames(); - - if (get_auto_resolve_externals()) { - // Resolve filenames that are relative to the egg file. - DSearchPath dir; - dir.append_directory(get_egg_filename().get_dirname()); - resolve_filenames(dir); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: EggData::pre_write -// Access: Private -// Description: Does whatever processing is appropriate just before -// writing the data out to an egg file. This includes -// verifying that vertex pool names are unique, etc. -//////////////////////////////////////////////////////////////////// -void EggData:: -pre_write() { - // Pull out all of the texture definitions in the file and massage - // them a bit. - EggTextureCollection textures; - textures.extract_textures(this); - - // Remove any textures that aren't being used. - textures.remove_unused_textures(this); - - // Collapse out any textures that are completely equivalent. For - // this purpose, we consider two textures with identical properties - // but different tref names to be different. - textures.collapse_equivalent_textures(~0, this); - - // Make sure all of the textures have unique TRef names. - textures.uniquify_trefs(); - textures.sort_by_tref(); - - // Do the same thing with the materials. - EggMaterialCollection materials; - materials.extract_materials(this); - materials.remove_unused_materials(this); - materials.collapse_equivalent_materials(~0, this); - materials.uniquify_mrefs(); - materials.sort_by_mref(); - - // Now put them all back at the head of the file, after any initial - // comment records. - iterator ci = begin(); - while (ci != end() && (*ci)->is_of_type(EggComment::get_class_type())) { - ++ci; - } - textures.insert_textures(this, ci); - materials.insert_materials(this, ci); - - // Also make sure that the vertex pools are uniquely named. This - // also checks textures and materials, which is kind of redundant - // since we just did that, but we don't mind. - EggPoolUniquifier pu; - pu.uniquify(this); -} +// Filename: eggData.cxx +// Created by: drose (20Jan99) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "eggData.h" +#include "eggCoordinateSystem.h" +#include "eggTextureCollection.h" +#include "eggMaterialCollection.h" +#include "eggComment.h" +#include "eggPoolUniquifier.h" +#include "config_egg.h" +#include "config_util.h" +#include "config_express.h" +#include "string_utils.h" +#include "dSearchPath.h" +#include "virtualFileSystem.h" +#include "lightMutexHolder.h" +#include "zStream.h" + +extern int eggyyparse(); +#include "parserDefs.h" +#include "lexerDefs.h" + +TypeHandle EggData::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: EggData::resolve_egg_filename +// Access: Public, Static +// Description: Looks for the indicated filename, first along the +// indicated searchpath, and then along the model_path. +// If found, updates the filename to the full path and +// returns true; otherwise, returns false. +//////////////////////////////////////////////////////////////////// +bool EggData:: +resolve_egg_filename(Filename &egg_filename, const DSearchPath &searchpath) { + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + + if (egg_filename.is_fully_qualified() && vfs->exists(egg_filename)) { + return true; + } + + vfs->resolve_filename(egg_filename, searchpath, "egg") || + vfs->resolve_filename(egg_filename, get_model_path(), "egg"); + + return vfs->exists(egg_filename); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggData::read +// Access: Public +// Description: Opens the indicated filename and reads the egg data +// contents from it. Returns true if the file was +// successfully opened and read, false if there were +// some errors, in which case the data may be partially +// read. +// +// error is the output stream to which to write error +// messages. +//////////////////////////////////////////////////////////////////// +bool EggData:: +read(Filename filename, string display_name) { + filename.set_text(); + set_egg_filename(filename); + + if (display_name.empty()) { + display_name = filename; + } + + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + + PT(VirtualFile) vfile = vfs->get_file(filename); + if (vfile == NULL) { + egg_cat.error() << "Could not find " << display_name << "\n"; + return false; + } + set_egg_timestamp(vfile->get_timestamp()); + + istream *file = vfile->open_read_file(true); + if (file == (istream *)NULL) { + egg_cat.error() << "Unable to open " << display_name << "\n"; + return false; + } + + egg_cat.info() + << "Reading " << display_name << "\n"; + + bool read_ok = read(*file); + vfile->close_read_file(file); + return read_ok; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggData::read +// Access: Public +// Description: Parses the egg syntax contained in the indicated +// input stream. Returns true if the stream was a +// completely valid egg file, false if there were some +// errors, in which case the data may be partially read. +// +// Before you call this routine, you should probably +// call set_egg_filename() to set the name of the egg +// file we're processing, if at all possible. If there +// is no such filename, you may set it to the empty +// string. +//////////////////////////////////////////////////////////////////// +bool EggData:: +read(istream &in) { + // First, dispense with any children we had previously. We will + // replace them with the new data. + clear(); + + // Create a temporary EggData structure to read into. We initialize + // it with a copy of ourselves, so that it will get our _coordsys + // value, if the user set it. + PT(EggData) data = new EggData(*this); + + int error_count; + { + LightMutexHolder holder(egg_lock); + egg_init_parser(in, get_egg_filename(), data, data); + eggyyparse(); + egg_cleanup_parser(); + error_count = egg_error_count(); + } + + data->post_read(); + + steal_children(*data); + (*this) = *data; + + return (error_count == 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggData::merge +// Access: Public +// Description: Appends the other egg structure to the end of this +// one. The other egg structure is invalidated. +//////////////////////////////////////////////////////////////////// +void EggData:: +merge(EggData &other) { + if (get_coordinate_system() == CS_default) { + // If we haven't specified a coordinate system yet, we inherit the + // other one's. + set_coordinate_system(other.get_coordinate_system()); + + } else { + // Otherwise, the other one is forced into our coordinate system + // before we merge. + other.set_coordinate_system(get_coordinate_system()); + } + steal_children(other); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggData::load_externals +// Access: Public +// Description: Loads up all the egg files referenced by +// entries within the egg structure, and inserts their +// contents in place of the entries. Searches +// for files in the searchpath, if not found directly, +// and writes error messages to the indicated output +// stream. Returns true if all externals were loaded +// successfully, false otherwise. +//////////////////////////////////////////////////////////////////// +bool EggData:: +load_externals(const DSearchPath &searchpath) { + return + r_load_externals(searchpath, get_coordinate_system(), NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggData::load_externals +// Access: Public +// Description: Loads up all the egg files referenced by +// entries within the egg structure, and inserts their +// contents in place of the entries. Searches +// for files in the searchpath, if not found directly, +// and writes error messages to the indicated output +// stream. Returns true if all externals were loaded +// successfully, false otherwise. +//////////////////////////////////////////////////////////////////// +bool EggData:: +load_externals(const DSearchPath &searchpath, BamCacheRecord *record) { + return + r_load_externals(searchpath, get_coordinate_system(), record); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggData::collapse_equivalent_textures +// Access: Public +// Description: Removes duplicate references to the same texture +// image with the same properties. Considers two +// texture references with identical properties, but +// different tref names, to be equivalent, and collapses +// them, choosing one tref name to keep arbitrarily. +// Returns the number of textures removed. +//////////////////////////////////////////////////////////////////// +int EggData:: +collapse_equivalent_textures() { + EggTextureCollection textures; + textures.find_used_textures(this); + return + textures.collapse_equivalent_textures(~EggTexture::E_tref_name, this); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggData::collapse_equivalent_materials +// Access: Public +// Description: Removes duplicate references to the same material +// with the same properties. Considers two material +// references with identical properties, but different +// mref names, to be equivalent, and collapses them, +// choosing one mref name to keep arbitrarily. Returns +// the number of materials removed. +//////////////////////////////////////////////////////////////////// +int EggData:: +collapse_equivalent_materials() { + EggMaterialCollection materials; + materials.find_used_materials(this); + return + materials.collapse_equivalent_materials(~EggMaterial::E_mref_name, this); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggData::write_egg +// Access: Public +// Description: The main interface for writing complete egg files. +//////////////////////////////////////////////////////////////////// +bool EggData:: +write_egg(Filename filename) { + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + filename.set_text(); + vfs->delete_file(filename); + ostream *file = vfs->open_write_file(filename, true, true); + if (file == (ostream *)NULL) { + egg_cat.error() << "Unable to open " << filename << " for writing.\n"; + return false; + } + + bool wrote_ok = write_egg(*file); + vfs->close_write_file(file); + return wrote_ok; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggData::write_egg +// Access: Public +// Description: The main interface for writing complete egg files. +//////////////////////////////////////////////////////////////////// +bool EggData:: +write_egg(ostream &out) { + pre_write(); + + if (egg_precision > 0) { + // Change the egg precision as requested. + streamsize orig_precision = out.precision(); + out.precision((streamsize)egg_precision); + write(out, 0); + out.precision(orig_precision); + } else { + // Use the stream default precision. + write(out, 0); + } + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggData::set_coordinate_system +// Access: Public +// Description: Changes the coordinate system of the EggData. If the +// coordinate system was previously different, this may +// result in a conversion of the data. +//////////////////////////////////////////////////////////////////// +void EggData:: +set_coordinate_system(CoordinateSystem new_coordsys) { + if (new_coordsys == CS_default) { + new_coordsys = get_default_coordinate_system(); + } + if (new_coordsys != _coordsys && + (_coordsys != CS_default && _coordsys != CS_invalid)) { + // Time to convert the data. + LMatrix4d mat = LMatrix4d::convert_mat(_coordsys, new_coordsys); + LMatrix4d inv = LMatrix4d::convert_mat(new_coordsys, _coordsys); + + r_transform(mat, inv, new_coordsys); + r_transform_vertices(mat); + + // Now we have to update the under_flags to ensure that all the + // cached relative matrices are correct. + update_under(0); + } + + _coordsys = new_coordsys; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggData::write +// Access: Protected, Virtual +// Description: Writes the egg data out to the indicated output +// stream. +//////////////////////////////////////////////////////////////////// +void EggData:: +write(ostream &out, int indent_level) const { + PT(EggCoordinateSystem) ecs = new EggCoordinateSystem(_coordsys); + ecs->write(out, indent_level); + EggGroupNode::write(out, indent_level); + out << flush; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggData::post_read +// Access: Private +// Description: Does whatever processing is appropriate after reading +// the data in from an egg file. +//////////////////////////////////////////////////////////////////// +void EggData:: +post_read() { + CoordinateSystem old_coordsys = _coordsys; + _coordsys = find_coordsys_entry(); + + if (_coordsys == CS_default) { + // If the egg file didn't contain a entry, + // assume it's Y-up, by convention. + _coordsys = CS_yup_right; + + } else if (_coordsys == CS_invalid) { + egg_cat.warning() + << "Contradictory entries encountered.\n"; + _coordsys = CS_yup_right; + } + + r_mark_coordsys(_coordsys); + + if (old_coordsys != CS_default) { + // Now if we had a previous definition, enforce it. This might + // convert the data to the given coordinate system. + set_coordinate_system(old_coordsys); + } + + // Fill this in before we automatically resolve pathnames. + _had_absolute_pathnames = has_absolute_pathnames(); + + if (get_auto_resolve_externals()) { + // Resolve filenames that are relative to the egg file. + DSearchPath dir; + dir.append_directory(get_egg_filename().get_dirname()); + resolve_filenames(dir); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggData::pre_write +// Access: Private +// Description: Does whatever processing is appropriate just before +// writing the data out to an egg file. This includes +// verifying that vertex pool names are unique, etc. +//////////////////////////////////////////////////////////////////// +void EggData:: +pre_write() { + // Pull out all of the texture definitions in the file and massage + // them a bit. + EggTextureCollection textures; + textures.extract_textures(this); + + // Remove any textures that aren't being used. + textures.remove_unused_textures(this); + + // Collapse out any textures that are completely equivalent. For + // this purpose, we consider two textures with identical properties + // but different tref names to be different. + textures.collapse_equivalent_textures(~0, this); + + // Make sure all of the textures have unique TRef names. + textures.uniquify_trefs(); + textures.sort_by_tref(); + + // Do the same thing with the materials. + EggMaterialCollection materials; + materials.extract_materials(this); + materials.remove_unused_materials(this); + materials.collapse_equivalent_materials(~0, this); + materials.uniquify_mrefs(); + materials.sort_by_mref(); + + // Now put them all back at the head of the file, after any initial + // comment records. + iterator ci = begin(); + while (ci != end() && (*ci)->is_of_type(EggComment::get_class_type())) { + ++ci; + } + textures.insert_textures(this, ci); + materials.insert_materials(this, ci); + + // Also make sure that the vertex pools are uniquely named. This + // also checks textures and materials, which is kind of redundant + // since we just did that, but we don't mind. + EggPoolUniquifier pu; + pu.uniquify(this); +} diff --git a/pandatool/src/objegg/Sources.pp b/pandatool/src/objegg/Sources.pp index 7e372a8666..fb787d47bf 100644 --- a/pandatool/src/objegg/Sources.pp +++ b/pandatool/src/objegg/Sources.pp @@ -1,25 +1,25 @@ -#begin ss_lib_target - #define TARGET p3objegg - #define LOCAL_LIBS p3converter p3pandatoolbase - #define OTHER_LIBS \ - p3egg:c pandaegg:m \ - p3pipeline:c p3event:c p3pstatclient:c panda:m \ - p3pandabase:c p3pnmimage:c p3mathutil:c p3linmath:c p3putil:c p3express:c \ - p3interrogatedb:c p3prc:c p3dconfig:c p3dtoolconfig:m \ - p3dtoolutil:c p3dtoolbase:c p3dtool:m \ - $[if $[WANT_NATIVE_NET],p3nativenet:c] \ - $[if $[and $[HAVE_NET],$[WANT_NATIVE_NET]],p3net:c p3downloader:c] - - #define UNIX_SYS_LIBS \ - m - - #define SOURCES \ - config_objegg.cxx config_objegg.h \ - objToEggConverter.cxx objToEggConverter.h objToEggConverter.I \ - eggToObjConverter.cxx eggToObjConverter.h - - #define INSTALL_HEADERS \ - objToEggConverter.h objToEggConverter.I \ - eggToObjConverter.h - -#end ss_lib_target +#begin ss_lib_target + #define TARGET p3objegg + #define LOCAL_LIBS p3converter p3pandatoolbase + #define OTHER_LIBS \ + p3egg:c pandaegg:m \ + p3pipeline:c p3event:c p3pstatclient:c panda:m \ + p3pandabase:c p3pnmimage:c p3mathutil:c p3linmath:c p3putil:c p3express:c \ + p3interrogatedb:c p3prc:c p3dconfig:c p3dtoolconfig:m \ + p3dtoolutil:c p3dtoolbase:c p3dtool:m \ + $[if $[WANT_NATIVE_NET],p3nativenet:c] \ + $[if $[and $[HAVE_NET],$[WANT_NATIVE_NET]],p3net:c p3downloader:c] + + #define UNIX_SYS_LIBS \ + m + + #define SOURCES \ + config_objegg.cxx config_objegg.h \ + objToEggConverter.cxx objToEggConverter.h objToEggConverter.I \ + eggToObjConverter.cxx eggToObjConverter.h + + #define INSTALL_HEADERS \ + objToEggConverter.h objToEggConverter.I \ + eggToObjConverter.h + +#end ss_lib_target diff --git a/pandatool/src/objegg/eggToObjConverter.cxx b/pandatool/src/objegg/eggToObjConverter.cxx index 193782ff00..314785a83d 100644 --- a/pandatool/src/objegg/eggToObjConverter.cxx +++ b/pandatool/src/objegg/eggToObjConverter.cxx @@ -1,462 +1,462 @@ -// Filename: eggToObjConverter.cxx -// Created by: drose (19Dec12) -// -//////////////////////////////////////////////////////////////////// -// -// 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 "eggToObjConverter.h" -#include "config_objegg.h" -#include "config_egg.h" -#include "eggData.h" -#include "string_utils.h" -#include "streamReader.h" -#include "virtualFileSystem.h" -#include "eggPolygon.h" -#include "eggPoint.h" -#include "eggLine.h" -#include "dcast.h" - -//////////////////////////////////////////////////////////////////// -// Function: EggToObjConverter::Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -EggToObjConverter:: -EggToObjConverter() { -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObjConverter::Copy Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -EggToObjConverter:: -EggToObjConverter(const EggToObjConverter ©) : - EggToSomethingConverter(copy) -{ -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObjConverter::Destructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -EggToObjConverter:: -~EggToObjConverter() { -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObjConverter::make_copy -// Access: Public, Virtual -// Description: Allocates and returns a new copy of the converter. -//////////////////////////////////////////////////////////////////// -EggToSomethingConverter *EggToObjConverter:: -make_copy() { - return new EggToObjConverter(*this); -} - - -//////////////////////////////////////////////////////////////////// -// Function: EggToObjConverter::get_name -// Access: Public, Virtual -// Description: Returns the English name of the file type this -// converter supports. -//////////////////////////////////////////////////////////////////// -string EggToObjConverter:: -get_name() const { - return "obj"; -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObjConverter::get_extension -// Access: Public, Virtual -// Description: Returns the common extension of the file type this -// converter supports. -//////////////////////////////////////////////////////////////////// -string EggToObjConverter:: -get_extension() const { - return "obj"; -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObjConverter::supports_compressed -// Access: Published, Virtual -// Description: Returns true if this file type can transparently save -// compressed files (with a .pz extension), false -// otherwise. -//////////////////////////////////////////////////////////////////// -bool EggToObjConverter:: -supports_compressed() const { - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObjConverter::write_file -// Access: Public, Virtual -// Description: Handles the conversion of the internal EggData to the -// target file format, written to the specified -// filename. -//////////////////////////////////////////////////////////////////// -bool EggToObjConverter:: -write_file(const Filename &filename) { - clear_error(); - - if (_egg_data->get_coordinate_system() == CS_default) { - _egg_data->set_coordinate_system(CS_zup_right); - } - - if (!process(filename)) { - _error = true; - } - return !had_error(); -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObjConverter::process -// Access: Private -// Description: -//////////////////////////////////////////////////////////////////// -bool EggToObjConverter:: -process(const Filename &filename) { - _egg_data->flatten_transforms(); - collect_vertices(_egg_data); - - VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - Filename obj_filename = Filename::text_filename(filename); - vfs->delete_file(obj_filename); - ostream *file = vfs->open_write_file(obj_filename, true, true); - if (file == (ostream *)NULL) { - return false; - } - if (egg_precision != 0) { - file->precision(egg_precision); - } - - _current_group = NULL; - - /* - (*file) << "\n#\n" - << "# obj file generated by the following command:\n" - << "# " << get_exec_command() << "\n" - << "#\n\n"; - */ - - write_vertices(*file, "v", 3, _unique_vert3); - write_vertices(*file, "v", 4, _unique_vert4); - write_vertices(*file, "vt", 2, _unique_uv2); - write_vertices(*file, "vt", 3, _unique_uv3); - write_vertices(*file, "vn", 3, _unique_norm); - - write_faces(*file, _egg_data); - - bool success = (file != (ostream *)NULL); - vfs->close_write_file(file); - - return success; -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObjConverter::collect_vertices -// Access: Private -// Description: Recursively walks the egg structure, looking for -// vertices referenced by polygons or points. Any such -// vertices are added to the vertex tables for writing -// to the obj file. -//////////////////////////////////////////////////////////////////// -void EggToObjConverter:: -collect_vertices(EggNode *egg_node) { - if (egg_node->is_of_type(EggPrimitive::get_class_type())) { - EggPrimitive *egg_prim = DCAST(EggPrimitive, egg_node); - EggPrimitive::iterator pi; - for (pi = egg_prim->begin(); pi != egg_prim->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: EggToObjConverter::write_faces -// Access: Private -// Description: Recursively walks the egg structure again, this time -// writing out the face records for any polygons, -// points, or lines encountered. -//////////////////////////////////////////////////////////////////// -void EggToObjConverter:: -write_faces(ostream &out, EggNode *egg_node) { - if (egg_node->is_of_type(EggPrimitive::get_class_type())) { - const char *prim_type = NULL; - if (egg_node->is_of_type(EggPolygon::get_class_type())) { - prim_type = "f"; - } else if (egg_node->is_of_type(EggPoint::get_class_type())) { - prim_type = "p"; - } else if (egg_node->is_of_type(EggLine::get_class_type())) { - prim_type = "l"; - } - - if (prim_type != NULL) { - write_group_reference(out, egg_node); - - EggPrimitive *egg_prim = DCAST(EggPrimitive, egg_node); - - out << prim_type; - EggPrimitive::iterator pi; - for (pi = egg_prim->begin(); pi != egg_prim->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: EggToObjConverter::write_group_reference -// Access: Private -// Description: Writes the "g" tag to describe this polygon's group, -// if needed. -//////////////////////////////////////////////////////////////////// -void EggToObjConverter:: -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: EggToObjConverter::get_group_name -// Access: Private -// Description: Recursively determines the appropriate string to -// write for the "g" tag to describe a particular -// EggGroupNode. -//////////////////////////////////////////////////////////////////// -void EggToObjConverter:: -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: EggToObjConverter::record_vertex -// Access: Private -// Description: Adds the indicated EggVertex to the unique vertex -// tables, for writing later by write_vertices(). -//////////////////////////////////////////////////////////////////// -void EggToObjConverter:: -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: EggToObjConverter::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 EggToObjConverter:: -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: EggToObjConverter::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 EggToObjConverter:: -record_unique(UniqueVertices &unique, const LVecBase3d &vec) { - return record_unique(unique, LVecBase4d(vec[0], vec[1], vec[2], 0.0)); -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObjConverter::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 EggToObjConverter:: -record_unique(UniqueVertices &unique, const LVecBase2d &vec) { - return record_unique(unique, LVecBase4d(vec[0], vec[1], 0.0, 0.0)); -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObjConverter::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 EggToObjConverter:: -record_unique(UniqueVertices &unique, double pos) { - return record_unique(unique, LVecBase4d(pos, 0.0, 0.0, 0.0)); -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObjConverter::write_vertices -// Access: Private -// Description: Actually writes the vertex values recorded in the -// indicated table to the obj output stream. -//////////////////////////////////////////////////////////////////// -void EggToObjConverter:: -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 **)PANDA_MALLOC_ARRAY(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"; - } - PANDA_FREE_ARRAY(vertices); -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObjConverter::VertexDef::Constructor -// Access: Private -// Description: -//////////////////////////////////////////////////////////////////// -EggToObjConverter::VertexDef:: -VertexDef() : - _vert3_index(-1), - _vert4_index(-1), - _uv2_index(-1), - _uv3_index(-1), - _norm_index(-1) -{ -} +// Filename: eggToObjConverter.cxx +// Created by: drose (19Dec12) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "eggToObjConverter.h" +#include "config_objegg.h" +#include "config_egg.h" +#include "eggData.h" +#include "string_utils.h" +#include "streamReader.h" +#include "virtualFileSystem.h" +#include "eggPolygon.h" +#include "eggPoint.h" +#include "eggLine.h" +#include "dcast.h" + +//////////////////////////////////////////////////////////////////// +// Function: EggToObjConverter::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggToObjConverter:: +EggToObjConverter() { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObjConverter::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggToObjConverter:: +EggToObjConverter(const EggToObjConverter ©) : + EggToSomethingConverter(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObjConverter::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggToObjConverter:: +~EggToObjConverter() { +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObjConverter::make_copy +// Access: Public, Virtual +// Description: Allocates and returns a new copy of the converter. +//////////////////////////////////////////////////////////////////// +EggToSomethingConverter *EggToObjConverter:: +make_copy() { + return new EggToObjConverter(*this); +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggToObjConverter::get_name +// Access: Public, Virtual +// Description: Returns the English name of the file type this +// converter supports. +//////////////////////////////////////////////////////////////////// +string EggToObjConverter:: +get_name() const { + return "obj"; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObjConverter::get_extension +// Access: Public, Virtual +// Description: Returns the common extension of the file type this +// converter supports. +//////////////////////////////////////////////////////////////////// +string EggToObjConverter:: +get_extension() const { + return "obj"; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObjConverter::supports_compressed +// Access: Published, Virtual +// Description: Returns true if this file type can transparently save +// compressed files (with a .pz extension), false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool EggToObjConverter:: +supports_compressed() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObjConverter::write_file +// Access: Public, Virtual +// Description: Handles the conversion of the internal EggData to the +// target file format, written to the specified +// filename. +//////////////////////////////////////////////////////////////////// +bool EggToObjConverter:: +write_file(const Filename &filename) { + clear_error(); + + if (_egg_data->get_coordinate_system() == CS_default) { + _egg_data->set_coordinate_system(CS_zup_right); + } + + if (!process(filename)) { + _error = true; + } + return !had_error(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObjConverter::process +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +bool EggToObjConverter:: +process(const Filename &filename) { + _egg_data->flatten_transforms(); + collect_vertices(_egg_data); + + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + Filename obj_filename = Filename::text_filename(filename); + vfs->delete_file(obj_filename); + ostream *file = vfs->open_write_file(obj_filename, true, true); + if (file == (ostream *)NULL) { + return false; + } + if (egg_precision != 0) { + file->precision(egg_precision); + } + + _current_group = NULL; + + /* + (*file) << "\n#\n" + << "# obj file generated by the following command:\n" + << "# " << get_exec_command() << "\n" + << "#\n\n"; + */ + + write_vertices(*file, "v", 3, _unique_vert3); + write_vertices(*file, "v", 4, _unique_vert4); + write_vertices(*file, "vt", 2, _unique_uv2); + write_vertices(*file, "vt", 3, _unique_uv3); + write_vertices(*file, "vn", 3, _unique_norm); + + write_faces(*file, _egg_data); + + bool success = (file != (ostream *)NULL); + vfs->close_write_file(file); + + return success; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObjConverter::collect_vertices +// Access: Private +// Description: Recursively walks the egg structure, looking for +// vertices referenced by polygons or points. Any such +// vertices are added to the vertex tables for writing +// to the obj file. +//////////////////////////////////////////////////////////////////// +void EggToObjConverter:: +collect_vertices(EggNode *egg_node) { + if (egg_node->is_of_type(EggPrimitive::get_class_type())) { + EggPrimitive *egg_prim = DCAST(EggPrimitive, egg_node); + EggPrimitive::iterator pi; + for (pi = egg_prim->begin(); pi != egg_prim->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: EggToObjConverter::write_faces +// Access: Private +// Description: Recursively walks the egg structure again, this time +// writing out the face records for any polygons, +// points, or lines encountered. +//////////////////////////////////////////////////////////////////// +void EggToObjConverter:: +write_faces(ostream &out, EggNode *egg_node) { + if (egg_node->is_of_type(EggPrimitive::get_class_type())) { + const char *prim_type = NULL; + if (egg_node->is_of_type(EggPolygon::get_class_type())) { + prim_type = "f"; + } else if (egg_node->is_of_type(EggPoint::get_class_type())) { + prim_type = "p"; + } else if (egg_node->is_of_type(EggLine::get_class_type())) { + prim_type = "l"; + } + + if (prim_type != NULL) { + write_group_reference(out, egg_node); + + EggPrimitive *egg_prim = DCAST(EggPrimitive, egg_node); + + out << prim_type; + EggPrimitive::iterator pi; + for (pi = egg_prim->begin(); pi != egg_prim->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: EggToObjConverter::write_group_reference +// Access: Private +// Description: Writes the "g" tag to describe this polygon's group, +// if needed. +//////////////////////////////////////////////////////////////////// +void EggToObjConverter:: +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: EggToObjConverter::get_group_name +// Access: Private +// Description: Recursively determines the appropriate string to +// write for the "g" tag to describe a particular +// EggGroupNode. +//////////////////////////////////////////////////////////////////// +void EggToObjConverter:: +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: EggToObjConverter::record_vertex +// Access: Private +// Description: Adds the indicated EggVertex to the unique vertex +// tables, for writing later by write_vertices(). +//////////////////////////////////////////////////////////////////// +void EggToObjConverter:: +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: EggToObjConverter::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 EggToObjConverter:: +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: EggToObjConverter::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 EggToObjConverter:: +record_unique(UniqueVertices &unique, const LVecBase3d &vec) { + return record_unique(unique, LVecBase4d(vec[0], vec[1], vec[2], 0.0)); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObjConverter::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 EggToObjConverter:: +record_unique(UniqueVertices &unique, const LVecBase2d &vec) { + return record_unique(unique, LVecBase4d(vec[0], vec[1], 0.0, 0.0)); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObjConverter::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 EggToObjConverter:: +record_unique(UniqueVertices &unique, double pos) { + return record_unique(unique, LVecBase4d(pos, 0.0, 0.0, 0.0)); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObjConverter::write_vertices +// Access: Private +// Description: Actually writes the vertex values recorded in the +// indicated table to the obj output stream. +//////////////////////////////////////////////////////////////////// +void EggToObjConverter:: +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 **)PANDA_MALLOC_ARRAY(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"; + } + PANDA_FREE_ARRAY(vertices); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObjConverter::VertexDef::Constructor +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +EggToObjConverter::VertexDef:: +VertexDef() : + _vert3_index(-1), + _vert4_index(-1), + _uv2_index(-1), + _uv3_index(-1), + _norm_index(-1) +{ +} diff --git a/pandatool/src/objegg/objToEggConverter.cxx b/pandatool/src/objegg/objToEggConverter.cxx index 0db9464e5a..66949fe10f 100644 --- a/pandatool/src/objegg/objToEggConverter.cxx +++ b/pandatool/src/objegg/objToEggConverter.cxx @@ -1,1188 +1,1188 @@ -// Filename: objToEggConverter.cxx -// Created by: drose (07Dec10) -// -//////////////////////////////////////////////////////////////////// -// -// 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 "objToEggConverter.h" -#include "config_objegg.h" -#include "eggData.h" -#include "string_utils.h" -#include "streamReader.h" -#include "virtualFileSystem.h" -#include "eggPolygon.h" -#include "nodePath.h" -#include "geomTriangles.h" -#include "geomPoints.h" -#include "colorAttrib.h" -#include "shadeModelAttrib.h" -#include "dcast.h" -#include "triangulator3.h" -#include "config_egg2pg.h" - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -ObjToEggConverter:: -ObjToEggConverter() { -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::Copy Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -ObjToEggConverter:: -ObjToEggConverter(const ObjToEggConverter ©) : - SomethingToEggConverter(copy) -{ -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::Destructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -ObjToEggConverter:: -~ObjToEggConverter() { -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::make_copy -// Access: Public, Virtual -// Description: Allocates and returns a new copy of the converter. -//////////////////////////////////////////////////////////////////// -SomethingToEggConverter *ObjToEggConverter:: -make_copy() { - return new ObjToEggConverter(*this); -} - - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::get_name -// Access: Public, Virtual -// Description: Returns the English name of the file type this -// converter supports. -//////////////////////////////////////////////////////////////////// -string ObjToEggConverter:: -get_name() const { - return "obj"; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::get_extension -// Access: Public, Virtual -// Description: Returns the common extension of the file type this -// converter supports. -//////////////////////////////////////////////////////////////////// -string ObjToEggConverter:: -get_extension() const { - return "obj"; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::supports_compressed -// Access: Published, Virtual -// Description: Returns true if this file type can transparently load -// compressed files (with a .pz extension), false -// otherwise. -//////////////////////////////////////////////////////////////////// -bool ObjToEggConverter:: -supports_compressed() const { - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::supports_convert_to_node -// Access: Published, Virtual -// Description: Returns true if this converter can directly convert -// the model type to internal Panda memory structures, -// given the indicated options, or false otherwise. If -// this returns true, then convert_to_node() may be -// called to perform the conversion, which may be faster -// than calling convert_file() if the ultimate goal is a -// PandaNode anyway. -//////////////////////////////////////////////////////////////////// -bool ObjToEggConverter:: -supports_convert_to_node(const LoaderOptions &options) const { - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::convert_file -// Access: Public, Virtual -// Description: Handles the reading of the input file and converting -// it to egg. Returns true if successful, false -// otherwise. -//////////////////////////////////////////////////////////////////// -bool ObjToEggConverter:: -convert_file(const Filename &filename) { - clear_error(); - - if (_egg_data->get_coordinate_system() == CS_default) { - _egg_data->set_coordinate_system(CS_zup_right); - } - - if (!process(filename)) { - _error = true; - } - return !had_error(); -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::convert_to_node -// Access: Public, Virtual -// Description: Reads the input file and directly produces a -// ready-to-render model file as a PandaNode. Returns -// NULL on failure, or if it is not supported. (This -// functionality is not supported by all converter -// types; see supports_convert_to_node()). -//////////////////////////////////////////////////////////////////// -PT(PandaNode) ObjToEggConverter:: -convert_to_node(const LoaderOptions &options, const Filename &filename) { - clear_error(); - - _root_node = new PandaNode(""); - _current_vertex_data = new VertexData(_root_node, "root"); - - if (!process_node(filename)) { - _error = true; - } - - _current_vertex_data->close_geom(this); - delete _current_vertex_data; - - if (had_error()) { - return NULL; - } - - return _root_node; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::process -// Access: Protected -// Description: Reads the file and converts it to egg structures. -//////////////////////////////////////////////////////////////////// -bool ObjToEggConverter:: -process(const Filename &filename) { - VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - istream *strm = vfs->open_read_file(filename, true); - if (strm == NULL) { - objegg_cat.error() - << "Couldn't read " << filename << "\n"; - return false; - } - - _v_table.clear(); - _vn_table.clear(); - _rgb_table.clear(); - _vt_table.clear(); - _xvt_table.clear(); - _ref_plane_res.set(1.0, 1.0); - _v4_given = false; - _vt3_given = false; - _f_given = false; - - _vpool = new EggVertexPool("vpool"); - _egg_data->add_child(_vpool); - _root_group = new EggGroup("root"); - _egg_data->add_child(_root_group); - _current_group = _root_group; - - StreamReader sr(strm, true); - string line = sr.readline(); - _line_number = 1; - while (!line.empty()) { - line = trim(line); - if (line.empty()) { - line = sr.readline(); - continue; - } - - while (line[line.length() - 1] == '\\') { - // If it ends on a backslash, it's a continuation character. - string line2 = sr.readline(); - ++_line_number; - if (line2.empty()) { - break; - } - line = line.substr(0, line.length() - 1) + trim(line2); - } - - if (line.substr(0, 15) == "#_ref_plane_res") { - process_ref_plane_res(line); - line = sr.readline(); - continue; - } - - if (line[0] == '#') { - line = sr.readline(); - continue; - } - - if (!process_line(line)) { - return false; - } - line = sr.readline(); - ++_line_number; - } - - if (!_f_given) { - generate_egg_points(); - } - - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::process_line -// Access: Protected -// Description: -//////////////////////////////////////////////////////////////////// -bool ObjToEggConverter:: -process_line(const string &line) { - vector_string words; - tokenize(line, words, " \t", true); - nassertr(!words.empty(), false); - - string tag = words[0]; - if (tag == "v") { - return process_v(words); - } else if (tag == "vt") { - return process_vt(words); - } else if (tag == "xvt") { - return process_xvt(words); - } else if (tag == "xvc") { - return process_xvc(words); - } else if (tag == "vn") { - return process_vn(words); - } else if (tag == "f") { - return process_f(words); - } else if (tag == "g") { - return process_g(words); - } else { - bool inserted = _ignored_tags.insert(tag).second; - if (inserted) { - objegg_cat.info() - << "Ignoring tag " << tag << "\n"; - } - } - - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::process_line -// Access: Protected -// Description: -//////////////////////////////////////////////////////////////////// -bool ObjToEggConverter:: -process_ref_plane_res(const string &line) { - // the #_ref_plane_res line is a DRZ extension that defines the - // pixel resolution of the projector device. It's needed to - // properly scale the xvt lines. - - vector_string words; - tokenize(line, words, " \t", true); - nassertr(!words.empty(), false); - - if (words.size() != 3) { - objegg_cat.error() - << "Wrong number of tokens at line " << _line_number << "\n"; - return false; - } - - bool okflag = true; - LPoint3d pos; - okflag &= string_to_double(words[1], _ref_plane_res[0]); - okflag &= string_to_double(words[2], _ref_plane_res[1]); - - if (!okflag) { - objegg_cat.error() - << "Invalid number at line " << _line_number << ":\n"; - return false; - } - - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::process_v -// Access: Protected -// Description: -//////////////////////////////////////////////////////////////////// -bool ObjToEggConverter:: -process_v(vector_string &words) { - if (words.size() != 4 && words.size() != 5 && - words.size() != 7 && words.size() != 8) { - objegg_cat.error() - << "Wrong number of tokens at line " << _line_number << "\n"; - return false; - } - - bool okflag = true; - LPoint4d pos; - okflag &= string_to_double(words[1], pos[0]); - okflag &= string_to_double(words[2], pos[1]); - okflag &= string_to_double(words[3], pos[2]); - if (words.size() == 5 || words.size() == 8) { - okflag &= string_to_double(words[4], pos[3]); - _v4_given = true; - } else { - pos[3] = 1.0; - } - - if (!okflag) { - objegg_cat.error() - << "Invalid number at line " << _line_number << "\n"; - return false; - } - - _v_table.push_back(pos); - - // Meshlab format might include an RGB color following the vertex - // position. - if (words.size() == 7 && words.size() == 8) { - size_t si = words.size(); - LVecBase3d rgb; - okflag &= string_to_double(words[si - 3], rgb[0]); - okflag &= string_to_double(words[si - 2], rgb[1]); - okflag &= string_to_double(words[si - 1], rgb[2]); - - if (!okflag) { - objegg_cat.error() - << "Invalid number at line " << _line_number << "\n"; - return false; - } - while (_rgb_table.size() + 1 < _v_table.size()) { - _rgb_table.push_back(LVecBase3d(1.0, 1.0, 1.0)); - } - _rgb_table.push_back(rgb); - } - - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::process_vt -// Access: Protected -// Description: -//////////////////////////////////////////////////////////////////// -bool ObjToEggConverter:: -process_vt(vector_string &words) { - if (words.size() != 3 && words.size() != 4) { - objegg_cat.error() - << "Wrong number of tokens at line " << _line_number << "\n"; - return false; - } - - bool okflag = true; - LTexCoord3d uvw; - okflag &= string_to_double(words[1], uvw[0]); - okflag &= string_to_double(words[2], uvw[1]); - if (words.size() == 4) { - okflag &= string_to_double(words[3], uvw[2]); - _vt3_given = true; - } else { - uvw[2] = 0.0; - } - - if (!okflag) { - objegg_cat.error() - << "Invalid number at line " << _line_number << "\n"; - return false; - } - - _vt_table.push_back(uvw); - - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::process_xvt -// Access: Protected -// Description: "xvt" is an extended column invented by DRZ. It -// includes texture coordinates in pixel space of the -// projector device, as well as for each camera. We map -// it to the nominal texture coordinates here. -//////////////////////////////////////////////////////////////////// -bool ObjToEggConverter:: -process_xvt(vector_string &words) { - if (words.size() < 3) { - objegg_cat.error() - << "Wrong number of tokens at line " << _line_number << "\n"; - return false; - } - - bool okflag = true; - LTexCoordd uv; - okflag &= string_to_double(words[1], uv[0]); - okflag &= string_to_double(words[2], uv[1]); - - if (!okflag) { - objegg_cat.error() - << "Invalid number at line " << _line_number << "\n"; - return false; - } - - uv[0] /= _ref_plane_res[0]; - uv[1] = 1.0 - uv[1] / _ref_plane_res[1]; - - _xvt_table.push_back(uv); - - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::process_xvc -// Access: Protected -// Description: "xvc" is another extended column invented by DRZ. We -// quietly ignore it. -//////////////////////////////////////////////////////////////////// -bool ObjToEggConverter:: -process_xvc(vector_string &words) { - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::process_vn -// Access: Protected -// Description: -//////////////////////////////////////////////////////////////////// -bool ObjToEggConverter:: -process_vn(vector_string &words) { - if (words.size() != 4) { - objegg_cat.error() - << "Wrong number of tokens at line " << _line_number << "\n"; - return false; - } - - bool okflag = true; - LVector3d normal; - okflag &= string_to_double(words[1], normal[0]); - okflag &= string_to_double(words[2], normal[1]); - okflag &= string_to_double(words[3], normal[2]); - - if (!okflag) { - objegg_cat.error() - << "Invalid number at line " << _line_number << "\n"; - return false; - } - normal.normalize(); - - _vn_table.push_back(normal); - - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::process_f -// Access: Protected -// Description: Defines a face in the obj file. -//////////////////////////////////////////////////////////////////// -bool ObjToEggConverter:: -process_f(vector_string &words) { - _f_given = true; - - PT(EggPolygon) poly = new EggPolygon; - for (size_t i = 1; i < words.size(); ++i) { - EggVertex *vertex = get_face_vertex(words[i]); - if (vertex == NULL) { - return false; - } - poly->add_vertex(vertex); - } - _current_group->add_child(poly); - - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::process_g -// Access: Protected -// Description: Defines a group in the obj file. -//////////////////////////////////////////////////////////////////// -bool ObjToEggConverter:: -process_g(vector_string &words) { - EggGroup *group = _root_group; - - // We assume the group names define a hierarchy of more-specific to - // less-specific group names, so that the first group name is the - // bottommost node, and the last group name is the topmost node. - - // Thus, iterate from the back to the front. - size_t i = words.size(); - while (i > 1) { - --i; - EggNode *child = group->find_child(words[i]); - if (child == NULL || !child->is_of_type(EggGroup::get_class_type())) { - child = new EggGroup(words[i]); - group->add_child(child); - } - group = DCAST(EggGroup, child); - } - - _current_group = group; - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::get_face_vertex -// Access: Protected -// Description: Returns or creates a vertex in the vpool according to -// the indicated face reference. -//////////////////////////////////////////////////////////////////// -EggVertex *ObjToEggConverter:: -get_face_vertex(const string &reference) { - VertexEntry entry(this, reference); - - // Synthesize a vertex. - EggVertex synth; - - if (entry._vi != 0) { - if (_v4_given) { - synth.set_pos(LCAST(double, _v_table[entry._vi - 1])); - } else { - LPoint4d pos = _v_table[entry._vi - 1]; - synth.set_pos(LPoint3d(pos[0], pos[1], pos[2])); - } - - if (entry._vi - 1 < (int)_rgb_table.size()) { - // We have a per-vertex color too. - LRGBColord rgb = _rgb_table[entry._vi - 1]; - LColor rgba(rgb[0], rgb[1], rgb[2], 1.0); - synth.set_color(rgba); - } - } - - if (entry._vti != 0) { - // We have a texture coordinate; apply it. - if (_vt3_given) { - synth.set_uvw("", _vt_table[entry._vti - 1]); - } else { - LTexCoord3d uvw = _vt_table[entry._vti - 1]; - synth.set_uv("", LTexCoordd(uvw[0], uvw[1])); - } - } else if (entry._vi - 1 < (int)_xvt_table.size()) { - // We have an xvt texture coordinate. - synth.set_uv("", _xvt_table[entry._vi - 1]); - } - - if (entry._vni != 0) { - // We have a normal; apply it. - synth.set_normal(_vn_table[entry._vni - 1]); - } - - return _vpool->create_unique_vertex(synth); -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::generate_egg_points -// Access: Protected -// Description: If an obj file defines no faces, create a bunch of -// EggVertex objects to illustrate the vertex positions -// at least. -//////////////////////////////////////////////////////////////////// -void ObjToEggConverter:: -generate_egg_points() { - for (size_t vi = 0; vi < _v_table.size(); ++vi) { - const LVecBase4d &p = _v_table[vi]; - _vpool->make_new_vertex(LVecBase3d(p[0], p[1], p[2])); - } -} - - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::process_node -// Access: Protected -// Description: Reads the file and converts it to PandaNode structures. -//////////////////////////////////////////////////////////////////// -bool ObjToEggConverter:: -process_node(const Filename &filename) { - VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - istream *strm = vfs->open_read_file(filename, true); - if (strm == NULL) { - objegg_cat.error() - << "Couldn't read " << filename << "\n"; - return false; - } - - _v_table.clear(); - _vn_table.clear(); - _rgb_table.clear(); - _vt_table.clear(); - _xvt_table.clear(); - _ref_plane_res.set(1.0, 1.0); - _v4_given = false; - _vt3_given = false; - _f_given = false; - - StreamReader sr(strm, true); - string line = sr.readline(); - _line_number = 1; - while (!line.empty()) { - line = trim(line); - if (line.empty()) { - line = sr.readline(); - continue; - } - - if (line.substr(0, 15) == "#_ref_plane_res") { - process_ref_plane_res(line); - line = sr.readline(); - continue; - } - - if (line[0] == '#') { - line = sr.readline(); - continue; - } - - if (!process_line_node(line)) { - return false; - } - line = sr.readline(); - ++_line_number; - } - - if (!_f_given) { - generate_points(); - } - - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::process_line_node -// Access: Protected -// Description: -//////////////////////////////////////////////////////////////////// -bool ObjToEggConverter:: -process_line_node(const string &line) { - vector_string words; - tokenize(line, words, " \t", true); - nassertr(!words.empty(), false); - - string tag = words[0]; - if (tag == "v") { - return process_v(words); - } else if (tag == "vt") { - return process_vt(words); - } else if (tag == "xvt") { - return process_xvt(words); - } else if (tag == "xvc") { - return process_xvc(words); - } else if (tag == "vn") { - return process_vn(words); - } else if (tag == "f") { - return process_f_node(words); - } else if (tag == "g") { - return process_g_node(words); - } else { - bool inserted = _ignored_tags.insert(tag).second; - if (inserted) { - objegg_cat.info() - << "Ignoring tag " << tag << "\n"; - } - } - - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::process_f_node -// Access: Protected -// Description: Defines a face in the obj file. -//////////////////////////////////////////////////////////////////// -bool ObjToEggConverter:: -process_f_node(vector_string &words) { - _f_given = true; - - bool all_vn = true; - int non_vn_index = -1; - - pvector verts; - verts.reserve(words.size() - 1); - for (size_t i = 1; i < words.size(); ++i) { - VertexEntry entry(this, words[i]); - verts.push_back(entry); - if (entry._vni == 0) { - all_vn = false; - non_vn_index = i; - } - } - - if (verts.size() < 3) { - // Not enough vertices. - objegg_cat.error() - << "Degenerate face at " << _line_number << "\n"; - return false; - } - - int synth_vni = 0; - if (!all_vn) { - // Synthesize a normal if we need it. - LNormald normal = LNormald::zero(); - for (size_t i = 0; i < verts.size(); ++i) { - int vi0 = verts[i]._vi; - int vi1 = verts[(i + 1) % verts.size()]._vi; - if (vi0 == 0 || vi1 == 0) { - continue; - } - const LVecBase4d &p0 = _v_table[vi0 - 1]; - const LVecBase4d &p1 = _v_table[vi1 - 1]; - - normal[0] += p0[1] * p1[2] - p0[2] * p1[1]; - normal[1] += p0[2] * p1[0] - p0[0] * p1[2]; - normal[2] += p0[0] * p1[1] - p0[1] * p1[0]; - } - normal.normalize(); - synth_vni = add_synth_normal(normal); - } - - Triangulator3 tri; - int num_tris = 1; - - if (verts.size() != 3) { - // We have to triangulate a higher-order polygon. - for (size_t i = 0; i < verts.size(); ++i) { - const LVecBase4d &p = _v_table[verts[i]._vi - 1]; - tri.add_vertex(p[0], p[1], p[2]); - tri.add_polygon_vertex(i); - } - - tri.triangulate(); - num_tris = tri.get_num_triangles(); - } - - if (_current_vertex_data->_prim->get_num_vertices() + 3 * num_tris > egg_max_indices || - _current_vertex_data->_entries.size() + verts.size() > egg_max_vertices) { - // We'll exceed our specified limit with these triangles; start a new Geom. - _current_vertex_data->close_geom(this); - } - - if (verts.size() == 3) { - // It's already a triangle; add it directly. - _current_vertex_data->add_triangle(this, verts[0], verts[1], verts[2], synth_vni); - - } else { - // Get the triangulated results. - for (int ti = 0; ti < num_tris; ++ti) { - int i0 = tri.get_triangle_v0(ti); - int i1 = tri.get_triangle_v1(ti); - int i2 = tri.get_triangle_v2(ti); - _current_vertex_data->add_triangle(this, verts[i0], verts[i1], verts[i2], synth_vni); - } - } - - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::process_g_node -// Access: Protected -// Description: Defines a group in the obj file. -//////////////////////////////////////////////////////////////////// -bool ObjToEggConverter:: -process_g_node(vector_string &words) { - _current_vertex_data->close_geom(this); - delete _current_vertex_data; - _current_vertex_data = NULL; - - NodePath np(_root_node); - - // We assume the group names define a hierarchy of more-specific to - // less-specific group names, so that the first group name is the - // bottommost node, and the last group name is the topmost node. - - // Thus, iterate from the back to the front. - size_t i = words.size(); - string name; - while (i > 2) { - --i; - name = words[i]; - NodePath child = np.find(name); - if (!child) { - child = np.attach_new_node(name); - } - np = child; - } - - if (i > 1) { - --i; - name = words[i]; - } - - _current_vertex_data = new VertexData(np.node(), name); - - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::generate_points -// Access: Protected -// Description: If an obj file defines no faces, create a bunch of -// GeomPoints to illustrate the vertex positions at -// least. -//////////////////////////////////////////////////////////////////// -void ObjToEggConverter:: -generate_points() { - CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3(); - PT(GeomVertexData) vdata = new GeomVertexData("points", format, GeomEnums::UH_static); - vdata->set_num_rows(_v_table.size()); - GeomVertexWriter vertex_writer(vdata, InternalName::get_vertex()); - - for (size_t vi = 0; vi < _v_table.size(); ++vi) { - const LVecBase4d &p = _v_table[vi]; - vertex_writer.add_data3d(p[0], p[1], p[2]); - } - - PT(GeomPrimitive) prim = new GeomPoints(GeomEnums::UH_static); - prim->add_next_vertices(_v_table.size()); - prim->close_primitive(); - - PT(Geom) geom = new Geom(vdata); - geom->add_primitive(prim); - - PT(GeomNode) geom_node = new GeomNode("points"); - geom_node->add_geom(geom); - _root_node->add_child(geom_node); -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::add_synth_normal -// Access: Private -// Description: Adds a new normal to the synth_vn table, or returns -// an existing normal. In either case returns the -// 1-based index number to the normal. -//////////////////////////////////////////////////////////////////// -int ObjToEggConverter:: -add_synth_normal(const LVecBase3d &normal) { - pair result = _unique_synth_vn_table.insert(UniqueVec3Table::value_type(normal, _unique_synth_vn_table.size())); - UniqueVec3Table::iterator ni = result.first; - int index = (*ni).second; - - if (result.second) { - // If the normal was added to the table, it's a unique normal, and - // now we have to add it to the table too. - _synth_vn_table.push_back(normal); - } - - return index + 1; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::VertexEntry::Constructor -// Access: Public -// Description: Creates a VertexEntry from the n/n/n string format in -// the obj file face reference. -//////////////////////////////////////////////////////////////////// -ObjToEggConverter::VertexEntry:: -VertexEntry(const ObjToEggConverter *converter, const string &obj_vertex) { - _vi = 0; - _vti = 0; - _vni = 0; - _synth_vni = 0; - - vector_string words; - tokenize(obj_vertex, words, "/", false); - nassertv(!words.empty()); - - for (size_t i = 0; i < words.size(); ++i) { - int index; - if (trim(words[i]).empty()) { - index = 0; - } else { - if (!string_to_int(words[i], index)) { - index = 0; - } - } - - switch (i) { - case 0: - _vi = index; - if (_vi < 0) { - _vi = (int)converter->_v_table.size() + _vi; - } - if (_vi < 0 || _vi - 1 >= (int)converter->_v_table.size()) { - _vi = 0; - } - break; - - case 1: - _vti = index; - if (_vti < 0) { - _vti = (int)converter->_vt_table.size() + _vti; - } - if (_vti < 0 || _vti - 1 >= (int)converter->_vt_table.size()) { - _vti = 0; - } - break; - - case 2: - _vni = index; - if (_vni < 0) { - _vni = (int)converter->_vn_table.size() + _vni; - } - if (_vni < 0 || _vni - 1 >= (int)converter->_vn_table.size()) { - _vni = 0; - } - break; - } - } -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::VertexData::Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -ObjToEggConverter::VertexData:: -VertexData(PandaNode *parent, const string &name) : - _parent(parent), _name(name) -{ - _geom_node = NULL; - - _v4_given = false; - _vt3_given = false; - _vt_given = false; - _rgb_given = false; - _vn_given = false; - - _prim = new GeomTriangles(GeomEnums::UH_static); -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::VertexData::add_vertex -// Access: Public -// Description: Adds a new entry to the vertex data for the indicated -// VertexEntry, or returns an equivalent vertex already -// present. -//////////////////////////////////////////////////////////////////// -int ObjToEggConverter::VertexData:: -add_vertex(const ObjToEggConverter *converter, const VertexEntry &entry) { - pair result; - UniqueVertexEntries::iterator ni; - int index; - - if (entry._vni != 0 || entry._synth_vni != 0) { - // If we are storing a vertex with a normal, see if we - // have already stored a vertex without a normal first. - VertexEntry no_normal(entry); - no_normal._vni = 0; - no_normal._synth_vni = 0; - ni = _unique_entries.find(no_normal); - if (ni != _unique_entries.end()) { - // We did have such a vertex! In this case, repurpose this - // vertex, resetting it to contain this normal. - index = (*ni).second; - _unique_entries.erase(ni); - result = _unique_entries.insert(UniqueVertexEntries::value_type(entry, index)); - nassertr(result.second, index); - nassertr(_entries[index] == no_normal, index); - _entries[index]._vni = entry._vni; - _entries[index]._synth_vni = entry._synth_vni; - return index; - } - } else if (entry._vni == 0 && entry._synth_vni == 0) { - // If we are storing a vertex *without* any normal, see if we have - // already stored a vertex with a normal first. - ni = _unique_entries.lower_bound(entry); - if (ni != _unique_entries.end() && (*ni).first.matches_except_normal(entry)) { - // We had such a vertex, so use it. - index = (*ni).second; - return index; - } - } - - // We didn't already have a vertex we could repurpose, so try to add - // exactly the desired vertex. - result = _unique_entries.insert(UniqueVertexEntries::value_type(entry, _entries.size())); - ni = result.first; - index = (*ni).second; - - if (result.second) { - // If the vertex was added to the table, it's a unique vertex, and - // now we have to add it to the vertex data too. - _entries.push_back(entry); - - if (converter->_v4_given) { - _v4_given = true; - } - if (converter->_vt3_given) { - _vt3_given = true; - } - if (entry._vti != 0) { - _vt_given = true; - } else if (entry._vi - 1 < (int)converter->_xvt_table.size()) { - // We have an xvt texture coordinate. - _vt_given = true; - } - if (entry._vi - 1 < (int)converter->_rgb_table.size()) { - // We have a per-vertex color too. - _rgb_given = true; - } - if (entry._vni != 0) { - _vn_given = true; - } - } - - return index; -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::VertexData::add_triangle -// Access: Public -// Description: Adds a triangle to the primitive, as a triple of -// three VertexEntry objects, which are each added to -// the vertex pool. If synth_vni is not 0, it is -// assigned to the last vertex. -//////////////////////////////////////////////////////////////////// -void ObjToEggConverter::VertexData:: -add_triangle(const ObjToEggConverter *converter, const VertexEntry &v0, - const VertexEntry &v1, const VertexEntry &v2, - int synth_vni) { - int v0i, v1i, v2i; - - v0i = add_vertex(converter, v0); - v1i = add_vertex(converter, v1); - - if (synth_vni != 0) { - VertexEntry v2n(v2); - v2n._synth_vni = synth_vni; - v2i = add_vertex(converter, v2n); - } else { - v2i = add_vertex(converter, v2); - } - - _prim->add_vertices(v0i, v1i, v2i); - _prim->close_primitive(); -} - -//////////////////////////////////////////////////////////////////// -// Function: ObjToEggConverter::VertexData::close_geom -// Access: Public -// Description: Finishes the current geom and stores it as a child -// in the root. Prepares for new geoms. -//////////////////////////////////////////////////////////////////// -void ObjToEggConverter::VertexData:: -close_geom(const ObjToEggConverter *converter) { - if (_prim->get_num_vertices() != 0) { - // Create a new format that includes only the columns we actually - // used. - PT(GeomVertexArrayFormat) aformat = new GeomVertexArrayFormat; - if (_v4_given) { - aformat->add_column(InternalName::get_vertex(), 4, - GeomEnums::NT_stdfloat, GeomEnums::C_point); - } else { - aformat->add_column(InternalName::get_vertex(), 3, - GeomEnums::NT_stdfloat, GeomEnums::C_point); - } - - // We always add normals--if no normals appeared in the file, we - // synthesize them. - aformat->add_column(InternalName::get_normal(), 3, - GeomEnums::NT_stdfloat, GeomEnums::C_vector); - - if (_vt_given) { - if (_vt3_given) { - aformat->add_column(InternalName::get_texcoord(), 3, - GeomEnums::NT_stdfloat, GeomEnums::C_texcoord); - } else { - aformat->add_column(InternalName::get_texcoord(), 2, - GeomEnums::NT_stdfloat, GeomEnums::C_texcoord); - } - } - - if (_rgb_given) { - aformat->add_column(InternalName::get_color(), 4, - GeomEnums::NT_uint8, GeomEnums::C_color); - } - - CPT(GeomVertexFormat) format = GeomVertexFormat::register_format(aformat); - - // Create and populate the vertex data. - PT(GeomVertexData) vdata = new GeomVertexData(_name, format, GeomEnums::UH_static); - GeomVertexWriter vertex_writer(vdata, InternalName::get_vertex()); - GeomVertexWriter normal_writer(vdata, InternalName::get_normal()); - GeomVertexWriter texcoord_writer(vdata, InternalName::get_texcoord()); - GeomVertexWriter color_writer(vdata, InternalName::get_color()); - - for (size_t i = 0; i < _entries.size(); ++i) { - const VertexEntry &entry = _entries[i]; - - if (entry._vi != 0) { - vertex_writer.set_row(i); - vertex_writer.add_data4d(converter->_v_table[entry._vi - 1]); - } - if (entry._vti != 0) { - texcoord_writer.set_row(i); - texcoord_writer.add_data3d(converter->_vt_table[entry._vti - 1]); - } else if (entry._vi - 1 < (int)converter->_xvt_table.size()) { - // We have an xvt texture coordinate. - texcoord_writer.set_row(i); - texcoord_writer.add_data2d(converter->_xvt_table[entry._vi - 1]); - } - if (entry._vni != 0) { - normal_writer.set_row(i); - normal_writer.add_data3d(converter->_vn_table[entry._vni - 1]); - } else if (entry._synth_vni != 0) { - normal_writer.set_row(i); - normal_writer.add_data3d(converter->_synth_vn_table[entry._synth_vni - 1]); - } else { - // In this case, the normal isn't used and doesn't matter; we - // fill it in a unit vector just for neatness. - normal_writer.set_row(i); - normal_writer.add_data3d(0, 0, 1); - } - if (_rgb_given) { - if (entry._vi - 1 < (int)converter->_rgb_table.size()) { - color_writer.set_row(i); - color_writer.add_data3d(converter->_rgb_table[entry._vi - 1]); - } - } - } - - // Transform to zup-right. - vdata->transform_vertices(LMatrix4::convert_mat(CS_zup_right, CS_default)); - - // Now create a Geom with this data. - CPT(RenderState) state = RenderState::make_empty(); - if (_rgb_given) { - state = state->add_attrib(ColorAttrib::make_vertex()); - } else { - state = state->add_attrib(ColorAttrib::make_flat(LColor(1, 1, 1, 1))); - } - if (!_vn_given) { - // We have synthesized these normals; specify the flat-shading - // attrib. - state = state->add_attrib(ShadeModelAttrib::make(ShadeModelAttrib::M_flat)); - _prim->set_shade_model(GeomEnums::SM_flat_last_vertex); - } - - PT(Geom) geom = new Geom(vdata); - geom->add_primitive(_prim); - - if (_geom_node == NULL) { - _geom_node = new GeomNode(_name); - _parent->add_child(_geom_node); - } - - _geom_node->add_geom(geom, state); - } - - _prim = new GeomTriangles(GeomEnums::UH_static); - _entries.clear(); - _unique_entries.clear(); -} +// Filename: objToEggConverter.cxx +// Created by: drose (07Dec10) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "objToEggConverter.h" +#include "config_objegg.h" +#include "eggData.h" +#include "string_utils.h" +#include "streamReader.h" +#include "virtualFileSystem.h" +#include "eggPolygon.h" +#include "nodePath.h" +#include "geomTriangles.h" +#include "geomPoints.h" +#include "colorAttrib.h" +#include "shadeModelAttrib.h" +#include "dcast.h" +#include "triangulator3.h" +#include "config_egg2pg.h" + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ObjToEggConverter:: +ObjToEggConverter() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ObjToEggConverter:: +ObjToEggConverter(const ObjToEggConverter ©) : + SomethingToEggConverter(copy) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ObjToEggConverter:: +~ObjToEggConverter() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::make_copy +// Access: Public, Virtual +// Description: Allocates and returns a new copy of the converter. +//////////////////////////////////////////////////////////////////// +SomethingToEggConverter *ObjToEggConverter:: +make_copy() { + return new ObjToEggConverter(*this); +} + + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::get_name +// Access: Public, Virtual +// Description: Returns the English name of the file type this +// converter supports. +//////////////////////////////////////////////////////////////////// +string ObjToEggConverter:: +get_name() const { + return "obj"; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::get_extension +// Access: Public, Virtual +// Description: Returns the common extension of the file type this +// converter supports. +//////////////////////////////////////////////////////////////////// +string ObjToEggConverter:: +get_extension() const { + return "obj"; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::supports_compressed +// Access: Published, Virtual +// Description: Returns true if this file type can transparently load +// compressed files (with a .pz extension), false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool ObjToEggConverter:: +supports_compressed() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::supports_convert_to_node +// Access: Published, Virtual +// Description: Returns true if this converter can directly convert +// the model type to internal Panda memory structures, +// given the indicated options, or false otherwise. If +// this returns true, then convert_to_node() may be +// called to perform the conversion, which may be faster +// than calling convert_file() if the ultimate goal is a +// PandaNode anyway. +//////////////////////////////////////////////////////////////////// +bool ObjToEggConverter:: +supports_convert_to_node(const LoaderOptions &options) const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::convert_file +// Access: Public, Virtual +// Description: Handles the reading of the input file and converting +// it to egg. Returns true if successful, false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool ObjToEggConverter:: +convert_file(const Filename &filename) { + clear_error(); + + if (_egg_data->get_coordinate_system() == CS_default) { + _egg_data->set_coordinate_system(CS_zup_right); + } + + if (!process(filename)) { + _error = true; + } + return !had_error(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::convert_to_node +// Access: Public, Virtual +// Description: Reads the input file and directly produces a +// ready-to-render model file as a PandaNode. Returns +// NULL on failure, or if it is not supported. (This +// functionality is not supported by all converter +// types; see supports_convert_to_node()). +//////////////////////////////////////////////////////////////////// +PT(PandaNode) ObjToEggConverter:: +convert_to_node(const LoaderOptions &options, const Filename &filename) { + clear_error(); + + _root_node = new PandaNode(""); + _current_vertex_data = new VertexData(_root_node, "root"); + + if (!process_node(filename)) { + _error = true; + } + + _current_vertex_data->close_geom(this); + delete _current_vertex_data; + + if (had_error()) { + return NULL; + } + + return _root_node; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::process +// Access: Protected +// Description: Reads the file and converts it to egg structures. +//////////////////////////////////////////////////////////////////// +bool ObjToEggConverter:: +process(const Filename &filename) { + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + istream *strm = vfs->open_read_file(filename, true); + if (strm == NULL) { + objegg_cat.error() + << "Couldn't read " << filename << "\n"; + return false; + } + + _v_table.clear(); + _vn_table.clear(); + _rgb_table.clear(); + _vt_table.clear(); + _xvt_table.clear(); + _ref_plane_res.set(1.0, 1.0); + _v4_given = false; + _vt3_given = false; + _f_given = false; + + _vpool = new EggVertexPool("vpool"); + _egg_data->add_child(_vpool); + _root_group = new EggGroup("root"); + _egg_data->add_child(_root_group); + _current_group = _root_group; + + StreamReader sr(strm, true); + string line = sr.readline(); + _line_number = 1; + while (!line.empty()) { + line = trim(line); + if (line.empty()) { + line = sr.readline(); + continue; + } + + while (line[line.length() - 1] == '\\') { + // If it ends on a backslash, it's a continuation character. + string line2 = sr.readline(); + ++_line_number; + if (line2.empty()) { + break; + } + line = line.substr(0, line.length() - 1) + trim(line2); + } + + if (line.substr(0, 15) == "#_ref_plane_res") { + process_ref_plane_res(line); + line = sr.readline(); + continue; + } + + if (line[0] == '#') { + line = sr.readline(); + continue; + } + + if (!process_line(line)) { + return false; + } + line = sr.readline(); + ++_line_number; + } + + if (!_f_given) { + generate_egg_points(); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::process_line +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +bool ObjToEggConverter:: +process_line(const string &line) { + vector_string words; + tokenize(line, words, " \t", true); + nassertr(!words.empty(), false); + + string tag = words[0]; + if (tag == "v") { + return process_v(words); + } else if (tag == "vt") { + return process_vt(words); + } else if (tag == "xvt") { + return process_xvt(words); + } else if (tag == "xvc") { + return process_xvc(words); + } else if (tag == "vn") { + return process_vn(words); + } else if (tag == "f") { + return process_f(words); + } else if (tag == "g") { + return process_g(words); + } else { + bool inserted = _ignored_tags.insert(tag).second; + if (inserted) { + objegg_cat.info() + << "Ignoring tag " << tag << "\n"; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::process_line +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +bool ObjToEggConverter:: +process_ref_plane_res(const string &line) { + // the #_ref_plane_res line is a DRZ extension that defines the + // pixel resolution of the projector device. It's needed to + // properly scale the xvt lines. + + vector_string words; + tokenize(line, words, " \t", true); + nassertr(!words.empty(), false); + + if (words.size() != 3) { + objegg_cat.error() + << "Wrong number of tokens at line " << _line_number << "\n"; + return false; + } + + bool okflag = true; + LPoint3d pos; + okflag &= string_to_double(words[1], _ref_plane_res[0]); + okflag &= string_to_double(words[2], _ref_plane_res[1]); + + if (!okflag) { + objegg_cat.error() + << "Invalid number at line " << _line_number << ":\n"; + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::process_v +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +bool ObjToEggConverter:: +process_v(vector_string &words) { + if (words.size() != 4 && words.size() != 5 && + words.size() != 7 && words.size() != 8) { + objegg_cat.error() + << "Wrong number of tokens at line " << _line_number << "\n"; + return false; + } + + bool okflag = true; + LPoint4d pos; + okflag &= string_to_double(words[1], pos[0]); + okflag &= string_to_double(words[2], pos[1]); + okflag &= string_to_double(words[3], pos[2]); + if (words.size() == 5 || words.size() == 8) { + okflag &= string_to_double(words[4], pos[3]); + _v4_given = true; + } else { + pos[3] = 1.0; + } + + if (!okflag) { + objegg_cat.error() + << "Invalid number at line " << _line_number << "\n"; + return false; + } + + _v_table.push_back(pos); + + // Meshlab format might include an RGB color following the vertex + // position. + if (words.size() == 7 && words.size() == 8) { + size_t si = words.size(); + LVecBase3d rgb; + okflag &= string_to_double(words[si - 3], rgb[0]); + okflag &= string_to_double(words[si - 2], rgb[1]); + okflag &= string_to_double(words[si - 1], rgb[2]); + + if (!okflag) { + objegg_cat.error() + << "Invalid number at line " << _line_number << "\n"; + return false; + } + while (_rgb_table.size() + 1 < _v_table.size()) { + _rgb_table.push_back(LVecBase3d(1.0, 1.0, 1.0)); + } + _rgb_table.push_back(rgb); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::process_vt +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +bool ObjToEggConverter:: +process_vt(vector_string &words) { + if (words.size() != 3 && words.size() != 4) { + objegg_cat.error() + << "Wrong number of tokens at line " << _line_number << "\n"; + return false; + } + + bool okflag = true; + LTexCoord3d uvw; + okflag &= string_to_double(words[1], uvw[0]); + okflag &= string_to_double(words[2], uvw[1]); + if (words.size() == 4) { + okflag &= string_to_double(words[3], uvw[2]); + _vt3_given = true; + } else { + uvw[2] = 0.0; + } + + if (!okflag) { + objegg_cat.error() + << "Invalid number at line " << _line_number << "\n"; + return false; + } + + _vt_table.push_back(uvw); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::process_xvt +// Access: Protected +// Description: "xvt" is an extended column invented by DRZ. It +// includes texture coordinates in pixel space of the +// projector device, as well as for each camera. We map +// it to the nominal texture coordinates here. +//////////////////////////////////////////////////////////////////// +bool ObjToEggConverter:: +process_xvt(vector_string &words) { + if (words.size() < 3) { + objegg_cat.error() + << "Wrong number of tokens at line " << _line_number << "\n"; + return false; + } + + bool okflag = true; + LTexCoordd uv; + okflag &= string_to_double(words[1], uv[0]); + okflag &= string_to_double(words[2], uv[1]); + + if (!okflag) { + objegg_cat.error() + << "Invalid number at line " << _line_number << "\n"; + return false; + } + + uv[0] /= _ref_plane_res[0]; + uv[1] = 1.0 - uv[1] / _ref_plane_res[1]; + + _xvt_table.push_back(uv); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::process_xvc +// Access: Protected +// Description: "xvc" is another extended column invented by DRZ. We +// quietly ignore it. +//////////////////////////////////////////////////////////////////// +bool ObjToEggConverter:: +process_xvc(vector_string &words) { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::process_vn +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +bool ObjToEggConverter:: +process_vn(vector_string &words) { + if (words.size() != 4) { + objegg_cat.error() + << "Wrong number of tokens at line " << _line_number << "\n"; + return false; + } + + bool okflag = true; + LVector3d normal; + okflag &= string_to_double(words[1], normal[0]); + okflag &= string_to_double(words[2], normal[1]); + okflag &= string_to_double(words[3], normal[2]); + + if (!okflag) { + objegg_cat.error() + << "Invalid number at line " << _line_number << "\n"; + return false; + } + normal.normalize(); + + _vn_table.push_back(normal); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::process_f +// Access: Protected +// Description: Defines a face in the obj file. +//////////////////////////////////////////////////////////////////// +bool ObjToEggConverter:: +process_f(vector_string &words) { + _f_given = true; + + PT(EggPolygon) poly = new EggPolygon; + for (size_t i = 1; i < words.size(); ++i) { + EggVertex *vertex = get_face_vertex(words[i]); + if (vertex == NULL) { + return false; + } + poly->add_vertex(vertex); + } + _current_group->add_child(poly); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::process_g +// Access: Protected +// Description: Defines a group in the obj file. +//////////////////////////////////////////////////////////////////// +bool ObjToEggConverter:: +process_g(vector_string &words) { + EggGroup *group = _root_group; + + // We assume the group names define a hierarchy of more-specific to + // less-specific group names, so that the first group name is the + // bottommost node, and the last group name is the topmost node. + + // Thus, iterate from the back to the front. + size_t i = words.size(); + while (i > 1) { + --i; + EggNode *child = group->find_child(words[i]); + if (child == NULL || !child->is_of_type(EggGroup::get_class_type())) { + child = new EggGroup(words[i]); + group->add_child(child); + } + group = DCAST(EggGroup, child); + } + + _current_group = group; + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::get_face_vertex +// Access: Protected +// Description: Returns or creates a vertex in the vpool according to +// the indicated face reference. +//////////////////////////////////////////////////////////////////// +EggVertex *ObjToEggConverter:: +get_face_vertex(const string &reference) { + VertexEntry entry(this, reference); + + // Synthesize a vertex. + EggVertex synth; + + if (entry._vi != 0) { + if (_v4_given) { + synth.set_pos(LCAST(double, _v_table[entry._vi - 1])); + } else { + LPoint4d pos = _v_table[entry._vi - 1]; + synth.set_pos(LPoint3d(pos[0], pos[1], pos[2])); + } + + if (entry._vi - 1 < (int)_rgb_table.size()) { + // We have a per-vertex color too. + LRGBColord rgb = _rgb_table[entry._vi - 1]; + LColor rgba(rgb[0], rgb[1], rgb[2], 1.0); + synth.set_color(rgba); + } + } + + if (entry._vti != 0) { + // We have a texture coordinate; apply it. + if (_vt3_given) { + synth.set_uvw("", _vt_table[entry._vti - 1]); + } else { + LTexCoord3d uvw = _vt_table[entry._vti - 1]; + synth.set_uv("", LTexCoordd(uvw[0], uvw[1])); + } + } else if (entry._vi - 1 < (int)_xvt_table.size()) { + // We have an xvt texture coordinate. + synth.set_uv("", _xvt_table[entry._vi - 1]); + } + + if (entry._vni != 0) { + // We have a normal; apply it. + synth.set_normal(_vn_table[entry._vni - 1]); + } + + return _vpool->create_unique_vertex(synth); +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::generate_egg_points +// Access: Protected +// Description: If an obj file defines no faces, create a bunch of +// EggVertex objects to illustrate the vertex positions +// at least. +//////////////////////////////////////////////////////////////////// +void ObjToEggConverter:: +generate_egg_points() { + for (size_t vi = 0; vi < _v_table.size(); ++vi) { + const LVecBase4d &p = _v_table[vi]; + _vpool->make_new_vertex(LVecBase3d(p[0], p[1], p[2])); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::process_node +// Access: Protected +// Description: Reads the file and converts it to PandaNode structures. +//////////////////////////////////////////////////////////////////// +bool ObjToEggConverter:: +process_node(const Filename &filename) { + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + istream *strm = vfs->open_read_file(filename, true); + if (strm == NULL) { + objegg_cat.error() + << "Couldn't read " << filename << "\n"; + return false; + } + + _v_table.clear(); + _vn_table.clear(); + _rgb_table.clear(); + _vt_table.clear(); + _xvt_table.clear(); + _ref_plane_res.set(1.0, 1.0); + _v4_given = false; + _vt3_given = false; + _f_given = false; + + StreamReader sr(strm, true); + string line = sr.readline(); + _line_number = 1; + while (!line.empty()) { + line = trim(line); + if (line.empty()) { + line = sr.readline(); + continue; + } + + if (line.substr(0, 15) == "#_ref_plane_res") { + process_ref_plane_res(line); + line = sr.readline(); + continue; + } + + if (line[0] == '#') { + line = sr.readline(); + continue; + } + + if (!process_line_node(line)) { + return false; + } + line = sr.readline(); + ++_line_number; + } + + if (!_f_given) { + generate_points(); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::process_line_node +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +bool ObjToEggConverter:: +process_line_node(const string &line) { + vector_string words; + tokenize(line, words, " \t", true); + nassertr(!words.empty(), false); + + string tag = words[0]; + if (tag == "v") { + return process_v(words); + } else if (tag == "vt") { + return process_vt(words); + } else if (tag == "xvt") { + return process_xvt(words); + } else if (tag == "xvc") { + return process_xvc(words); + } else if (tag == "vn") { + return process_vn(words); + } else if (tag == "f") { + return process_f_node(words); + } else if (tag == "g") { + return process_g_node(words); + } else { + bool inserted = _ignored_tags.insert(tag).second; + if (inserted) { + objegg_cat.info() + << "Ignoring tag " << tag << "\n"; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::process_f_node +// Access: Protected +// Description: Defines a face in the obj file. +//////////////////////////////////////////////////////////////////// +bool ObjToEggConverter:: +process_f_node(vector_string &words) { + _f_given = true; + + bool all_vn = true; + int non_vn_index = -1; + + pvector verts; + verts.reserve(words.size() - 1); + for (size_t i = 1; i < words.size(); ++i) { + VertexEntry entry(this, words[i]); + verts.push_back(entry); + if (entry._vni == 0) { + all_vn = false; + non_vn_index = i; + } + } + + if (verts.size() < 3) { + // Not enough vertices. + objegg_cat.error() + << "Degenerate face at " << _line_number << "\n"; + return false; + } + + int synth_vni = 0; + if (!all_vn) { + // Synthesize a normal if we need it. + LNormald normal = LNormald::zero(); + for (size_t i = 0; i < verts.size(); ++i) { + int vi0 = verts[i]._vi; + int vi1 = verts[(i + 1) % verts.size()]._vi; + if (vi0 == 0 || vi1 == 0) { + continue; + } + const LVecBase4d &p0 = _v_table[vi0 - 1]; + const LVecBase4d &p1 = _v_table[vi1 - 1]; + + normal[0] += p0[1] * p1[2] - p0[2] * p1[1]; + normal[1] += p0[2] * p1[0] - p0[0] * p1[2]; + normal[2] += p0[0] * p1[1] - p0[1] * p1[0]; + } + normal.normalize(); + synth_vni = add_synth_normal(normal); + } + + Triangulator3 tri; + int num_tris = 1; + + if (verts.size() != 3) { + // We have to triangulate a higher-order polygon. + for (size_t i = 0; i < verts.size(); ++i) { + const LVecBase4d &p = _v_table[verts[i]._vi - 1]; + tri.add_vertex(p[0], p[1], p[2]); + tri.add_polygon_vertex(i); + } + + tri.triangulate(); + num_tris = tri.get_num_triangles(); + } + + if (_current_vertex_data->_prim->get_num_vertices() + 3 * num_tris > egg_max_indices || + _current_vertex_data->_entries.size() + verts.size() > egg_max_vertices) { + // We'll exceed our specified limit with these triangles; start a new Geom. + _current_vertex_data->close_geom(this); + } + + if (verts.size() == 3) { + // It's already a triangle; add it directly. + _current_vertex_data->add_triangle(this, verts[0], verts[1], verts[2], synth_vni); + + } else { + // Get the triangulated results. + for (int ti = 0; ti < num_tris; ++ti) { + int i0 = tri.get_triangle_v0(ti); + int i1 = tri.get_triangle_v1(ti); + int i2 = tri.get_triangle_v2(ti); + _current_vertex_data->add_triangle(this, verts[i0], verts[i1], verts[i2], synth_vni); + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::process_g_node +// Access: Protected +// Description: Defines a group in the obj file. +//////////////////////////////////////////////////////////////////// +bool ObjToEggConverter:: +process_g_node(vector_string &words) { + _current_vertex_data->close_geom(this); + delete _current_vertex_data; + _current_vertex_data = NULL; + + NodePath np(_root_node); + + // We assume the group names define a hierarchy of more-specific to + // less-specific group names, so that the first group name is the + // bottommost node, and the last group name is the topmost node. + + // Thus, iterate from the back to the front. + size_t i = words.size(); + string name; + while (i > 2) { + --i; + name = words[i]; + NodePath child = np.find(name); + if (!child) { + child = np.attach_new_node(name); + } + np = child; + } + + if (i > 1) { + --i; + name = words[i]; + } + + _current_vertex_data = new VertexData(np.node(), name); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::generate_points +// Access: Protected +// Description: If an obj file defines no faces, create a bunch of +// GeomPoints to illustrate the vertex positions at +// least. +//////////////////////////////////////////////////////////////////// +void ObjToEggConverter:: +generate_points() { + CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3(); + PT(GeomVertexData) vdata = new GeomVertexData("points", format, GeomEnums::UH_static); + vdata->set_num_rows(_v_table.size()); + GeomVertexWriter vertex_writer(vdata, InternalName::get_vertex()); + + for (size_t vi = 0; vi < _v_table.size(); ++vi) { + const LVecBase4d &p = _v_table[vi]; + vertex_writer.add_data3d(p[0], p[1], p[2]); + } + + PT(GeomPrimitive) prim = new GeomPoints(GeomEnums::UH_static); + prim->add_next_vertices(_v_table.size()); + prim->close_primitive(); + + PT(Geom) geom = new Geom(vdata); + geom->add_primitive(prim); + + PT(GeomNode) geom_node = new GeomNode("points"); + geom_node->add_geom(geom); + _root_node->add_child(geom_node); +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::add_synth_normal +// Access: Private +// Description: Adds a new normal to the synth_vn table, or returns +// an existing normal. In either case returns the +// 1-based index number to the normal. +//////////////////////////////////////////////////////////////////// +int ObjToEggConverter:: +add_synth_normal(const LVecBase3d &normal) { + pair result = _unique_synth_vn_table.insert(UniqueVec3Table::value_type(normal, _unique_synth_vn_table.size())); + UniqueVec3Table::iterator ni = result.first; + int index = (*ni).second; + + if (result.second) { + // If the normal was added to the table, it's a unique normal, and + // now we have to add it to the table too. + _synth_vn_table.push_back(normal); + } + + return index + 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::VertexEntry::Constructor +// Access: Public +// Description: Creates a VertexEntry from the n/n/n string format in +// the obj file face reference. +//////////////////////////////////////////////////////////////////// +ObjToEggConverter::VertexEntry:: +VertexEntry(const ObjToEggConverter *converter, const string &obj_vertex) { + _vi = 0; + _vti = 0; + _vni = 0; + _synth_vni = 0; + + vector_string words; + tokenize(obj_vertex, words, "/", false); + nassertv(!words.empty()); + + for (size_t i = 0; i < words.size(); ++i) { + int index; + if (trim(words[i]).empty()) { + index = 0; + } else { + if (!string_to_int(words[i], index)) { + index = 0; + } + } + + switch (i) { + case 0: + _vi = index; + if (_vi < 0) { + _vi = (int)converter->_v_table.size() + _vi; + } + if (_vi < 0 || _vi - 1 >= (int)converter->_v_table.size()) { + _vi = 0; + } + break; + + case 1: + _vti = index; + if (_vti < 0) { + _vti = (int)converter->_vt_table.size() + _vti; + } + if (_vti < 0 || _vti - 1 >= (int)converter->_vt_table.size()) { + _vti = 0; + } + break; + + case 2: + _vni = index; + if (_vni < 0) { + _vni = (int)converter->_vn_table.size() + _vni; + } + if (_vni < 0 || _vni - 1 >= (int)converter->_vn_table.size()) { + _vni = 0; + } + break; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::VertexData::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ObjToEggConverter::VertexData:: +VertexData(PandaNode *parent, const string &name) : + _parent(parent), _name(name) +{ + _geom_node = NULL; + + _v4_given = false; + _vt3_given = false; + _vt_given = false; + _rgb_given = false; + _vn_given = false; + + _prim = new GeomTriangles(GeomEnums::UH_static); +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::VertexData::add_vertex +// Access: Public +// Description: Adds a new entry to the vertex data for the indicated +// VertexEntry, or returns an equivalent vertex already +// present. +//////////////////////////////////////////////////////////////////// +int ObjToEggConverter::VertexData:: +add_vertex(const ObjToEggConverter *converter, const VertexEntry &entry) { + pair result; + UniqueVertexEntries::iterator ni; + int index; + + if (entry._vni != 0 || entry._synth_vni != 0) { + // If we are storing a vertex with a normal, see if we + // have already stored a vertex without a normal first. + VertexEntry no_normal(entry); + no_normal._vni = 0; + no_normal._synth_vni = 0; + ni = _unique_entries.find(no_normal); + if (ni != _unique_entries.end()) { + // We did have such a vertex! In this case, repurpose this + // vertex, resetting it to contain this normal. + index = (*ni).second; + _unique_entries.erase(ni); + result = _unique_entries.insert(UniqueVertexEntries::value_type(entry, index)); + nassertr(result.second, index); + nassertr(_entries[index] == no_normal, index); + _entries[index]._vni = entry._vni; + _entries[index]._synth_vni = entry._synth_vni; + return index; + } + } else if (entry._vni == 0 && entry._synth_vni == 0) { + // If we are storing a vertex *without* any normal, see if we have + // already stored a vertex with a normal first. + ni = _unique_entries.lower_bound(entry); + if (ni != _unique_entries.end() && (*ni).first.matches_except_normal(entry)) { + // We had such a vertex, so use it. + index = (*ni).second; + return index; + } + } + + // We didn't already have a vertex we could repurpose, so try to add + // exactly the desired vertex. + result = _unique_entries.insert(UniqueVertexEntries::value_type(entry, _entries.size())); + ni = result.first; + index = (*ni).second; + + if (result.second) { + // If the vertex was added to the table, it's a unique vertex, and + // now we have to add it to the vertex data too. + _entries.push_back(entry); + + if (converter->_v4_given) { + _v4_given = true; + } + if (converter->_vt3_given) { + _vt3_given = true; + } + if (entry._vti != 0) { + _vt_given = true; + } else if (entry._vi - 1 < (int)converter->_xvt_table.size()) { + // We have an xvt texture coordinate. + _vt_given = true; + } + if (entry._vi - 1 < (int)converter->_rgb_table.size()) { + // We have a per-vertex color too. + _rgb_given = true; + } + if (entry._vni != 0) { + _vn_given = true; + } + } + + return index; +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::VertexData::add_triangle +// Access: Public +// Description: Adds a triangle to the primitive, as a triple of +// three VertexEntry objects, which are each added to +// the vertex pool. If synth_vni is not 0, it is +// assigned to the last vertex. +//////////////////////////////////////////////////////////////////// +void ObjToEggConverter::VertexData:: +add_triangle(const ObjToEggConverter *converter, const VertexEntry &v0, + const VertexEntry &v1, const VertexEntry &v2, + int synth_vni) { + int v0i, v1i, v2i; + + v0i = add_vertex(converter, v0); + v1i = add_vertex(converter, v1); + + if (synth_vni != 0) { + VertexEntry v2n(v2); + v2n._synth_vni = synth_vni; + v2i = add_vertex(converter, v2n); + } else { + v2i = add_vertex(converter, v2); + } + + _prim->add_vertices(v0i, v1i, v2i); + _prim->close_primitive(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjToEggConverter::VertexData::close_geom +// Access: Public +// Description: Finishes the current geom and stores it as a child +// in the root. Prepares for new geoms. +//////////////////////////////////////////////////////////////////// +void ObjToEggConverter::VertexData:: +close_geom(const ObjToEggConverter *converter) { + if (_prim->get_num_vertices() != 0) { + // Create a new format that includes only the columns we actually + // used. + PT(GeomVertexArrayFormat) aformat = new GeomVertexArrayFormat; + if (_v4_given) { + aformat->add_column(InternalName::get_vertex(), 4, + GeomEnums::NT_stdfloat, GeomEnums::C_point); + } else { + aformat->add_column(InternalName::get_vertex(), 3, + GeomEnums::NT_stdfloat, GeomEnums::C_point); + } + + // We always add normals--if no normals appeared in the file, we + // synthesize them. + aformat->add_column(InternalName::get_normal(), 3, + GeomEnums::NT_stdfloat, GeomEnums::C_vector); + + if (_vt_given) { + if (_vt3_given) { + aformat->add_column(InternalName::get_texcoord(), 3, + GeomEnums::NT_stdfloat, GeomEnums::C_texcoord); + } else { + aformat->add_column(InternalName::get_texcoord(), 2, + GeomEnums::NT_stdfloat, GeomEnums::C_texcoord); + } + } + + if (_rgb_given) { + aformat->add_column(InternalName::get_color(), 4, + GeomEnums::NT_uint8, GeomEnums::C_color); + } + + CPT(GeomVertexFormat) format = GeomVertexFormat::register_format(aformat); + + // Create and populate the vertex data. + PT(GeomVertexData) vdata = new GeomVertexData(_name, format, GeomEnums::UH_static); + GeomVertexWriter vertex_writer(vdata, InternalName::get_vertex()); + GeomVertexWriter normal_writer(vdata, InternalName::get_normal()); + GeomVertexWriter texcoord_writer(vdata, InternalName::get_texcoord()); + GeomVertexWriter color_writer(vdata, InternalName::get_color()); + + for (size_t i = 0; i < _entries.size(); ++i) { + const VertexEntry &entry = _entries[i]; + + if (entry._vi != 0) { + vertex_writer.set_row(i); + vertex_writer.add_data4d(converter->_v_table[entry._vi - 1]); + } + if (entry._vti != 0) { + texcoord_writer.set_row(i); + texcoord_writer.add_data3d(converter->_vt_table[entry._vti - 1]); + } else if (entry._vi - 1 < (int)converter->_xvt_table.size()) { + // We have an xvt texture coordinate. + texcoord_writer.set_row(i); + texcoord_writer.add_data2d(converter->_xvt_table[entry._vi - 1]); + } + if (entry._vni != 0) { + normal_writer.set_row(i); + normal_writer.add_data3d(converter->_vn_table[entry._vni - 1]); + } else if (entry._synth_vni != 0) { + normal_writer.set_row(i); + normal_writer.add_data3d(converter->_synth_vn_table[entry._synth_vni - 1]); + } else { + // In this case, the normal isn't used and doesn't matter; we + // fill it in a unit vector just for neatness. + normal_writer.set_row(i); + normal_writer.add_data3d(0, 0, 1); + } + if (_rgb_given) { + if (entry._vi - 1 < (int)converter->_rgb_table.size()) { + color_writer.set_row(i); + color_writer.add_data3d(converter->_rgb_table[entry._vi - 1]); + } + } + } + + // Transform to zup-right. + vdata->transform_vertices(LMatrix4::convert_mat(CS_zup_right, CS_default)); + + // Now create a Geom with this data. + CPT(RenderState) state = RenderState::make_empty(); + if (_rgb_given) { + state = state->add_attrib(ColorAttrib::make_vertex()); + } else { + state = state->add_attrib(ColorAttrib::make_flat(LColor(1, 1, 1, 1))); + } + if (!_vn_given) { + // We have synthesized these normals; specify the flat-shading + // attrib. + state = state->add_attrib(ShadeModelAttrib::make(ShadeModelAttrib::M_flat)); + _prim->set_shade_model(GeomEnums::SM_flat_last_vertex); + } + + PT(Geom) geom = new Geom(vdata); + geom->add_primitive(_prim); + + if (_geom_node == NULL) { + _geom_node = new GeomNode(_name); + _parent->add_child(_geom_node); + } + + _geom_node->add_geom(geom, state); + } + + _prim = new GeomTriangles(GeomEnums::UH_static); + _entries.clear(); + _unique_entries.clear(); +} diff --git a/pandatool/src/objegg/objToEggConverter.h b/pandatool/src/objegg/objToEggConverter.h index ba537dbe2a..66fb6d7f37 100644 --- a/pandatool/src/objegg/objToEggConverter.h +++ b/pandatool/src/objegg/objToEggConverter.h @@ -1,152 +1,152 @@ -// Filename: ObjToEggConverter.h -// Created by: drose (07Dec10) -// -//////////////////////////////////////////////////////////////////// -// -// 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 OBJTOEGGCONVERTER_H -#define OBJTOEGGCONVERTER_H - -#include "pandatoolbase.h" - -#include "somethingToEggConverter.h" -#include "eggVertexPool.h" -#include "eggGroup.h" -#include "geomVertexData.h" -#include "geomVertexWriter.h" -#include "geomPrimitive.h" -#include "geomNode.h" -#include "pandaNode.h" -#include "pvector.h" -#include "epvector.h" - -//////////////////////////////////////////////////////////////////// -// Class : ObjToEggConverter -// Description : Convert an Obj file to egg data. -//////////////////////////////////////////////////////////////////// -class ObjToEggConverter : public SomethingToEggConverter { -public: - ObjToEggConverter(); - ObjToEggConverter(const ObjToEggConverter ©); - ~ObjToEggConverter(); - - virtual SomethingToEggConverter *make_copy(); - - virtual string get_name() const; - virtual string get_extension() const; - virtual bool supports_compressed() const; - virtual bool supports_convert_to_node(const LoaderOptions &options) const; - - virtual bool convert_file(const Filename &filename); - virtual PT(PandaNode) convert_to_node(const LoaderOptions &options, const Filename &filename); - -protected: - bool process(const Filename &filename); - bool process_line(const string &line); - bool process_ref_plane_res(const string &line); - - bool process_v(vector_string &words); - bool process_vt(vector_string &words); - bool process_xvt(vector_string &words); - bool process_xvc(vector_string &words); - bool process_vn(vector_string &words); - bool process_f(vector_string &words); - bool process_g(vector_string &words); - - EggVertex *get_face_vertex(const string &face_reference); - void generate_egg_points(); - - bool process_node(const Filename &filename); - bool process_line_node(const string &line); - - bool process_f_node(vector_string &words); - bool process_g_node(vector_string &words); - - void generate_points(); - int add_synth_normal(const LVecBase3d &normal); - - // Read from the obj file. - int _line_number; - typedef epvector Vec4Table; - typedef epvector Vec3Table; - typedef epvector Vec2Table; - typedef pmap UniqueVec3Table; - - Vec4Table _v_table; - Vec3Table _vn_table, _rgb_table; - Vec3Table _vt_table; - Vec2Table _xvt_table; - Vec3Table _synth_vn_table; - UniqueVec3Table _unique_synth_vn_table; - LVecBase2d _ref_plane_res; - bool _v4_given, _vt3_given; - bool _f_given; - - pset _ignored_tags; - - // Structures filled when creating an egg file. - PT(EggVertexPool) _vpool; - PT(EggGroup) _root_group; - EggGroup *_current_group; - - // Structures filled when creating a PandaNode directly. - PT(PandaNode) _root_node; - - class VertexEntry { - public: - VertexEntry(); - VertexEntry(const ObjToEggConverter *converter, const string &obj_vertex); - - INLINE bool operator < (const VertexEntry &other) const; - INLINE bool operator == (const VertexEntry &other) const; - INLINE bool matches_except_normal(const VertexEntry &other) const; - - // The 1-based vertex, texcoord, and normal index numbers - // appearing in the obj file for this vertex. 0 if the index - // number is not given. - int _vi, _vti, _vni; - - // The 1-based index number to the synthesized normal, if needed. - int _synth_vni; - }; - typedef pmap UniqueVertexEntries; - typedef pvector VertexEntries; - - class VertexData { - public: - VertexData(PandaNode *parent, const string &name); - - int add_vertex(const ObjToEggConverter *converter, const VertexEntry &entry); - void add_triangle(const ObjToEggConverter *converter, const VertexEntry &v0, - const VertexEntry &v1, const VertexEntry &v2, - int synth_vni); - void close_geom(const ObjToEggConverter *converter); - - PT(PandaNode) _parent; - string _name; - PT(GeomNode) _geom_node; - - PT(GeomPrimitive) _prim; - VertexEntries _entries; - UniqueVertexEntries _unique_entries; - - bool _v4_given, _vt3_given; - bool _vt_given, _rgb_given, _vn_given; - }; - - VertexData *_current_vertex_data; - - friend class VertexData; -}; - -#include "objToEggConverter.I" - -#endif +// Filename: ObjToEggConverter.h +// Created by: drose (07Dec10) +// +//////////////////////////////////////////////////////////////////// +// +// 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 OBJTOEGGCONVERTER_H +#define OBJTOEGGCONVERTER_H + +#include "pandatoolbase.h" + +#include "somethingToEggConverter.h" +#include "eggVertexPool.h" +#include "eggGroup.h" +#include "geomVertexData.h" +#include "geomVertexWriter.h" +#include "geomPrimitive.h" +#include "geomNode.h" +#include "pandaNode.h" +#include "pvector.h" +#include "epvector.h" + +//////////////////////////////////////////////////////////////////// +// Class : ObjToEggConverter +// Description : Convert an Obj file to egg data. +//////////////////////////////////////////////////////////////////// +class ObjToEggConverter : public SomethingToEggConverter { +public: + ObjToEggConverter(); + ObjToEggConverter(const ObjToEggConverter ©); + ~ObjToEggConverter(); + + virtual SomethingToEggConverter *make_copy(); + + virtual string get_name() const; + virtual string get_extension() const; + virtual bool supports_compressed() const; + virtual bool supports_convert_to_node(const LoaderOptions &options) const; + + virtual bool convert_file(const Filename &filename); + virtual PT(PandaNode) convert_to_node(const LoaderOptions &options, const Filename &filename); + +protected: + bool process(const Filename &filename); + bool process_line(const string &line); + bool process_ref_plane_res(const string &line); + + bool process_v(vector_string &words); + bool process_vt(vector_string &words); + bool process_xvt(vector_string &words); + bool process_xvc(vector_string &words); + bool process_vn(vector_string &words); + bool process_f(vector_string &words); + bool process_g(vector_string &words); + + EggVertex *get_face_vertex(const string &face_reference); + void generate_egg_points(); + + bool process_node(const Filename &filename); + bool process_line_node(const string &line); + + bool process_f_node(vector_string &words); + bool process_g_node(vector_string &words); + + void generate_points(); + int add_synth_normal(const LVecBase3d &normal); + + // Read from the obj file. + int _line_number; + typedef epvector Vec4Table; + typedef epvector Vec3Table; + typedef epvector Vec2Table; + typedef pmap UniqueVec3Table; + + Vec4Table _v_table; + Vec3Table _vn_table, _rgb_table; + Vec3Table _vt_table; + Vec2Table _xvt_table; + Vec3Table _synth_vn_table; + UniqueVec3Table _unique_synth_vn_table; + LVecBase2d _ref_plane_res; + bool _v4_given, _vt3_given; + bool _f_given; + + pset _ignored_tags; + + // Structures filled when creating an egg file. + PT(EggVertexPool) _vpool; + PT(EggGroup) _root_group; + EggGroup *_current_group; + + // Structures filled when creating a PandaNode directly. + PT(PandaNode) _root_node; + + class VertexEntry { + public: + VertexEntry(); + VertexEntry(const ObjToEggConverter *converter, const string &obj_vertex); + + INLINE bool operator < (const VertexEntry &other) const; + INLINE bool operator == (const VertexEntry &other) const; + INLINE bool matches_except_normal(const VertexEntry &other) const; + + // The 1-based vertex, texcoord, and normal index numbers + // appearing in the obj file for this vertex. 0 if the index + // number is not given. + int _vi, _vti, _vni; + + // The 1-based index number to the synthesized normal, if needed. + int _synth_vni; + }; + typedef pmap UniqueVertexEntries; + typedef pvector VertexEntries; + + class VertexData { + public: + VertexData(PandaNode *parent, const string &name); + + int add_vertex(const ObjToEggConverter *converter, const VertexEntry &entry); + void add_triangle(const ObjToEggConverter *converter, const VertexEntry &v0, + const VertexEntry &v1, const VertexEntry &v2, + int synth_vni); + void close_geom(const ObjToEggConverter *converter); + + PT(PandaNode) _parent; + string _name; + PT(GeomNode) _geom_node; + + PT(GeomPrimitive) _prim; + VertexEntries _entries; + UniqueVertexEntries _unique_entries; + + bool _v4_given, _vt3_given; + bool _vt_given, _rgb_given, _vn_given; + }; + + VertexData *_current_vertex_data; + + friend class VertexData; +}; + +#include "objToEggConverter.I" + +#endif