diff --git a/direct/src/showbase/Loader.py b/direct/src/showbase/Loader.py index 51a5c8b9cd..2f4b81e411 100644 --- a/direct/src/showbase/Loader.py +++ b/direct/src/showbase/Loader.py @@ -289,6 +289,75 @@ class Loader(DirectObject): assert Loader.notify.debug("Unloading model: %s" % (modelNode.getFullpath())) ModelPool.releaseModel(modelNode) + def saveModel(self, modelPath, node, loaderOptions = None, + callback = None, extraArgs = [], priority = None): + """ Saves the model (a NodePath or PandaNode) to the indicated + filename path. Returns true on success, false on failure. If + a callback is used, the model is saved asynchronously, and the + true/false status is passed to the callback function. """ + + if loaderOptions == None: + loaderOptions = LoaderOptions() + else: + loaderOptions = LoaderOptions(loaderOptions) + + if isinstance(modelPath, types.StringTypes) or \ + isinstance(modelPath, Filename): + # We were given a single model pathname. + modelList = [modelPath] + nodeList = [node] + if phaseChecker: + phaseChecker(modelPath, loaderOptions) + + gotList = False + else: + # Assume we were given a list of model pathnames. + modelList = modelPath + nodeList = node + gotList = True + + assert(len(modelList) == len(nodeList)) + + # Make sure we have PandaNodes, not NodePaths. + for i in range(len(nodeList)): + if isinstance(nodeList[i], NodePath): + nodeList[i] = nodeList[i].node() + + # From here on, we deal with a list of (filename, node) pairs. + modelList = zip(modelList, nodeList) + + if callback is None: + # We got no callback, so it's a synchronous save. + + result = [] + for modelPath, node in modelList: + thisResult = self.loader.saveSync(Filename(modelPath), loaderOptions, node) + result.append(thisResult) + + if gotList: + return result + else: + return result[0] + + else: + # We got a callback, so we want an asynchronous (threaded) + # save. We'll return immediately, but when all of the + # requested models have been saved, we'll invoke the + # callback (passing it the models on the parameter list). + + cb = Loader.Callback(len(modelList), gotList, callback, extraArgs) + i=0 + for modelPath, node in modelList: + request = self.loader.makeAsyncSaveRequest(Filename(modelPath), loaderOptions, node) + if priority is not None: + request.setPriority(priority) + request.setDoneEvent(self.hook) + request.setPythonObject((cb, i)) + i+=1 + self.loader.saveAsync(request) + cb.requests[request] = True + return cb + # font loading funcs def loadFont(self, modelPath, @@ -875,4 +944,7 @@ class Loader(DirectObject): elif hasattr(request, "getSound"): object = request.getSound() + elif hasattr(request, "getSuccess"): + object = request.getSuccess() + cb.gotObject(i, object) diff --git a/panda/src/egg2pg/Sources.pp b/panda/src/egg2pg/Sources.pp index f9c3cbefa8..9ec47767e4 100644 --- a/panda/src/egg2pg/Sources.pp +++ b/panda/src/egg2pg/Sources.pp @@ -20,8 +20,10 @@ eggBinner.h \ eggLoader.h eggLoader.I \ eggRenderState.h eggRenderState.I \ + eggSaver.h eggSaver.I \ egg_parametrics.h \ load_egg_file.h \ + save_egg_file.h \ loaderFileTypeEgg.h #define INCLUDED_SOURCES \ @@ -32,8 +34,10 @@ eggBinner.cxx \ eggLoader.cxx \ eggRenderState.cxx \ + eggSaver.cxx \ egg_parametrics.cxx \ load_egg_file.cxx \ + save_egg_file.cxx \ loaderFileTypeEgg.cxx #if $[DONT_COMBINE_PGRAPH] @@ -43,8 +47,8 @@ #endif #define INSTALL_HEADERS \ - egg_parametrics.h load_egg_file.h config_egg2pg.h + egg_parametrics.h load_egg_file.h save_egg_file.h config_egg2pg.h - #define IGATESCAN load_egg_file.h + #define IGATESCAN load_egg_file.h save_egg_file.h #end lib_target diff --git a/panda/src/egg2pg/eggSaver.I b/panda/src/egg2pg/eggSaver.I new file mode 100755 index 0000000000..97a40f1764 --- /dev/null +++ b/panda/src/egg2pg/eggSaver.I @@ -0,0 +1,25 @@ +// Filename: eggSaver.I +// 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." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::get_egg_data +// Access: Published +// Description: Returns the EggData populated within this class. +//////////////////////////////////////////////////////////////////// +INLINE EggData *EggSaver:: +get_egg_data() const { + return _data; +} + diff --git a/panda/src/egg2pg/eggSaver.cxx b/panda/src/egg2pg/eggSaver.cxx new file mode 100755 index 0000000000..2ed7fe27d8 --- /dev/null +++ b/panda/src/egg2pg/eggSaver.cxx @@ -0,0 +1,1132 @@ +// Filename: eggSaver.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 "eggSaver.h" + +#include "pandaNode.h" +#include "workingNodePath.h" +#include "nodePath.h" +#include "billboardEffect.h" +#include "renderEffects.h" +#include "transformState.h" +#include "colorScaleAttrib.h" +#include "colorAttrib.h" +#include "textureAttrib.h" +#include "cullFaceAttrib.h" +#include "transparencyAttrib.h" +#include "depthWriteAttrib.h" +#include "lodNode.h" +#include "switchNode.h" +#include "sequenceNode.h" +#include "collisionNode.h" +#include "collisionPolygon.h" +#include "collisionPlane.h" +#include "collisionSphere.h" +#include "collisionInvSphere.h" +#include "collisionTube.h" +#include "textureStage.h" +#include "geomNode.h" +#include "geom.h" +#include "geomTriangles.h" +#include "geomPatches.h" +#include "geomPoints.h" +#include "geomLines.h" +#include "geomVertexReader.h" +#include "transformTable.h" +#include "modelNode.h" +#include "animBundleNode.h" +#include "animChannelMatrixXfmTable.h" +#include "characterJoint.h" +#include "character.h" +#include "string_utils.h" +#include "bamFile.h" +#include "bamCacheRecord.h" +#include "eggSAnimData.h" +#include "eggXfmAnimData.h" +#include "eggXfmSAnim.h" +#include "eggGroup.h" +#include "eggVertexPool.h" +#include "eggVertex.h" +#include "eggPrimitive.h" +#include "eggPolygon.h" +#include "eggPatch.h" +#include "eggPoint.h" +#include "eggLine.h" +#include "eggTexture.h" +#include "eggMaterial.h" +#include "eggRenderMode.h" +#include "eggTable.h" +#include "dcast.h" + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +EggSaver:: +EggSaver(EggData *data) : + _data(data) +{ + if (_data == NULL) { + _data = new EggData; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::add_node +// Access: Published +// Description: Adds the scene graph rooted at the indicated node to +// the accumulated egg data within this object. Call +// get_egg_data() to retrieve the result. +//////////////////////////////////////////////////////////////////// +void EggSaver:: +add_node(PandaNode *node) { + _vpool = new EggVertexPool(node->get_name()); + _data->add_child(_vpool); + + NodePath root(node); + convert_node(WorkingNodePath(root), _data, false); + + // Remove the vertex pool if it has no vertices. + if (_vpool->empty()) { + _data->remove_child(_vpool); + } + _vpool = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::convert_node +// Access: Private +// Description: Converts the indicated node to the corresponding Egg +// constructs, by first determining what kind of node it +// is. +//////////////////////////////////////////////////////////////////// +void EggSaver:: +convert_node(const WorkingNodePath &node_path, EggGroupNode *egg_parent, + bool has_decal) { + PandaNode *node = node_path.node(); + if (node->is_geom_node()) { + convert_geom_node(DCAST(GeomNode, node), node_path, egg_parent, has_decal); + + } else if (node->is_of_type(LODNode::get_class_type())) { + convert_lod_node(DCAST(LODNode, node), node_path, egg_parent, has_decal); + + } else if (node->is_of_type(SequenceNode::get_class_type())) { + convert_sequence_node(DCAST(SequenceNode, node), node_path, egg_parent, has_decal); + + } else if (node->is_of_type(SwitchNode::get_class_type())) { + convert_switch_node(DCAST(SwitchNode, node), node_path, egg_parent, has_decal); + + } else if (node->is_of_type(CollisionNode::get_class_type())) { + convert_collision_node(DCAST(CollisionNode, node), node_path, egg_parent, has_decal); + + } else if (node->is_of_type(AnimBundleNode::get_class_type())) { + convert_anim_node(DCAST(AnimBundleNode, node), node_path, egg_parent, has_decal); + + } else if (node->is_of_type(Character::get_class_type())) { + convert_character_node(DCAST(Character, node), node_path, egg_parent, has_decal); + + } else { + // Just a generic node. + EggGroup *egg_group = new EggGroup(node->get_name()); + egg_parent->add_child(egg_group); + apply_node_properties(egg_group, node); + + recurse_nodes(node_path, egg_group, has_decal); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::convert_lod_node +// Access: Private +// Description: Converts the indicated LODNode to the corresponding +// Egg constructs. +//////////////////////////////////////////////////////////////////// +void EggSaver:: +convert_lod_node(LODNode *node, const WorkingNodePath &node_path, + EggGroupNode *egg_parent, bool has_decal) { + // An LOD node gets converted to an ordinary EggGroup, but we apply + // the appropriate switch conditions to each of our children. + EggGroup *egg_group = new EggGroup(node->get_name()); + egg_parent->add_child(egg_group); + apply_node_properties(egg_group, node); + + int num_children = node->get_num_children(); + int num_switches = node->get_num_switches(); + + num_children = min(num_children, num_switches); + + for (int i = 0; i < num_children; i++) { + PandaNode *child = node->get_child(i); + + // Convert just this one node to an EggGroup. + PT(EggGroup) next_group = new EggGroup; + convert_node(WorkingNodePath(node_path, child), next_group, has_decal); + + if (next_group->size() == 1) { + // If we have exactly one child, and that child is an EggGroup, + // collapse. + EggNode *child_node = *next_group->begin(); + if (child_node->is_of_type(EggGroup::get_class_type())) { + PT(EggGroup) child = DCAST(EggGroup, child_node); + next_group->remove_child(child.p()); + next_group = child; + } + } + + // Now set up the switching properties appropriately. + PN_stdfloat in = node->get_in(i); + PN_stdfloat out = node->get_out(i); + LPoint3 center = node->get_center(); + EggSwitchConditionDistance dist(in, out, LCAST(double, center)); + next_group->set_lod(dist); + egg_group->add_child(next_group.p()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::convert_sequence_node +// Access: Private +// Description: Converts the indicated SequenceNode to the corresponding +// Egg constructs. +//////////////////////////////////////////////////////////////////// +void EggSaver:: +convert_sequence_node(SequenceNode *node, const WorkingNodePath &node_path, + EggGroupNode *egg_parent, bool has_decal) { + // A sequence node gets converted to an ordinary EggGroup, we only apply + // the appropriate switch attributes to turn it into a sequence + EggGroup *egg_group = new EggGroup(node->get_name()); + egg_parent->add_child(egg_group); + apply_node_properties(egg_group, node); + + // turn it into a sequence with the right frame-rate + egg_group->set_switch_flag(true); + egg_group->set_switch_fps(node->get_frame_rate()); + + int num_children = node->get_num_children(); + + for (int i = 0; i < num_children; i++) { + PandaNode *child = node->get_child(i); + + // Convert just this one node to an EggGroup. + PT(EggGroup) next_group = new EggGroup; + convert_node(WorkingNodePath(node_path, child), next_group, has_decal); + + egg_group->add_child(next_group.p()); + } + +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::convert_switch_node +// Access: Private +// Description: Converts the indicated SwitchNode to the corresponding +// Egg constructs. +//////////////////////////////////////////////////////////////////// +void EggSaver:: +convert_switch_node(SwitchNode *node, const WorkingNodePath &node_path, + EggGroupNode *egg_parent, bool has_decal) { + // A sequence node gets converted to an ordinary EggGroup, we only apply + // the appropriate switch attributes to turn it into a sequence + EggGroup *egg_group = new EggGroup(node->get_name()); + egg_parent->add_child(egg_group); + apply_node_properties(egg_group, node); + + // turn it into a switch.. + egg_group->set_switch_flag(true); + + int num_children = node->get_num_children(); + + for (int i = 0; i < num_children; i++) { + PandaNode *child = node->get_child(i); + + // Convert just this one node to an EggGroup. + PT(EggGroup) next_group = new EggGroup; + convert_node(WorkingNodePath(node_path, child), next_group, has_decal); + + egg_group->add_child(next_group.p()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::convert_animGroup_node +// Access: Private +// Description: Converts the indicated AnimationGroupNodes to the corresponding +// Egg constructs. +//////////////////////////////////////////////////////////////////// +EggGroupNode * EggSaver::convert_animGroup_node(AnimGroup *animGroup, double fps ) { + int num_children = animGroup->get_num_children(); + + EggGroupNode *eggNode = NULL; + if (animGroup->is_of_type(AnimBundle::get_class_type())) { + EggTable *eggTable = new EggTable(animGroup->get_name()); + eggTable ->set_table_type(EggTable::TT_bundle); + eggNode = eggTable; + } else if (animGroup->is_of_type(AnimGroup::get_class_type())) { + EggTable *eggTable = new EggTable(animGroup->get_name()); + eggTable ->set_table_type(EggTable::TT_table); + eggNode = eggTable; + } + + if (animGroup->is_of_type(AnimChannelMatrixXfmTable::get_class_type())) { + AnimChannelMatrixXfmTable *xmfTable = DCAST(AnimChannelMatrixXfmTable, animGroup); + EggXfmSAnim *egg_anim = new EggXfmSAnim("xform"); + egg_anim->set_fps(fps); + for (int i = 0; i < num_matrix_components; i++) { + string componentName(1, matrix_component_letters[i]); + char table_id = matrix_component_letters[i]; + CPTA_stdfloat table = xmfTable->get_table(table_id); + + if (xmfTable->has_table(table_id)) { + for (unsigned int j = 0; j < table.size(); j++) { + egg_anim->add_component_data(componentName, table[(int)j]); + } + } + } + eggNode->add_child(egg_anim); + } + for (int i = 0; i < num_children; i++) { + AnimGroup *animChild = animGroup->get_child(i); + EggGroupNode *eggChildNode = convert_animGroup_node(animChild, fps); + if (eggChildNode!=NULL) { + nassertr(eggNode!=NULL, NULL); + eggNode->add_child(eggChildNode); + } + } + return eggNode; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::convert_anim_node +// Access: Private +// Description: Converts the indicated AnimNode to the corresponding +// Egg constructs. +//////////////////////////////////////////////////////////////////// +void EggSaver:: +convert_anim_node(AnimBundleNode *node, const WorkingNodePath &node_path, + EggGroupNode *egg_parent, bool has_decal) { + + // A sequence node gets converted to an ordinary EggGroup, we only apply + // the appropriate switch attributes to turn it into a sequence + EggTable *eggTable = new EggTable(); + //egg_parent->add_child(eggTable); + _data->add_child(eggTable); + + AnimBundle *animBundle = node->get_bundle(); + // turn it into a switch.. + //egg_group->set_switch_flag(true); + + EggGroupNode *eggAnimation = convert_animGroup_node(animBundle, animBundle->get_base_frame_rate()); + eggTable->add_child(eggAnimation); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::convert_character_bundle +// Access: Private +// Description: Converts the indicated Character Bundle to the corresponding +// Egg joints structure. +//////////////////////////////////////////////////////////////////// +void EggSaver:: +convert_character_bundle(PartGroup *bundleNode, EggGroupNode *egg_parent, CharacterJointMap *jointMap) { + int num_children = bundleNode->get_num_children(); + + EggGroupNode *joint_group = egg_parent; + if (bundleNode->is_of_type(CharacterJoint::get_class_type())) { + CharacterJoint *character_joint = DCAST(CharacterJoint, bundleNode); + + LMatrix4 transformf; + character_joint->get_net_transform(transformf); + LMatrix4d transformd(LCAST(double, transformf)); + EggGroup *joint = new EggGroup(bundleNode->get_name()); + joint->add_matrix4(transformd); + joint->set_group_type(EggGroup::GT_joint); + joint_group = joint; + egg_parent->add_child(joint_group); + if (jointMap!=NULL) { + CharacterJointMap::iterator mi = jointMap->find(character_joint); + if (mi != jointMap->end()) { + pvector > &joint_vertices = (*mi).second; + pvector >::const_iterator vi; + for (vi = joint_vertices.begin(); vi != joint_vertices.end(); ++vi) { + joint->set_vertex_membership((*vi).first, (*vi).second); + } + } + } + } + + for (int i = 0; i < num_children ; i++) { + PartGroup *partGroup= bundleNode->get_child(i); + convert_character_bundle(partGroup, joint_group, jointMap); + } + +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::convert_character_node +// Access: Private +// Description: Converts the indicated Character to the corresponding +// Egg constructs. +//////////////////////////////////////////////////////////////////// +void EggSaver:: +convert_character_node(Character *node, const WorkingNodePath &node_path, + EggGroupNode *egg_parent, bool has_decal) { + + // A sequence node gets converted to an ordinary EggGroup, we only apply + // the appropriate switch attributes to turn it into a sequence + EggGroup *egg_group = new EggGroup(node->get_name()); + egg_group->set_dart_type(EggGroup::DT_default); + egg_parent->add_child(egg_group); + apply_node_properties(egg_group, node); + + CharacterJointMap jointMap; + + // turn it into a switch.. + //egg_group->set_switch_flag(true); + + int num_children = node->get_num_children(); + int num_bundles = node->get_num_bundles(); + + for (int i = 0; i < num_children; i++) { + PandaNode *child = node->get_child(i); + + if (child->is_geom_node()) { + convert_geom_node(DCAST(GeomNode, child), WorkingNodePath(node_path, child), egg_group, has_decal, &jointMap); + } + } + + for (int i = 0; i < num_bundles ; i++) { + PartBundle *bundle= node->get_bundle(i); + convert_character_bundle(bundle, egg_group, &jointMap); + } + +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::convert_collision_node +// Access: Private +// Description: Converts the indicated CollisionNode to the corresponding +// Egg constructs. +//////////////////////////////////////////////////////////////////// +void EggSaver:: +convert_collision_node(CollisionNode *node, const WorkingNodePath &node_path, + EggGroupNode *egg_parent, bool has_decal) { + // A sequence node gets converted to an ordinary EggGroup, we only apply + // the appropriate switch attributes to turn it into a sequence + EggGroup *egg_group = new EggGroup(node->get_name()); + egg_parent->add_child(egg_group); + apply_node_properties(egg_group, node, false); + + // turn it into a collision node + egg_group->set_cs_type(EggGroup::CST_polyset); + egg_group->set_collide_flags(EggGroup::CF_descend); + + NodePath np = node_path.get_node_path(); + CPT(TransformState) net_transform = np.get_net_transform(); + LMatrix4 net_mat = net_transform->get_mat(); + LMatrix4 inv = LCAST(PN_stdfloat, egg_parent->get_vertex_frame_inv()); + net_mat = net_mat * inv; + + int num_solids = node->get_num_solids(); + + if (num_solids > 0) { + // create vertex pool for collisions + EggVertexPool *cvpool = new EggVertexPool("vpool-collision"); + egg_group->add_child(cvpool); + + // traverse solids + for (int i = 0; i < num_solids; i++) { + CPT(CollisionSolid) child = node->get_solid(i); + if (child->is_of_type(CollisionPolygon::get_class_type())) { + EggPolygon *egg_poly = new EggPolygon; + egg_group->add_child(egg_poly); + + CPT(CollisionPolygon) poly = DCAST(CollisionPolygon, child); + int num_points = poly->get_num_points(); + for (int j = 0; j < num_points; j++) { + EggVertex egg_vert; + egg_vert.set_pos(LCAST(double, poly->get_point(j) * net_mat)); + egg_vert.set_normal(LCAST(double, poly->get_normal() * net_mat)); + + EggVertex *new_egg_vert = cvpool->create_unique_vertex(egg_vert); + egg_poly->add_vertex(new_egg_vert); + } + } else if (child->is_of_type(CollisionPlane::get_class_type())) { + nout << "Encountered unhandled collsion type: CollisionPlane" << "\n"; + } else if (child->is_of_type(CollisionSphere::get_class_type())) { + nout << "Encountered unhandled collsion type: CollisionSphere" << "\n"; + } else if (child->is_of_type(CollisionInvSphere::get_class_type())) { + nout << "Encountered unhandled collsion type: CollisionInvSphere" << "\n"; + } else if (child->is_of_type(CollisionTube::get_class_type())) { + nout << "Encountered unhandled collsion type: CollisionTube" << "\n"; + } else { + nout << "Encountered unknown CollisionSolid" << "\n"; + } + } + } + + // recurse over children - hm. do I need to do this? + recurse_nodes(node_path, egg_group, has_decal); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::convert_geom_node +// Access: Private +// Description: Converts a GeomNode to the corresponding egg +// structures. +//////////////////////////////////////////////////////////////////// +void EggSaver:: +convert_geom_node(GeomNode *node, const WorkingNodePath &node_path, + EggGroupNode *egg_parent, bool has_decal, CharacterJointMap *jointMap) { + PT(EggGroup) egg_group = new EggGroup(node->get_name()); + bool fancy_attributes = apply_node_properties(egg_group, node); + + if (node->get_effects()->has_decal()) { + has_decal = true; + } + + if (has_decal) { + egg_group->set_decal_flag(true); + } + + if (fancy_attributes || has_decal || !node->get_name().empty()) { + // If we have any fancy attributes on the node, or if we're making + // decal geometry, we have to make a special node to hold the + // geometry (normally it would just appear within its parent). + egg_parent->add_child(egg_group.p()); + egg_parent = egg_group; + } + + NodePath np = node_path.get_node_path(); + CPT(RenderState) net_state = np.get_net_state(); + CPT(TransformState) net_transform = np.get_net_transform(); + LMatrix4 net_mat = net_transform->get_mat(); + LMatrix4 inv = LCAST(PN_stdfloat, egg_parent->get_vertex_frame_inv()); + net_mat = net_mat * inv; + + // Now get out all the various kinds of geometry. + int num_geoms = node->get_num_geoms(); + for (int i = 0; i < num_geoms; ++i) { + CPT(RenderState) geom_state = net_state->compose(node->get_geom_state(i)); + + const Geom *geom = node->get_geom(i); + int num_primitives = geom->get_num_primitives(); + for (int j = 0; j < num_primitives; ++j) { + const GeomPrimitive *primitive = geom->get_primitive(j); + CPT(GeomPrimitive) simple = primitive->decompose(); + CPT(GeomVertexData) vdata = geom->get_vertex_data(); + // vdata = vdata->animate_vertices(true, Thread::get_current_thread()); + convert_primitive(vdata, simple, geom_state, + net_mat, egg_parent, jointMap); + } + } + + recurse_nodes(node_path, egg_parent, has_decal); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::convert_primitive +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void EggSaver:: +convert_primitive(const GeomVertexData *vertex_data, + const GeomPrimitive *primitive, + const RenderState *net_state, + const LMatrix4 &net_mat, EggGroupNode *egg_parent, + CharacterJointMap *jointMap) { + GeomVertexReader reader(vertex_data); + + // Check for a color scale. + LVecBase4 color_scale(1.0f, 1.0f, 1.0f, 1.0f); + const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, net_state->get_attrib(ColorScaleAttrib::get_class_type())); + if (csa != (const ColorScaleAttrib *)NULL) { + color_scale = csa->get_scale(); + } + + // Check for a color override. + bool has_color_override = false; + bool has_color_off = false; + LColor color_override; + const ColorAttrib *ca = DCAST(ColorAttrib, net_state->get_attrib(ColorAttrib::get_class_type())); + if (ca != (const ColorAttrib *)NULL) { + if (ca->get_color_type() == ColorAttrib::T_flat) { + has_color_override = true; + color_override = ca->get_color(); + color_override.set(color_override[0] * color_scale[0], + color_override[1] * color_scale[1], + color_override[2] * color_scale[2], + color_override[3] * color_scale[3]); + + } else if (ca->get_color_type() == ColorAttrib::T_off) { + has_color_off = true; + } + } + + // Check for a texture. + EggTexture *egg_tex = (EggTexture *)NULL; + const TextureAttrib *ta = DCAST(TextureAttrib, net_state->get_attrib(TextureAttrib::get_class_type())); + if (ta != (const TextureAttrib *)NULL) { + egg_tex = get_egg_texture(ta->get_texture()); + } + + // Check the texture environment + if ((ta != (const TextureAttrib *)NULL) && (egg_tex != (const EggTexture *)NULL)) { + TextureStage* tex_stage = ta->get_on_stage(0); + if (tex_stage != (const TextureStage *)NULL) { + switch (tex_stage->get_mode()) { + case TextureStage::M_modulate: + if (has_color_off == true) { + egg_tex->set_env_type(EggTexture::ET_replace); + } else { + egg_tex->set_env_type(EggTexture::ET_modulate); + } + break; + case TextureStage::M_decal: + egg_tex->set_env_type(EggTexture::ET_decal); + break; + case TextureStage::M_blend: + egg_tex->set_env_type(EggTexture::ET_blend); + break; + case TextureStage::M_replace: + egg_tex->set_env_type(EggTexture::ET_replace); + break; + case TextureStage::M_add: + egg_tex->set_env_type(EggTexture::ET_add); + break; + case TextureStage::M_blend_color_scale: + egg_tex->set_env_type(EggTexture::ET_blend_color_scale); + break; + default: + break; + } + } + } + + // Check the backface flag. + bool bface = false; + const RenderAttrib *cf_attrib = net_state->get_attrib(CullFaceAttrib::get_class_type()); + if (cf_attrib != (const RenderAttrib *)NULL) { + const CullFaceAttrib *cfa = DCAST(CullFaceAttrib, cf_attrib); + if (cfa->get_effective_mode() == CullFaceAttrib::M_cull_none) { + bface = true; + } + } + + // Check the depth write flag - only needed for AM_blend_no_occlude + bool has_depthwrite = false; + DepthWriteAttrib::Mode depthwrite = DepthWriteAttrib::M_on; + const RenderAttrib *dw_attrib = net_state->get_attrib(DepthWriteAttrib::get_class_type()); + if (dw_attrib != (const RenderAttrib *)NULL) { + const DepthWriteAttrib *dwa = DCAST(DepthWriteAttrib, dw_attrib); + depthwrite = dwa->get_mode(); + has_depthwrite = true; + } + + // Check the transparency flag. + bool has_transparency = false; + TransparencyAttrib::Mode transparency = TransparencyAttrib::M_none; + const RenderAttrib *tr_attrib = net_state->get_attrib(TransparencyAttrib::get_class_type()); + if (tr_attrib != (const RenderAttrib *)NULL) { + const TransparencyAttrib *tra = DCAST(TransparencyAttrib, tr_attrib); + transparency = tra->get_mode(); + has_transparency = true; + } + if (has_transparency && (egg_tex != (EggTexture *)NULL)) { + EggRenderMode::AlphaMode tex_trans = EggRenderMode::AM_unspecified; + switch (transparency) { + case TransparencyAttrib::M_none: + tex_trans = EggRenderMode::AM_off; + break; + case TransparencyAttrib::M_alpha: + if (has_depthwrite && (depthwrite == DepthWriteAttrib::M_off)) { + tex_trans = EggRenderMode::AM_blend_no_occlude; + has_depthwrite = false; + } else { + tex_trans = EggRenderMode::AM_blend; + } + break; + case TransparencyAttrib::M_multisample: + tex_trans = EggRenderMode::AM_ms; + break; + case TransparencyAttrib::M_multisample_mask: + tex_trans = EggRenderMode::AM_ms_mask; + break; + case TransparencyAttrib::M_binary: + tex_trans = EggRenderMode::AM_binary; + break; + case TransparencyAttrib::M_dual: + tex_trans = EggRenderMode::AM_dual; + break; + default: // intentional fall-through + case TransparencyAttrib::M_notused: + break; + } + if (tex_trans != EggRenderMode::AM_unspecified) { + egg_tex->set_alpha_mode(tex_trans); + } + } + + + LNormal normal; + LColor color; + CPT(TransformBlendTable) transformBlendTable = vertex_data->get_transform_blend_table(); + + int num_primitives = primitive->get_num_primitives(); + int num_vertices = primitive->get_num_vertices_per_primitive(); + + EggPrimitive *(*make_func)(void); + + if (primitive->is_of_type(GeomTriangles::get_class_type())) { + make_func = make_egg_polygon; + } else if (primitive->is_of_type(GeomPatches::get_class_type())) { + make_func = make_egg_patch; + } else if (primitive->is_of_type(GeomPoints::get_class_type())) { + make_func = make_egg_point; + } else if (primitive->is_of_type(GeomLines::get_class_type())) { + make_func = make_egg_line; + } else { + // Huh, an unknown geometry type. + return; + } + + for (int i = 0; i < num_primitives; ++i) { + PT(EggPrimitive) egg_prim = (*make_func)(); + + egg_parent->add_child(egg_prim); + if (egg_tex != (EggTexture *)NULL) { + egg_prim->set_texture(egg_tex); + } + + if (bface) { + egg_prim->set_bface_flag(true); + } + + for (int j = 0; j < num_vertices; j++) { + EggVertex egg_vert; + + // Get per-vertex properties. + reader.set_row(primitive->get_vertex(i * num_vertices + j)); + + reader.set_column(InternalName::get_vertex()); + LVertex vertex = reader.get_data3(); + egg_vert.set_pos(LCAST(double, vertex * net_mat)); + + if (vertex_data->has_column(InternalName::get_normal())) { + reader.set_column(InternalName::get_normal()); + LNormal normal = reader.get_data3(); + egg_vert.set_normal(LCAST(double, normal * net_mat)); + } + if (has_color_override) { + egg_vert.set_color(color_override); + + } else if (!has_color_off) { + LColor color(1.0f, 1.0f, 1.0f, 1.0f); + if (vertex_data->has_column(InternalName::get_color())) { + reader.set_column(InternalName::get_color()); + color = reader.get_data4(); + } + egg_vert.set_color(LColor(color[0] * color_scale[0], + color[1] * color_scale[1], + color[2] * color_scale[2], + color[3] * color_scale[3])); + } + + if (vertex_data->has_column(InternalName::get_texcoord())) { + reader.set_column(InternalName::get_texcoord()); + LTexCoord uv = reader.get_data2(); + egg_vert.set_uv(LCAST(double, uv)); + } + + EggVertex *new_egg_vert = _vpool->create_unique_vertex(egg_vert); + + if ((vertex_data->has_column(InternalName::get_transform_blend())) && + (jointMap!=NULL) && (transformBlendTable!=NULL)) { + reader.set_column(InternalName::get_transform_blend()); + int idx = reader.get_data1i(); + const TransformBlend &blend = transformBlendTable->get_blend(idx); + int num_weights = blend.get_num_transforms(); + for (int k = 0; k < num_weights; ++k) { + PN_stdfloat weight = blend.get_weight(k); + if (weight!=0) { + const VertexTransform *vertex_transform = blend.get_transform(k); + if (vertex_transform->is_of_type(JointVertexTransform::get_class_type())) { + const JointVertexTransform *joint_vertex_transform = DCAST(const JointVertexTransform, vertex_transform); + + CharacterJointMap::iterator mi = jointMap->find(joint_vertex_transform->get_joint()); + if (mi == jointMap->end()) { + mi = jointMap->insert(CharacterJointMap::value_type(joint_vertex_transform->get_joint(), pvector >())).first; + } + pvector > &joint_vertices = (*mi).second; + joint_vertices.push_back(pair(new_egg_vert, weight)); + } + } + } + } + + egg_prim->add_vertex(new_egg_vert); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::recurse_nodes +// Access: Private +// Description: Converts all the children of the indicated node. +//////////////////////////////////////////////////////////////////// +void EggSaver:: +recurse_nodes(const WorkingNodePath &node_path, EggGroupNode *egg_parent, + bool has_decal) { + PandaNode *node = node_path.node(); + int num_children = node->get_num_children(); + + for (int i = 0; i < num_children; i++) { + PandaNode *child = node->get_child(i); + convert_node(WorkingNodePath(node_path, child), egg_parent, has_decal); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::apply_node_properties +// Access: Private +// Description: Applies any special properties that might be stored +// on the node, like billboarding. Returns true if any +// were applied, false otherwise. +//////////////////////////////////////////////////////////////////// +bool EggSaver:: +apply_node_properties(EggGroup *egg_group, PandaNode *node, bool allow_backstage) { + bool any_applied = false; + + if (node->is_overall_hidden() && allow_backstage) { + // This node is hidden. We'll go ahead and convert it, but we'll + // put in the "backstage" flag to mean it's not real geometry. + // unless the caller wants to keep it (by setting allow_backstage to false) + egg_group->add_object_type("backstage"); + } + + if (node->has_tags()) { + if (apply_tags(egg_group, node)) { + any_applied = true; + } + } + + if (node->is_of_type(ModelNode::get_class_type())) { + ModelNode *model_node = DCAST(ModelNode, node); + switch (model_node->get_preserve_transform()) { + case ModelNode::PT_none: + case ModelNode::PT_drop_node: + break; + + case ModelNode::PT_net: + egg_group->set_dcs_type(EggGroup::DC_net); + break; + + case ModelNode::PT_local: + egg_group->set_dcs_type(EggGroup::DC_local); + break; + + case ModelNode::PT_no_touch: + egg_group->set_dcs_type(EggGroup::DC_no_touch); + break; + } + } + + const RenderEffects *effects = node->get_effects(); + const RenderEffect *effect = effects->get_effect(BillboardEffect::get_class_type()); + if (effect != (RenderEffect *)NULL) { + const BillboardEffect *bbe = DCAST(BillboardEffect, effect); + if (bbe->get_axial_rotate()) { + egg_group->set_billboard_type(EggGroup::BT_axis); + any_applied = true; + + } else if (bbe->get_eye_relative()) { + egg_group->set_billboard_type(EggGroup::BT_point_camera_relative); + any_applied = true; + + } else { + egg_group->set_billboard_type(EggGroup::BT_point_world_relative); + any_applied = true; + } + } + + const TransformState *transform = node->get_transform(); + if (!transform->is_identity()) { + if (transform->has_components()) { + // If the transform can be represented componentwise, we prefer + // storing it that way in the egg file. + const LVecBase3 &scale = transform->get_scale(); + const LQuaternion &quat = transform->get_quat(); + const LVecBase3 &pos = transform->get_pos(); + if (!scale.almost_equal(LVecBase3(1.0f, 1.0f, 1.0f))) { + egg_group->add_scale3d(LCAST(double, scale)); + } + if (!quat.is_identity()) { + egg_group->add_rotate3d(LCAST(double, quat)); + } + if (!pos.almost_equal(LVecBase3::zero())) { + egg_group->add_translate3d(LCAST(double, pos)); + } + + } else if (transform->has_mat()) { + // Otherwise, we store the raw matrix. + const LMatrix4 &mat = transform->get_mat(); + egg_group->set_transform3d(LCAST(double, mat)); + } + any_applied = true; + } + + return any_applied; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::apply_tags +// Access: Private +// Description: Applies string tags to the egg file. Returns true if +// any were applied, false otherwise. +//////////////////////////////////////////////////////////////////// +bool EggSaver:: +apply_tags(EggGroup *egg_group, PandaNode *node) { + ostringstream strm; + char delimiter = '\n'; + string delimiter_str(1, delimiter); + node->list_tags(strm, delimiter_str); + + string data = strm.str(); + if (data.empty()) { + return false; + } + + bool any_applied = false; + + size_t p = 0; + size_t q = data.find(delimiter); + while (q != string::npos) { + string tag = data.substr(p, q); + if (apply_tag(egg_group, node, tag)) { + any_applied = true; + } + p = q + 1; + q = data.find(delimiter, p); + } + + string tag = data.substr(p); + if (apply_tag(egg_group, node, tag)) { + any_applied = true; + } + + return any_applied; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::apply_tag +// Access: Private +// Description: Applies the named string tags to the egg file. +//////////////////////////////////////////////////////////////////// +bool EggSaver:: +apply_tag(EggGroup *egg_group, PandaNode *node, const string &tag) { + if (!node->has_tag(tag)) { + return false; + } + + string value = node->get_tag(tag); + egg_group->set_tag(tag, value); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::get_egg_texture +// Access: Private +// Description: Returns an EggTexture pointer that corresponds to the +// indicated Texture. +//////////////////////////////////////////////////////////////////// +EggTexture *EggSaver:: +get_egg_texture(Texture *tex) { + if (tex != (Texture *)NULL) { + if (tex->has_filename()) { + Filename filename = tex->get_filename(); + EggTexture temp(filename.get_basename_wo_extension(), filename); + if (tex->has_alpha_filename()) { + Filename alpha = tex->get_alpha_filename(); + temp.set_alpha_filename(alpha); + } + + switch (tex->get_minfilter()) { + case Texture::FT_nearest: + temp.set_minfilter(EggTexture::FT_nearest); + break; + case Texture::FT_linear: + temp.set_minfilter(EggTexture::FT_linear); + break; + case Texture::FT_nearest_mipmap_nearest: + temp.set_minfilter(EggTexture::FT_nearest_mipmap_nearest); + break; + case Texture::FT_linear_mipmap_nearest: + temp.set_minfilter(EggTexture::FT_linear_mipmap_nearest); + break; + case Texture::FT_nearest_mipmap_linear: + temp.set_minfilter(EggTexture::FT_nearest_mipmap_linear); + break; + case Texture::FT_linear_mipmap_linear: + temp.set_minfilter(EggTexture::FT_linear_mipmap_linear); + break; + + default: + break; + } + + switch (tex->get_magfilter()) { + case Texture::FT_nearest: + temp.set_magfilter(EggTexture::FT_nearest); + break; + case Texture::FT_linear: + temp.set_magfilter(EggTexture::FT_linear); + break; + + default: + break; + } + + switch (tex->get_wrap_u()) { + case Texture::WM_clamp: + temp.set_wrap_u(EggTexture::WM_clamp); + break; + case Texture::WM_repeat: + temp.set_wrap_u(EggTexture::WM_repeat); + break; + + default: + // There are some new wrap options on Texture that aren't yet + // supported in egg. + break; + } + + switch (tex->get_wrap_v()) { + case Texture::WM_clamp: + temp.set_wrap_v(EggTexture::WM_clamp); + break; + case Texture::WM_repeat: + temp.set_wrap_v(EggTexture::WM_repeat); + break; + + default: + // There are some new wrap options on Texture that aren't yet + // supported in egg. + break; + } + + switch (tex->get_format()) { + case Texture::F_red: + temp.set_format(EggTexture::F_red); + break; + case Texture::F_green: + temp.set_format(EggTexture::F_green); + break; + case Texture::F_blue: + temp.set_format(EggTexture::F_blue); + break; + case Texture::F_alpha: + temp.set_format(EggTexture::F_alpha); + break; + case Texture::F_rgb: + temp.set_format(EggTexture::F_rgb); + break; + case Texture::F_rgb5: + temp.set_format(EggTexture::F_rgb5); + break; + case Texture::F_rgb8: + temp.set_format(EggTexture::F_rgb8); + break; + case Texture::F_rgb12: + temp.set_format(EggTexture::F_rgb12); + break; + case Texture::F_rgb332: + temp.set_format(EggTexture::F_rgb332); + break; + case Texture::F_rgba: + temp.set_format(EggTexture::F_rgba); + break; + case Texture::F_rgbm: + temp.set_format(EggTexture::F_rgbm); + break; + case Texture::F_rgba4: + temp.set_format(EggTexture::F_rgba4); + break; + case Texture::F_rgba5: + temp.set_format(EggTexture::F_rgba5); + break; + case Texture::F_rgba8: + temp.set_format(EggTexture::F_rgba8); + break; + case Texture::F_rgba12: + temp.set_format(EggTexture::F_rgba12); + break; + case Texture::F_luminance: + temp.set_format(EggTexture::F_luminance); + break; + case Texture::F_luminance_alpha: + temp.set_format(EggTexture::F_luminance_alpha); + break; + case Texture::F_luminance_alphamask: + temp.set_format(EggTexture::F_luminance_alphamask); + break; + default: + break; + } + + return _textures.create_unique_texture(temp, ~EggTexture::E_tref_name); + } + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::make_egg_polygon +// Access: Private, Static +// Description: A factory function to make a new EggPolygon instance. +//////////////////////////////////////////////////////////////////// +EggPrimitive *EggSaver:: +make_egg_polygon() { + return new EggPolygon; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::make_egg_patch +// Access: Private, Static +// Description: A factory function to make a new EggPatch instance. +//////////////////////////////////////////////////////////////////// +EggPrimitive *EggSaver:: +make_egg_patch() { + return new EggPatch; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::make_egg_point +// Access: Private, Static +// Description: A factory function to make a new EggPoint instance. +//////////////////////////////////////////////////////////////////// +EggPrimitive *EggSaver:: +make_egg_point() { + return new EggPoint; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggSaver::make_egg_line +// Access: Private, Static +// Description: A factory function to make a new EggLine instance. +//////////////////////////////////////////////////////////////////// +EggPrimitive *EggSaver:: +make_egg_line() { + return new EggLine; +} diff --git a/panda/src/egg2pg/eggSaver.h b/panda/src/egg2pg/eggSaver.h new file mode 100755 index 0000000000..2cd5064cf4 --- /dev/null +++ b/panda/src/egg2pg/eggSaver.h @@ -0,0 +1,110 @@ +// Filename: eggSaver.h +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGSAVER_H +#define EGGSAVER_H + +#include "pandabase.h" + +#include "luse.h" +#include "eggTextureCollection.h" +#include "eggMaterialCollection.h" + +class WorkingNodePath; +class EggGroup; +class EggGroupNode; +class EggVertexPool; +class EggTexture; +class LODNode; +class SequenceNode; +class SwitchNode; +class AnimBundleNode; +class AnimGroup; +class Character; +class PartGroup; +class CollisionNode; +class GeomNode; +class GeomVertexData; +class GeomPrimitive; +class PandaNode; +class RenderState; +class Texture; +class CharacterJoint; +class EggVertex; + +//////////////////////////////////////////////////////////////////// +// Class : EggSaver +// Description : Converts the scene graph beginning at the indicated +// node into an EggData structure, for writing to an egg +// file. The conversion is not necessarily complete +// (some Panda or egg constructs are not fully supported +// by this class). +//////////////////////////////////////////////////////////////////// +class EggSaver { +PUBLISHED: + EggSaver(EggData *data = NULL); + + void add_node(PandaNode *node); + INLINE EggData *get_egg_data() const; + +private: + typedef pmap > > CharacterJointMap; + + void convert_node(const WorkingNodePath &node_path, EggGroupNode *egg_parent, + bool has_decal); + void convert_lod_node(LODNode *node, const WorkingNodePath &node_path, + EggGroupNode *egg_parent, bool has_decal); + void convert_sequence_node(SequenceNode *node, const WorkingNodePath &node_path, + EggGroupNode *egg_parent, bool has_decal); + void convert_switch_node(SwitchNode *node, const WorkingNodePath &node_path, + EggGroupNode *egg_parent, bool has_decal); + EggGroupNode *convert_animGroup_node(AnimGroup *animGroup, double fps ); + void convert_anim_node(AnimBundleNode *node, const WorkingNodePath &node_path, + EggGroupNode *egg_parent, bool has_decal); + void convert_character_node(Character *node, const WorkingNodePath &node_path, + EggGroupNode *egg_parent, bool has_decal); + void convert_character_bundle(PartGroup *bundleNode, EggGroupNode *egg_parent, CharacterJointMap *jointMap); + void convert_collision_node(CollisionNode *node, const WorkingNodePath &node_path, + EggGroupNode *egg_parent, bool has_decal); + void convert_geom_node(GeomNode *node, const WorkingNodePath &node_path, + EggGroupNode *egg_parent, bool has_decal, CharacterJointMap *jointMap=NULL); + void convert_primitive(const GeomVertexData *vertex_data, + const GeomPrimitive *primitive, + const RenderState *net_state, + const LMatrix4 &net_mat, EggGroupNode *egg_parent, + CharacterJointMap *jointMap); + + void recurse_nodes(const WorkingNodePath &node_path, EggGroupNode *egg_parent, + bool has_decal); + bool apply_node_properties(EggGroup *egg_group, PandaNode *node, bool allow_backstage = true); + bool apply_tags(EggGroup *egg_group, PandaNode *node); + bool apply_tag(EggGroup *egg_group, PandaNode *node, const string &tag); + + EggTexture *get_egg_texture(Texture *tex); + + static EggPrimitive *make_egg_polygon(); + static EggPrimitive *make_egg_patch(); + static EggPrimitive *make_egg_point(); + static EggPrimitive *make_egg_line(); + + PT(EggData) _data; + + PT(EggVertexPool) _vpool; + EggTextureCollection _textures; + EggMaterialCollection _materials; +}; + +#include "eggSaver.I" + +#endif diff --git a/panda/src/egg2pg/loaderFileTypeEgg.cxx b/panda/src/egg2pg/loaderFileTypeEgg.cxx index cada51bd1b..760001ef47 100644 --- a/panda/src/egg2pg/loaderFileTypeEgg.cxx +++ b/panda/src/egg2pg/loaderFileTypeEgg.cxx @@ -14,6 +14,7 @@ #include "loaderFileTypeEgg.h" #include "load_egg_file.h" +#include "save_egg_file.h" #include "eggData.h" @@ -60,6 +61,30 @@ supports_compressed() const { return true; } +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeEgg::supports_load +// Access: Published, Virtual +// Description: Returns true if the file type can be used to load +// files, and load_file() is supported. Returns false +// if load_file() is unimplemented and will always fail. +//////////////////////////////////////////////////////////////////// +bool LoaderFileTypeEgg:: +supports_load() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeEgg::supports_save +// Access: Published, Virtual +// Description: Returns true if the file type can be used to save +// files, and save_file() is supported. Returns false +// if save_file() is unimplemented and will always fail. +//////////////////////////////////////////////////////////////////// +bool LoaderFileTypeEgg:: +supports_save() const { + return true; +} + //////////////////////////////////////////////////////////////////// // Function: LoaderFileTypeEgg::load_file // Access: Public, Virtual @@ -71,3 +96,14 @@ load_file(const Filename &path, const LoaderOptions &, PT(PandaNode) result = load_egg_file(path, CS_default, record); return result; } + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeEgg::save_file +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool LoaderFileTypeEgg:: +save_file(const Filename &path, const LoaderOptions &options, + PandaNode *node) const { + return save_egg_file(path, node); +} diff --git a/panda/src/egg2pg/loaderFileTypeEgg.h b/panda/src/egg2pg/loaderFileTypeEgg.h index c6ee98eebe..f08f91911d 100644 --- a/panda/src/egg2pg/loaderFileTypeEgg.h +++ b/panda/src/egg2pg/loaderFileTypeEgg.h @@ -31,8 +31,13 @@ public: virtual string get_extension() const; virtual bool supports_compressed() const; + virtual bool supports_load() const; + virtual bool supports_save() const; + virtual PT(PandaNode) load_file(const Filename &path, const LoaderOptions &optoins, BamCacheRecord *record) const; + virtual bool save_file(const Filename &path, const LoaderOptions &options, + PandaNode *node) const; public: static TypeHandle get_class_type() { diff --git a/panda/src/egg2pg/p3egg2pg_composite1.cxx b/panda/src/egg2pg/p3egg2pg_composite1.cxx index 6e797603c1..38da9ac794 100644 --- a/panda/src/egg2pg/p3egg2pg_composite1.cxx +++ b/panda/src/egg2pg/p3egg2pg_composite1.cxx @@ -3,3 +3,4 @@ #include "config_egg2pg.cxx" #include "egg_parametrics.cxx" #include "eggRenderState.cxx" +#include "deferredNodeProperty.cxx" diff --git a/panda/src/egg2pg/p3egg2pg_composite2.cxx b/panda/src/egg2pg/p3egg2pg_composite2.cxx index 9824665353..8c4ae6a176 100644 --- a/panda/src/egg2pg/p3egg2pg_composite2.cxx +++ b/panda/src/egg2pg/p3egg2pg_composite2.cxx @@ -1,6 +1,7 @@ -#include "deferredNodeProperty.cxx" #include "eggBinner.cxx" #include "eggLoader.cxx" +#include "eggSaver.cxx" #include "load_egg_file.cxx" +#include "save_egg_file.cxx" #include "loaderFileTypeEgg.cxx" diff --git a/panda/src/egg2pg/save_egg_file.cxx b/panda/src/egg2pg/save_egg_file.cxx new file mode 100755 index 0000000000..0241684d38 --- /dev/null +++ b/panda/src/egg2pg/save_egg_file.cxx @@ -0,0 +1,52 @@ +// Filename: save_egg_file.cxx +// Created by: drose (26Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "save_egg_file.h" +#include "eggSaver.h" +#include "config_egg2pg.h" +#include "sceneGraphReducer.h" +#include "virtualFileSystem.h" +#include "config_util.h" + +//////////////////////////////////////////////////////////////////// +// Function: save_egg_file +// Description: A convenience function; converts the indicated scene +// graph to an egg file and writes it to disk. +//////////////////////////////////////////////////////////////////// +bool +save_egg_file(const Filename &filename, PandaNode *node, CoordinateSystem cs) { + PT(EggData) data = new EggData; + if (cs == CS_default) { + cs = get_default_coordinate_system(); + } + data->set_coordinate_system(cs); + + EggSaver saver(data); + saver.add_node(node); + + return data->write_egg(filename); +} + +//////////////////////////////////////////////////////////////////// +// Function: save_egg_data +// Description: Another convenience function; works like +// save_egg_file() but populates an EggData instead of +// writing the results to disk. +//////////////////////////////////////////////////////////////////// +bool +save_egg_data(EggData *data, PandaNode *node) { + EggSaver saver(data); + saver.add_node(node); + return true; +} diff --git a/panda/src/egg2pg/save_egg_file.h b/panda/src/egg2pg/save_egg_file.h new file mode 100755 index 0000000000..524cd51c51 --- /dev/null +++ b/panda/src/egg2pg/save_egg_file.h @@ -0,0 +1,44 @@ +// Filename: save_egg_file.h +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef SAVE_EGG_FILE_H +#define SAVE_EGG_FILE_H + +#include "pandabase.h" + +#include "pandaNode.h" +#include "coordinateSystem.h" +#include "eggData.h" + +BEGIN_PUBLISH +//////////////////////////////////////////////////////////////////// +// Function: save_egg_file +// Description: A convenience function; converts the indicated scene +// graph to an egg file and writes it to disk. +//////////////////////////////////////////////////////////////////// +EXPCL_PANDAEGG bool +save_egg_file(const Filename &filename, PandaNode *node, + CoordinateSystem cs = CS_default); + +//////////////////////////////////////////////////////////////////// +// Function: save_egg_data +// Description: Another convenience function; works like +// save_egg_file() but populates an EggData instead of +// writing the results to disk. +//////////////////////////////////////////////////////////////////// +EXPCL_PANDAEGG bool +save_egg_data(EggData *data, PandaNode *node); +END_PUBLISH + +#endif diff --git a/panda/src/pgraph/Sources.pp b/panda/src/pgraph/Sources.pp index 71410197f1..ab3d0fe9b9 100644 --- a/panda/src/pgraph/Sources.pp +++ b/panda/src/pgraph/Sources.pp @@ -67,6 +67,7 @@ materialCollection.I materialCollection.h \ modelFlattenRequest.I modelFlattenRequest.h \ modelLoadRequest.I modelLoadRequest.h \ + modelSaveRequest.I modelSaveRequest.h \ modelNode.I modelNode.h \ modelPool.I modelPool.h \ modelRoot.I modelRoot.h \ @@ -167,6 +168,7 @@ materialCollection.cxx \ modelFlattenRequest.cxx \ modelLoadRequest.cxx \ + modelSaveRequest.cxx \ modelNode.cxx \ modelPool.cxx \ modelRoot.cxx \ @@ -262,6 +264,7 @@ materialCollection.I materialCollection.h \ modelFlattenRequest.I modelFlattenRequest.h \ modelLoadRequest.I modelLoadRequest.h \ + modelSaveRequest.I modelSaveRequest.h \ modelNode.I modelNode.h \ modelPool.I modelPool.h \ modelRoot.I modelRoot.h \ diff --git a/panda/src/pgraph/config_pgraph.cxx b/panda/src/pgraph/config_pgraph.cxx index d0e7ddeb21..55f51b7015 100644 --- a/panda/src/pgraph/config_pgraph.cxx +++ b/panda/src/pgraph/config_pgraph.cxx @@ -55,6 +55,7 @@ #include "materialAttrib.h" #include "modelFlattenRequest.h" #include "modelLoadRequest.h" +#include "modelSaveRequest.h" #include "modelNode.h" #include "modelRoot.h" #include "nodePath.h" @@ -473,6 +474,7 @@ init_libpgraph() { MaterialAttrib::init_type(); ModelFlattenRequest::init_type(); ModelLoadRequest::init_type(); + ModelSaveRequest::init_type(); ModelNode::init_type(); ModelRoot::init_type(); NodePath::init_type(); diff --git a/panda/src/pgraph/loader.I b/panda/src/pgraph/loader.I index f0e3e05021..dcc82cda3c 100644 --- a/panda/src/pgraph/loader.I +++ b/panda/src/pgraph/loader.I @@ -200,11 +200,11 @@ load_sync(const Filename &filename, const LoaderOptions &options) const { // Function: Loader::load_async // Access: Published // Description: Begins an asynchronous load request. To use this -// call, first create a new ModelLoadRequest object with -// the filename you wish to load, and then add that -// object to the Loader with load_async. This function -// will return immediately, and the model will be loaded -// in the background. +// call, first call make_async_request() to create a new +// ModelLoadRequest object with the filename you wish to +// load, and then add that object to the Loader with +// load_async. This function will return immediately, +// and the model will be loaded in the background. // // To determine when the model has completely loaded, // you may poll request->is_ready() from time to time, @@ -218,6 +218,45 @@ load_async(AsyncTask *request) { _task_manager->add(request); } +//////////////////////////////////////////////////////////////////// +// Function: Loader::save_sync +// Access: Published +// Description: Saves the file immediately, waiting for it to +// complete. +//////////////////////////////////////////////////////////////////// +INLINE bool Loader:: +save_sync(const Filename &filename, const LoaderOptions &options, + PandaNode *node) const { + if (!_file_types_loaded) { + load_file_types(); + } + return save_file(filename, options, node); +} + +//////////////////////////////////////////////////////////////////// +// Function: Loader::save_async +// Access: Published +// Description: Begins an asynchronous save request. To use this +// call, first call make_async_save_request() to create +// a new ModelSaveRequest object with the filename you +// wish to load, and then add that object to the Loader +// with save_async. This function will return +// immediately, and the model will be loaded in the +// background. +// +// To determine when the model has completely loaded, +// you may poll request->is_ready() from time to time, +// or set the done_event on the request object and +// listen for that event. When the request is ready, +// you may retrieve the success or failure via +// request->get_success(). +//////////////////////////////////////////////////////////////////// +INLINE void Loader:: +save_async(AsyncTask *request) { + request->set_task_chain(_task_chain); + _task_manager->add(request); +} + //////////////////////////////////////////////////////////////////// // Function: Loader::get_global_ptr // Access: Published diff --git a/panda/src/pgraph/loader.cxx b/panda/src/pgraph/loader.cxx index a8d17440d5..c225cc9a4e 100644 --- a/panda/src/pgraph/loader.cxx +++ b/panda/src/pgraph/loader.cxx @@ -18,6 +18,7 @@ #include "config_pgraph.h" #include "modelPool.h" #include "modelLoadRequest.h" +#include "modelSaveRequest.h" #include "config_express.h" #include "config_util.h" #include "virtualFileSystem.h" @@ -84,6 +85,19 @@ make_async_request(const Filename &filename, const LoaderOptions &options) { filename, options, this); } +//////////////////////////////////////////////////////////////////// +// Function: Loader::make_async_save_request +// Access: Published +// Description: Returns a new AsyncTask object suitable for adding to +// save_async() to start an asynchronous model save. +//////////////////////////////////////////////////////////////////// +PT(AsyncTask) Loader:: +make_async_save_request(const Filename &filename, const LoaderOptions &options, + PandaNode *node) { + return new ModelSaveRequest(string("model_save:")+filename.get_basename(), + filename, options, node, this); +} + //////////////////////////////////////////////////////////////////// // Function: Loader::load_bam_stream // Access: Published @@ -176,6 +190,13 @@ load_file(const Filename &filename, const LoaderOptions &options) const { reg->write(loader_cat.error(false), 2); } return NULL; + } else if (!requested_type->supports_load()) { + if (report_errors) { + loader_cat.error() + << requested_type->get_name() << " file type (." + << extension << ") does not support loading.\n"; + } + return NULL; } else if (pz_file && !requested_type->supports_compressed()) { if (report_errors) { loader_cat.error() @@ -338,6 +359,102 @@ try_load_file(const Filename &pathname, const LoaderOptions &options, return NULL; } +//////////////////////////////////////////////////////////////////// +// Function: Loader::save_file +// Access: Private +// Description: Saves a scene graph to a single file, if possible. +// The file type written is implicit in the filename +// extension. +//////////////////////////////////////////////////////////////////// +bool Loader:: +save_file(const Filename &filename, const LoaderOptions &options, + PandaNode *node) const { + Filename this_filename(filename); + LoaderOptions this_options(options); + + bool report_errors = (this_options.get_flags() & LoaderOptions::LF_report_errors) != 0; + + string extension = this_filename.get_extension(); + if (extension.empty()) { + // If the filename has no filename extension, append the default + // extension specified in the Config file. + this_filename = this_filename.get_fullpath() + default_model_extension.get_value(); + extension = this_filename.get_extension(); + } + + bool pz_file = false; +#ifdef HAVE_ZLIB + if (extension == "pz") { + pz_file = true; + extension = Filename(this_filename.get_basename_wo_extension()).get_extension(); + } +#endif // HAVE_ZLIB + + if (extension.empty()) { + if (report_errors) { + loader_cat.error() + << "Cannot save " << this_filename + << " without filename extension.\n"; + } + return NULL; + } + + LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr(); + LoaderFileType *requested_type = + reg->get_type_from_extension(extension); + if (requested_type == (LoaderFileType *)NULL) { + if (report_errors) { + loader_cat.error() + << "Extension of file " << this_filename + << " is unrecognized; cannot save.\n"; + loader_cat.error(false) + << "Currently known scene file types are:\n"; + reg->write(loader_cat.error(false), 2); + } + return NULL; + } else if (!requested_type->supports_save()) { + if (report_errors) { + loader_cat.error() + << requested_type->get_name() << " file type (." + << extension << ") does not support saving.\n"; + } + return NULL; + } else if (pz_file && !requested_type->supports_compressed()) { + if (report_errors) { + loader_cat.error() + << requested_type->get_name() << " file type (." + << extension << ") does not support in-line compression.\n"; + } + return NULL; + } + + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + + bool result = try_save_file(this_filename, this_options, node, requested_type); + if (!result) { + if (report_errors) { + loader_cat.error() + << "Couldn't save file " << this_filename << ".\n"; + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: Loader::try_save_file +// Access: Private +// Description: The implementation of save_file(), this tries to +// write a specific file type. +//////////////////////////////////////////////////////////////////// +bool Loader:: +try_save_file(const Filename &pathname, const LoaderOptions &options, + PandaNode *node, LoaderFileType *requested_type) const { + bool report_errors = ((options.get_flags() & LoaderOptions::LF_report_errors) != 0 || loader_cat.is_debug()); + + bool result = requested_type->save_file(pathname, options, node); + return result; +} //////////////////////////////////////////////////////////////////// // Function: Loader::load_file_types diff --git a/panda/src/pgraph/loader.h b/panda/src/pgraph/loader.h index 7cbe35c657..6587a59636 100644 --- a/panda/src/pgraph/loader.h +++ b/panda/src/pgraph/loader.h @@ -92,6 +92,13 @@ PUBLISHED: const LoaderOptions &options = LoaderOptions()); INLINE void load_async(AsyncTask *request); + INLINE bool save_sync(const Filename &filename, const LoaderOptions &options, + PandaNode *node) const; + PT(AsyncTask) make_async_save_request(const Filename &filename, + const LoaderOptions &options, + PandaNode *node); + INLINE void save_async(AsyncTask *request); + BLOCKING PT(PandaNode) load_bam_stream(istream &in); virtual void output(ostream &out) const; @@ -103,6 +110,11 @@ private: PT(PandaNode) try_load_file(const Filename &pathname, const LoaderOptions &options, LoaderFileType *requested_type) const; + bool save_file(const Filename &filename, const LoaderOptions &options, + PandaNode *node) const; + bool try_save_file(const Filename &filename, const LoaderOptions &options, + PandaNode *node, LoaderFileType *requested_type) const; + static void make_global_ptr(); PT(AsyncTaskManager) _task_manager; diff --git a/panda/src/pgraph/loaderFileType.cxx b/panda/src/pgraph/loaderFileType.cxx index 03335b0089..9c086f3b79 100644 --- a/panda/src/pgraph/loaderFileType.cxx +++ b/panda/src/pgraph/loaderFileType.cxx @@ -81,7 +81,7 @@ get_allow_disk_cache(const LoaderOptions &options) const { //////////////////////////////////////////////////////////////////// // Function: LoaderFileType::get_allow_ram_cache -// Access: Published +// Access: Published, Virtual // Description: Returns true if the loader flags allow retrieving the // model from the in-memory ModelPool cache, false // otherwise. @@ -91,6 +91,30 @@ get_allow_ram_cache(const LoaderOptions &options) const { return (options.get_flags() & (LoaderOptions::LF_no_ram_cache | _no_cache_flags)) == 0; } +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileType::supports_load +// Access: Published, Virtual +// Description: Returns true if the file type can be used to load +// files, and load_file() is supported. Returns false +// if load_file() is unimplemented and will always fail. +//////////////////////////////////////////////////////////////////// +bool LoaderFileType:: +supports_load() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileType::supports_save +// Access: Published, Virtual +// Description: Returns true if the file type can be used to save +// files, and save_file() is supported. Returns false +// if save_file() is unimplemented and will always fail. +//////////////////////////////////////////////////////////////////// +bool LoaderFileType:: +supports_save() const { + return false; +} + //////////////////////////////////////////////////////////////////// // Function: LoaderFileType::load_file // Access: Public, Virtual @@ -103,3 +127,16 @@ load_file(const Filename &path, const LoaderOptions &options, << get_type() << " cannot read PandaNode objects.\n"; return NULL; } + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileType::save_file +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool LoaderFileType:: +save_file(const Filename &path, const LoaderOptions &options, + PandaNode *node) const { + loader_cat.error() + << get_type() << " cannot save PandaNode objects.\n"; + return NULL; +} diff --git a/panda/src/pgraph/loaderFileType.h b/panda/src/pgraph/loaderFileType.h index 189a917e29..8a25db238c 100644 --- a/panda/src/pgraph/loaderFileType.h +++ b/panda/src/pgraph/loaderFileType.h @@ -49,9 +49,14 @@ PUBLISHED: virtual bool get_allow_disk_cache(const LoaderOptions &options) const; virtual bool get_allow_ram_cache(const LoaderOptions &options) const; + virtual bool supports_load() const; + virtual bool supports_save() const; + public: virtual PT(PandaNode) load_file(const Filename &path, const LoaderOptions &options, BamCacheRecord *record) const; + virtual bool save_file(const Filename &path, const LoaderOptions &options, + PandaNode *node) const; protected: int _no_cache_flags; diff --git a/panda/src/pgraph/loaderFileTypeBam.cxx b/panda/src/pgraph/loaderFileTypeBam.cxx index f11e66bbc6..9fd3bc1347 100644 --- a/panda/src/pgraph/loaderFileTypeBam.cxx +++ b/panda/src/pgraph/loaderFileTypeBam.cxx @@ -63,6 +63,30 @@ supports_compressed() const { return true; } +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeBam::supports_load +// Access: Published, Virtual +// Description: Returns true if the file type can be used to load +// files, and load_file() is supported. Returns false +// if load_file() is unimplemented and will always fail. +//////////////////////////////////////////////////////////////////// +bool LoaderFileTypeBam:: +supports_load() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeBam::supports_save +// Access: Published, Virtual +// Description: Returns true if the file type can be used to save +// files, and save_file() is supported. Returns false +// if save_file() is unimplemented and will always fail. +//////////////////////////////////////////////////////////////////// +bool LoaderFileTypeBam:: +supports_save() const { + return true; +} + //////////////////////////////////////////////////////////////////// // Function: LoaderFileTypeBam::load_file // Access: Public, Virtual @@ -94,3 +118,26 @@ load_file(const Filename &path, const LoaderOptions &options, return node; } + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeBam::save_file +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool LoaderFileTypeBam:: +save_file(const Filename &path, const LoaderOptions &options, + PandaNode *node) const { + BamFile bam_file; + + bool report_errors = (options.get_flags() & LoaderOptions::LF_report_errors) != 0; + + bool okflag = false; + + if (bam_file.open_write(path, report_errors)) { + if (bam_file.write_object(node)) { + okflag = true; + } + bam_file.close(); + } + return okflag; +} diff --git a/panda/src/pgraph/loaderFileTypeBam.h b/panda/src/pgraph/loaderFileTypeBam.h index e99aa50e0c..f293d8e871 100644 --- a/panda/src/pgraph/loaderFileTypeBam.h +++ b/panda/src/pgraph/loaderFileTypeBam.h @@ -31,8 +31,13 @@ public: virtual string get_extension() const; virtual bool supports_compressed() const; + virtual bool supports_load() const; + virtual bool supports_save() const; + virtual PT(PandaNode) load_file(const Filename &path, const LoaderOptions &options, BamCacheRecord *record) const; + virtual bool save_file(const Filename &path, const LoaderOptions &options, + PandaNode *node) const; public: static TypeHandle get_class_type() { diff --git a/panda/src/pgraph/modelSaveRequest.I b/panda/src/pgraph/modelSaveRequest.I new file mode 100755 index 0000000000..57dacb18c2 --- /dev/null +++ b/panda/src/pgraph/modelSaveRequest.I @@ -0,0 +1,82 @@ +// Filename: modelSaveRequest.I +// 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." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: ModelSaveRequest::get_filename +// Access: Published +// Description: Returns the filename associated with this +// asynchronous ModelSaveRequest. +//////////////////////////////////////////////////////////////////// +INLINE const Filename &ModelSaveRequest:: +get_filename() const { + return _filename; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelSaveRequest::get_options +// Access: Published +// Description: Returns the LoaderOptions associated with this +// asynchronous ModelSaveRequest. +//////////////////////////////////////////////////////////////////// +INLINE const LoaderOptions &ModelSaveRequest:: +get_options() const { + return _options; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelSaveRequest::get_node +// Access: Published +// Description: Returns the node that was passed to the constructor. +//////////////////////////////////////////////////////////////////// +INLINE PandaNode *ModelSaveRequest:: +get_node() const { + return _node; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelSaveRequest::get_loader +// Access: Published +// Description: Returns the Loader object associated with this +// asynchronous ModelSaveRequest. +//////////////////////////////////////////////////////////////////// +INLINE Loader *ModelSaveRequest:: +get_loader() const { + return _loader; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelSaveRequest::is_ready +// Access: Published +// Description: Returns true if this request has completed, false if +// it is still pending. When this returns true, you may +// retrieve the success flag with get_success(). +//////////////////////////////////////////////////////////////////// +INLINE bool ModelSaveRequest:: +is_ready() const { + return _is_ready; +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelSaveRequest::get_success +// Access: Published +// Description: Returns the true if the model was saved successfully, +// false otherwise. It is an error to call this unless +// is_ready() returns true. +//////////////////////////////////////////////////////////////////// +INLINE bool ModelSaveRequest:: +get_success() const { + nassertr(_is_ready, NULL); + return _success; +} diff --git a/panda/src/pgraph/modelSaveRequest.cxx b/panda/src/pgraph/modelSaveRequest.cxx new file mode 100755 index 0000000000..02c31b2ab3 --- /dev/null +++ b/panda/src/pgraph/modelSaveRequest.cxx @@ -0,0 +1,58 @@ +// Filename: modelSaveRequest.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 "modelSaveRequest.h" +#include "loader.h" +#include "config_pgraph.h" + +TypeHandle ModelSaveRequest::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ModelSaveRequest::Constructor +// Access: Published +// Description: Create a new ModelSaveRequest, and add it to the loader +// via save_async(), to begin an asynchronous save. +//////////////////////////////////////////////////////////////////// +ModelSaveRequest:: +ModelSaveRequest(const string &name, + const Filename &filename, const LoaderOptions &options, + PandaNode *node, Loader *loader) : + AsyncTask(name), + _filename(filename), + _options(options), + _node(node), + _loader(loader), + _is_ready(false), + _success(false) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ModelSaveRequest::do_task +// Access: Protected, Virtual +// Description: Performs the task: that is, saves the one model. +//////////////////////////////////////////////////////////////////// +AsyncTask::DoneStatus ModelSaveRequest:: +do_task() { + double delay = async_load_delay; + if (delay != 0.0) { + Thread::sleep(delay); + } + + _success = _loader->save_sync(_filename, _options, _node); + _is_ready = true; + + // Don't continue the task; we're done. + return DS_done; +} diff --git a/panda/src/pgraph/modelSaveRequest.h b/panda/src/pgraph/modelSaveRequest.h new file mode 100755 index 0000000000..aa72952fc1 --- /dev/null +++ b/panda/src/pgraph/modelSaveRequest.h @@ -0,0 +1,83 @@ +// Filename: modelSaveRequest.h +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef MODELSAVEREQUEST +#define MODELSAVEREQUEST + +#include "pandabase.h" + +#include "asyncTask.h" +#include "filename.h" +#include "loaderOptions.h" +#include "pandaNode.h" +#include "pointerTo.h" +#include "loader.h" + +//////////////////////////////////////////////////////////////////// +// Class : ModelSaveRequest +// Description : A class object that manages a single asynchronous +// model save request. Create a new ModelSaveRequest, +// and add it to the loader via save_async(), to begin +// an asynchronous save. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA_PGRAPH ModelSaveRequest : public AsyncTask { +public: + ALLOC_DELETED_CHAIN(ModelSaveRequest); + +PUBLISHED: + ModelSaveRequest(const string &name, + const Filename &filename, + const LoaderOptions &options, + PandaNode *node, Loader *loader); + + INLINE const Filename &get_filename() const; + INLINE const LoaderOptions &get_options() const; + INLINE PandaNode *get_node() const; + INLINE Loader *get_loader() const; + + INLINE bool is_ready() const; + INLINE bool get_success() const; + +protected: + virtual DoneStatus do_task(); + +private: + Filename _filename; + LoaderOptions _options; + PT(PandaNode) _node; + PT(Loader) _loader; + bool _is_ready; + bool _success; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + AsyncTask::init_type(); + register_type(_type_handle, "ModelSaveRequest", + AsyncTask::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "modelSaveRequest.I" + +#endif diff --git a/panda/src/pgraph/p3pgraph_composite3.cxx b/panda/src/pgraph/p3pgraph_composite3.cxx index 2a27dacbf2..b539fefcee 100644 --- a/panda/src/pgraph/p3pgraph_composite3.cxx +++ b/panda/src/pgraph/p3pgraph_composite3.cxx @@ -11,6 +11,7 @@ #include "materialCollection.cxx" #include "modelFlattenRequest.cxx" #include "modelLoadRequest.cxx" +#include "modelSaveRequest.cxx" #include "modelNode.cxx" #include "modelPool.cxx" #include "modelRoot.cxx" diff --git a/pandatool/src/bam/bamToEgg.cxx b/pandatool/src/bam/bamToEgg.cxx index 3605910304..4239a05f42 100644 --- a/pandatool/src/bam/bamToEgg.cxx +++ b/pandatool/src/bam/bamToEgg.cxx @@ -13,63 +13,11 @@ //////////////////////////////////////////////////////////////////// #include "bamToEgg.h" - -#include "pandaNode.h" -#include "workingNodePath.h" -#include "nodePath.h" -#include "billboardEffect.h" -#include "renderEffects.h" -#include "transformState.h" -#include "colorScaleAttrib.h" -#include "colorAttrib.h" -#include "textureAttrib.h" -#include "cullFaceAttrib.h" -#include "transparencyAttrib.h" -#include "depthWriteAttrib.h" -#include "lodNode.h" -#include "switchNode.h" -#include "sequenceNode.h" -#include "collisionNode.h" -#include "collisionPolygon.h" -#include "collisionPlane.h" -#include "collisionSphere.h" -#include "collisionInvSphere.h" -#include "collisionTube.h" -#include "textureStage.h" -#include "geomNode.h" -#include "geom.h" -#include "geomTriangles.h" -#include "geomPatches.h" -#include "geomPoints.h" -#include "geomLines.h" -#include "geomVertexReader.h" -#include "transformTable.h" -#include "modelNode.h" -#include "animBundleNode.h" -#include "animChannelMatrixXfmTable.h" -#include "characterJoint.h" -#include "character.h" +#include "save_egg_file.h" +#include "pystub.h" #include "string_utils.h" #include "bamFile.h" #include "bamCacheRecord.h" -#include "eggSAnimData.h" -#include "eggXfmAnimData.h" -#include "eggXfmSAnim.h" -#include "eggGroup.h" -#include "eggVertexPool.h" -#include "eggVertex.h" -#include "eggPrimitive.h" -#include "eggPolygon.h" -#include "eggPatch.h" -#include "eggPoint.h" -#include "eggLine.h" -#include "eggTexture.h" -#include "eggMaterial.h" -#include "eggRenderMode.h" -#include "eggTable.h" -#include "somethingToEggConverter.h" -#include "dcast.h" -#include "pystub.h" //////////////////////////////////////////////////////////////////// // Function: BamToEgg::Constructor @@ -80,9 +28,6 @@ BamToEgg:: BamToEgg() : SomethingToEgg("bam", ".bam") { - add_path_replace_options(); - add_path_store_options(); - set_program_description ("This program converts native Panda bam files to egg. The conversion " "is somewhat incomplete; running egg2bam followed by bam2egg should not " @@ -142,1054 +87,20 @@ run() { bam_file.close(); _data->set_coordinate_system(_coordinate_system); - _vpool = new EggVertexPool("vpool"); - _data->add_child(_vpool); if (objects.size() == 1 && objects[0]->is_of_type(PandaNode::get_class_type())) { PandaNode *node = DCAST(PandaNode, objects[0]); - NodePath root(node); - convert_node(WorkingNodePath(root), _data, false); + save_egg_data(_data, node); } else { nout << "File does not contain a scene graph.\n"; exit(1); } - // Remove the vertex pool if it has no vertices. - if (_vpool->empty()) { - _data->remove_child(_vpool); - } - write_egg_file(); } -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::convert_node -// Access: Private -// Description: Converts the indicated node to the corresponding Egg -// constructs, by first determining what kind of node it -// is. -//////////////////////////////////////////////////////////////////// -void BamToEgg:: -convert_node(const WorkingNodePath &node_path, EggGroupNode *egg_parent, - bool has_decal) { - PandaNode *node = node_path.node(); - if (node->is_geom_node()) { - convert_geom_node(DCAST(GeomNode, node), node_path, egg_parent, has_decal); - - } else if (node->is_of_type(LODNode::get_class_type())) { - convert_lod_node(DCAST(LODNode, node), node_path, egg_parent, has_decal); - - } else if (node->is_of_type(SequenceNode::get_class_type())) { - convert_sequence_node(DCAST(SequenceNode, node), node_path, egg_parent, has_decal); - - } else if (node->is_of_type(SwitchNode::get_class_type())) { - convert_switch_node(DCAST(SwitchNode, node), node_path, egg_parent, has_decal); - - } else if (node->is_of_type(CollisionNode::get_class_type())) { - convert_collision_node(DCAST(CollisionNode, node), node_path, egg_parent, has_decal); - - } else if (node->is_of_type(AnimBundleNode::get_class_type())) { - convert_anim_node(DCAST(AnimBundleNode, node), node_path, egg_parent, has_decal); - - } else if (node->is_of_type(Character::get_class_type())) { - convert_character_node(DCAST(Character, node), node_path, egg_parent, has_decal); - - } else { - // Just a generic node. - EggGroup *egg_group = new EggGroup(node->get_name()); - egg_parent->add_child(egg_group); - apply_node_properties(egg_group, node); - - recurse_nodes(node_path, egg_group, has_decal); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::convert_lod_node -// Access: Private -// Description: Converts the indicated LODNode to the corresponding -// Egg constructs. -//////////////////////////////////////////////////////////////////// -void BamToEgg:: -convert_lod_node(LODNode *node, const WorkingNodePath &node_path, - EggGroupNode *egg_parent, bool has_decal) { - // An LOD node gets converted to an ordinary EggGroup, but we apply - // the appropriate switch conditions to each of our children. - EggGroup *egg_group = new EggGroup(node->get_name()); - egg_parent->add_child(egg_group); - apply_node_properties(egg_group, node); - - int num_children = node->get_num_children(); - int num_switches = node->get_num_switches(); - - num_children = min(num_children, num_switches); - - for (int i = 0; i < num_children; i++) { - PandaNode *child = node->get_child(i); - - // Convert just this one node to an EggGroup. - PT(EggGroup) next_group = new EggGroup; - convert_node(WorkingNodePath(node_path, child), next_group, has_decal); - - if (next_group->size() == 1) { - // If we have exactly one child, and that child is an EggGroup, - // collapse. - EggNode *child_node = *next_group->begin(); - if (child_node->is_of_type(EggGroup::get_class_type())) { - PT(EggGroup) child = DCAST(EggGroup, child_node); - next_group->remove_child(child.p()); - next_group = child; - } - } - - // Now set up the switching properties appropriately. - PN_stdfloat in = node->get_in(i); - PN_stdfloat out = node->get_out(i); - LPoint3 center = node->get_center(); - EggSwitchConditionDistance dist(in, out, LCAST(double, center)); - next_group->set_lod(dist); - egg_group->add_child(next_group.p()); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::convert_sequence_node -// Access: Private -// Description: Converts the indicated SequenceNode to the corresponding -// Egg constructs. -//////////////////////////////////////////////////////////////////// -void BamToEgg:: -convert_sequence_node(SequenceNode *node, const WorkingNodePath &node_path, - EggGroupNode *egg_parent, bool has_decal) { - // A sequence node gets converted to an ordinary EggGroup, we only apply - // the appropriate switch attributes to turn it into a sequence - EggGroup *egg_group = new EggGroup(node->get_name()); - egg_parent->add_child(egg_group); - apply_node_properties(egg_group, node); - - // turn it into a sequence with the right frame-rate - egg_group->set_switch_flag(true); - egg_group->set_switch_fps(node->get_frame_rate()); - - int num_children = node->get_num_children(); - - for (int i = 0; i < num_children; i++) { - PandaNode *child = node->get_child(i); - - // Convert just this one node to an EggGroup. - PT(EggGroup) next_group = new EggGroup; - convert_node(WorkingNodePath(node_path, child), next_group, has_decal); - - egg_group->add_child(next_group.p()); - } - -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::convert_switch_node -// Access: Private -// Description: Converts the indicated SwitchNode to the corresponding -// Egg constructs. -//////////////////////////////////////////////////////////////////// -void BamToEgg:: -convert_switch_node(SwitchNode *node, const WorkingNodePath &node_path, - EggGroupNode *egg_parent, bool has_decal) { - // A sequence node gets converted to an ordinary EggGroup, we only apply - // the appropriate switch attributes to turn it into a sequence - EggGroup *egg_group = new EggGroup(node->get_name()); - egg_parent->add_child(egg_group); - apply_node_properties(egg_group, node); - - // turn it into a switch.. - egg_group->set_switch_flag(true); - - int num_children = node->get_num_children(); - - for (int i = 0; i < num_children; i++) { - PandaNode *child = node->get_child(i); - - // Convert just this one node to an EggGroup. - PT(EggGroup) next_group = new EggGroup; - convert_node(WorkingNodePath(node_path, child), next_group, has_decal); - - egg_group->add_child(next_group.p()); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::convert_animGroup_node -// Access: Private -// Description: Converts the indicated AnimationGroupNodes to the corresponding -// Egg constructs. -//////////////////////////////////////////////////////////////////// -EggGroupNode * BamToEgg::convert_animGroup_node(AnimGroup *animGroup, double fps ) { - int num_children = animGroup->get_num_children(); - - EggGroupNode *eggNode = NULL; - if (animGroup->is_of_type(AnimBundle::get_class_type())) { - EggTable *eggTable = new EggTable(animGroup->get_name()); - eggTable ->set_table_type(EggTable::TT_bundle); - eggNode = eggTable; - } else if (animGroup->is_of_type(AnimGroup::get_class_type())) { - EggTable *eggTable = new EggTable(animGroup->get_name()); - eggTable ->set_table_type(EggTable::TT_table); - eggNode = eggTable; - } - - if (animGroup->is_of_type(AnimChannelMatrixXfmTable::get_class_type())) { - AnimChannelMatrixXfmTable *xmfTable = DCAST(AnimChannelMatrixXfmTable, animGroup); - EggXfmSAnim *egg_anim = new EggXfmSAnim("xform"); - egg_anim->set_fps(fps); - for (int i = 0; i < num_matrix_components; i++) { - string componentName(1, matrix_component_letters[i]); - char table_id = matrix_component_letters[i]; - CPTA_stdfloat table = xmfTable->get_table(table_id); - - if (xmfTable->has_table(table_id)) { - for (unsigned int j = 0; j < table.size(); j++) { - egg_anim->add_component_data(componentName, table[(int)j]); - } - } - } - eggNode->add_child(egg_anim); - } - for (int i = 0; i < num_children; i++) { - AnimGroup *animChild = animGroup->get_child(i); - EggGroupNode *eggChildNode = convert_animGroup_node(animChild, fps); - if (eggChildNode!=NULL) { - nassertr(eggNode!=NULL, NULL); - eggNode->add_child(eggChildNode); - } - } - return eggNode; -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::convert_anim_node -// Access: Private -// Description: Converts the indicated AnimNode to the corresponding -// Egg constructs. -//////////////////////////////////////////////////////////////////// -void BamToEgg:: -convert_anim_node(AnimBundleNode *node, const WorkingNodePath &node_path, - EggGroupNode *egg_parent, bool has_decal) { - - // A sequence node gets converted to an ordinary EggGroup, we only apply - // the appropriate switch attributes to turn it into a sequence - EggTable *eggTable = new EggTable(); - //egg_parent->add_child(eggTable); - _data->add_child(eggTable); - - AnimBundle *animBundle = node->get_bundle(); - // turn it into a switch.. - //egg_group->set_switch_flag(true); - - EggGroupNode *eggAnimation = convert_animGroup_node(animBundle, animBundle->get_base_frame_rate()); - eggTable->add_child(eggAnimation); -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::convert_character_bundle -// Access: Private -// Description: Converts the indicated Character Bundle to the corresponding -// Egg joints structure. -//////////////////////////////////////////////////////////////////// -void BamToEgg:: -convert_character_bundle(PartGroup *bundleNode, EggGroupNode *egg_parent, CharacterJointMap *jointMap) { - int num_children = bundleNode->get_num_children(); - - EggGroupNode *joint_group = egg_parent; - if (bundleNode->is_of_type(CharacterJoint::get_class_type())) { - CharacterJoint *character_joint = DCAST(CharacterJoint, bundleNode); - - LMatrix4 transformf; - character_joint->get_net_transform(transformf); - LMatrix4d transformd(LCAST(double, transformf)); - EggGroup *joint = new EggGroup(bundleNode->get_name()); - joint->add_matrix4(transformd); - joint->set_group_type(EggGroup::GT_joint); - joint_group = joint; - egg_parent->add_child(joint_group); - if (jointMap!=NULL) { - CharacterJointMap::iterator mi = jointMap->find(character_joint); - if (mi != jointMap->end()) { - pvector > &joint_vertices = (*mi).second; - pvector >::const_iterator vi; - for (vi = joint_vertices.begin(); vi != joint_vertices.end(); ++vi) { - joint->set_vertex_membership((*vi).first, (*vi).second); - } - } - } - } - - for (int i = 0; i < num_children ; i++) { - PartGroup *partGroup= bundleNode->get_child(i); - convert_character_bundle(partGroup, joint_group, jointMap); - } - -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::convert_character_node -// Access: Private -// Description: Converts the indicated Character to the corresponding -// Egg constructs. -//////////////////////////////////////////////////////////////////// -void BamToEgg:: -convert_character_node(Character *node, const WorkingNodePath &node_path, - EggGroupNode *egg_parent, bool has_decal) { - - // A sequence node gets converted to an ordinary EggGroup, we only apply - // the appropriate switch attributes to turn it into a sequence - EggGroup *egg_group = new EggGroup(node->get_name()); - egg_group->set_dart_type(EggGroup::DT_default); - egg_parent->add_child(egg_group); - apply_node_properties(egg_group, node); - - CharacterJointMap jointMap; - - // turn it into a switch.. - //egg_group->set_switch_flag(true); - - int num_children = node->get_num_children(); - int num_bundles = node->get_num_bundles(); - - for (int i = 0; i < num_children; i++) { - PandaNode *child = node->get_child(i); - - if (child->is_geom_node()) { - convert_geom_node(DCAST(GeomNode, child), WorkingNodePath(node_path, child), egg_group, has_decal, &jointMap); - } - } - - for (int i = 0; i < num_bundles ; i++) { - PartBundle *bundle= node->get_bundle(i); - convert_character_bundle(bundle, egg_group, &jointMap); - } - -} - - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::convert_collision_node -// Access: Private -// Description: Converts the indicated CollisionNode to the corresponding -// Egg constructs. -//////////////////////////////////////////////////////////////////// -void BamToEgg:: -convert_collision_node(CollisionNode *node, const WorkingNodePath &node_path, - EggGroupNode *egg_parent, bool has_decal) { - // A sequence node gets converted to an ordinary EggGroup, we only apply - // the appropriate switch attributes to turn it into a sequence - EggGroup *egg_group = new EggGroup(node->get_name()); - egg_parent->add_child(egg_group); - apply_node_properties(egg_group, node, false); - - // turn it into a collision node - egg_group->set_cs_type(EggGroup::CST_polyset); - egg_group->set_collide_flags(EggGroup::CF_descend); - - NodePath np = node_path.get_node_path(); - CPT(TransformState) net_transform = np.get_net_transform(); - LMatrix4 net_mat = net_transform->get_mat(); - LMatrix4 inv = LCAST(PN_stdfloat, egg_parent->get_vertex_frame_inv()); - net_mat = net_mat * inv; - - int num_solids = node->get_num_solids(); - - if (num_solids > 0) { - // create vertex pool for collisions - EggVertexPool *cvpool = new EggVertexPool("vpool-collision"); - egg_group->add_child(cvpool); - - // traverse solids - for (int i = 0; i < num_solids; i++) { - CPT(CollisionSolid) child = node->get_solid(i); - if (child->is_of_type(CollisionPolygon::get_class_type())) { - EggPolygon *egg_poly = new EggPolygon; - egg_group->add_child(egg_poly); - - CPT(CollisionPolygon) poly = DCAST(CollisionPolygon, child); - int num_points = poly->get_num_points(); - for (int j = 0; j < num_points; j++) { - EggVertex egg_vert; - egg_vert.set_pos(LCAST(double, poly->get_point(j) * net_mat)); - egg_vert.set_normal(LCAST(double, poly->get_normal() * net_mat)); - - EggVertex *new_egg_vert = cvpool->create_unique_vertex(egg_vert); - egg_poly->add_vertex(new_egg_vert); - } - } else if (child->is_of_type(CollisionPlane::get_class_type())) { - nout << "Encountered unhandled collsion type: CollisionPlane" << "\n"; - } else if (child->is_of_type(CollisionSphere::get_class_type())) { - nout << "Encountered unhandled collsion type: CollisionSphere" << "\n"; - } else if (child->is_of_type(CollisionInvSphere::get_class_type())) { - nout << "Encountered unhandled collsion type: CollisionInvSphere" << "\n"; - } else if (child->is_of_type(CollisionTube::get_class_type())) { - nout << "Encountered unhandled collsion type: CollisionTube" << "\n"; - } else { - nout << "Encountered unknown CollisionSolid" << "\n"; - } - } - } - - // recurse over children - hm. do I need to do this? - recurse_nodes(node_path, egg_group, has_decal); -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::convert_geom_node -// Access: Private -// Description: Converts a GeomNode to the corresponding egg -// structures. -//////////////////////////////////////////////////////////////////// -void BamToEgg:: -convert_geom_node(GeomNode *node, const WorkingNodePath &node_path, - EggGroupNode *egg_parent, bool has_decal, CharacterJointMap *jointMap) { - PT(EggGroup) egg_group = new EggGroup(node->get_name()); - bool fancy_attributes = apply_node_properties(egg_group, node); - - if (node->get_effects()->has_decal()) { - has_decal = true; - } - - if (has_decal) { - egg_group->set_decal_flag(true); - } - - if (fancy_attributes || has_decal || !node->get_name().empty()) { - // If we have any fancy attributes on the node, or if we're making - // decal geometry, we have to make a special node to hold the - // geometry (normally it would just appear within its parent). - egg_parent->add_child(egg_group.p()); - egg_parent = egg_group; - } - - NodePath np = node_path.get_node_path(); - CPT(RenderState) net_state = np.get_net_state(); - CPT(TransformState) net_transform = np.get_net_transform(); - LMatrix4 net_mat = net_transform->get_mat(); - LMatrix4 inv = LCAST(PN_stdfloat, egg_parent->get_vertex_frame_inv()); - net_mat = net_mat * inv; - - // Now get out all the various kinds of geometry. - int num_geoms = node->get_num_geoms(); - for (int i = 0; i < num_geoms; ++i) { - CPT(RenderState) geom_state = net_state->compose(node->get_geom_state(i)); - - const Geom *geom = node->get_geom(i); - int num_primitives = geom->get_num_primitives(); - for (int j = 0; j < num_primitives; ++j) { - const GeomPrimitive *primitive = geom->get_primitive(j); - CPT(GeomPrimitive) simple = primitive->decompose(); - CPT(GeomVertexData) vdata = geom->get_vertex_data(); - // vdata = vdata->animate_vertices(true, Thread::get_current_thread()); - convert_primitive(vdata, simple, geom_state, - net_mat, egg_parent, jointMap); - } - } - - recurse_nodes(node_path, egg_parent, has_decal); -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::convert_primitive -// Access: Private -// Description: -//////////////////////////////////////////////////////////////////// -void BamToEgg:: -convert_primitive(const GeomVertexData *vertex_data, - const GeomPrimitive *primitive, - const RenderState *net_state, - const LMatrix4 &net_mat, EggGroupNode *egg_parent, - CharacterJointMap *jointMap) { - GeomVertexReader reader(vertex_data); - - // Check for a color scale. - LVecBase4 color_scale(1.0f, 1.0f, 1.0f, 1.0f); - const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, net_state->get_attrib(ColorScaleAttrib::get_class_type())); - if (csa != (const ColorScaleAttrib *)NULL) { - color_scale = csa->get_scale(); - } - - // Check for a color override. - bool has_color_override = false; - bool has_color_off = false; - LColor color_override; - const ColorAttrib *ca = DCAST(ColorAttrib, net_state->get_attrib(ColorAttrib::get_class_type())); - if (ca != (const ColorAttrib *)NULL) { - if (ca->get_color_type() == ColorAttrib::T_flat) { - has_color_override = true; - color_override = ca->get_color(); - color_override.set(color_override[0] * color_scale[0], - color_override[1] * color_scale[1], - color_override[2] * color_scale[2], - color_override[3] * color_scale[3]); - - } else if (ca->get_color_type() == ColorAttrib::T_off) { - has_color_off = true; - } - } - - // Check for a texture. - EggTexture *egg_tex = (EggTexture *)NULL; - const TextureAttrib *ta = DCAST(TextureAttrib, net_state->get_attrib(TextureAttrib::get_class_type())); - if (ta != (const TextureAttrib *)NULL) { - egg_tex = get_egg_texture(ta->get_texture()); - } - - // Check the texture environment - if ((ta != (const TextureAttrib *)NULL) && (egg_tex != (const EggTexture *)NULL)) { - TextureStage* tex_stage = ta->get_on_stage(0); - if (tex_stage != (const TextureStage *)NULL) { - switch (tex_stage->get_mode()) { - case TextureStage::M_modulate: - if (has_color_off == true) { - egg_tex->set_env_type(EggTexture::ET_replace); - } else { - egg_tex->set_env_type(EggTexture::ET_modulate); - } - break; - case TextureStage::M_decal: - egg_tex->set_env_type(EggTexture::ET_decal); - break; - case TextureStage::M_blend: - egg_tex->set_env_type(EggTexture::ET_blend); - break; - case TextureStage::M_replace: - egg_tex->set_env_type(EggTexture::ET_replace); - break; - case TextureStage::M_add: - egg_tex->set_env_type(EggTexture::ET_add); - break; - case TextureStage::M_blend_color_scale: - egg_tex->set_env_type(EggTexture::ET_blend_color_scale); - break; - default: - break; - } - } - } - - // Check the backface flag. - bool bface = false; - const RenderAttrib *cf_attrib = net_state->get_attrib(CullFaceAttrib::get_class_type()); - if (cf_attrib != (const RenderAttrib *)NULL) { - const CullFaceAttrib *cfa = DCAST(CullFaceAttrib, cf_attrib); - if (cfa->get_effective_mode() == CullFaceAttrib::M_cull_none) { - bface = true; - } - } - - // Check the depth write flag - only needed for AM_blend_no_occlude - bool has_depthwrite = false; - DepthWriteAttrib::Mode depthwrite = DepthWriteAttrib::M_on; - const RenderAttrib *dw_attrib = net_state->get_attrib(DepthWriteAttrib::get_class_type()); - if (dw_attrib != (const RenderAttrib *)NULL) { - const DepthWriteAttrib *dwa = DCAST(DepthWriteAttrib, dw_attrib); - depthwrite = dwa->get_mode(); - has_depthwrite = true; - } - - // Check the transparency flag. - bool has_transparency = false; - TransparencyAttrib::Mode transparency = TransparencyAttrib::M_none; - const RenderAttrib *tr_attrib = net_state->get_attrib(TransparencyAttrib::get_class_type()); - if (tr_attrib != (const RenderAttrib *)NULL) { - const TransparencyAttrib *tra = DCAST(TransparencyAttrib, tr_attrib); - transparency = tra->get_mode(); - has_transparency = true; - } - if (has_transparency && (egg_tex != (EggTexture *)NULL)) { - EggRenderMode::AlphaMode tex_trans = EggRenderMode::AM_unspecified; - switch (transparency) { - case TransparencyAttrib::M_none: - tex_trans = EggRenderMode::AM_off; - break; - case TransparencyAttrib::M_alpha: - if (has_depthwrite && (depthwrite == DepthWriteAttrib::M_off)) { - tex_trans = EggRenderMode::AM_blend_no_occlude; - has_depthwrite = false; - } else { - tex_trans = EggRenderMode::AM_blend; - } - break; - case TransparencyAttrib::M_multisample: - tex_trans = EggRenderMode::AM_ms; - break; - case TransparencyAttrib::M_multisample_mask: - tex_trans = EggRenderMode::AM_ms_mask; - break; - case TransparencyAttrib::M_binary: - tex_trans = EggRenderMode::AM_binary; - break; - case TransparencyAttrib::M_dual: - tex_trans = EggRenderMode::AM_dual; - break; - default: // intentional fall-through - case TransparencyAttrib::M_notused: - break; - } - if (tex_trans != EggRenderMode::AM_unspecified) { - egg_tex->set_alpha_mode(tex_trans); - } - } - - - LNormal normal; - LColor color; - CPT(TransformBlendTable) transformBlendTable = vertex_data->get_transform_blend_table(); - - int num_primitives = primitive->get_num_primitives(); - int num_vertices = primitive->get_num_vertices_per_primitive(); - - EggPrimitive *(*make_func)(void); - - if (primitive->is_of_type(GeomTriangles::get_class_type())) { - make_func = make_egg_polygon; - } else if (primitive->is_of_type(GeomPatches::get_class_type())) { - make_func = make_egg_patch; - } else if (primitive->is_of_type(GeomPoints::get_class_type())) { - make_func = make_egg_point; - } else if (primitive->is_of_type(GeomLines::get_class_type())) { - make_func = make_egg_line; - } else { - // Huh, an unknown geometry type. - return; - } - - for (int i = 0; i < num_primitives; ++i) { - PT(EggPrimitive) egg_prim = (*make_func)(); - - egg_parent->add_child(egg_prim); - if (egg_tex != (EggTexture *)NULL) { - egg_prim->set_texture(egg_tex); - } - - if (bface) { - egg_prim->set_bface_flag(true); - } - - for (int j = 0; j < num_vertices; j++) { - EggVertex egg_vert; - - // Get per-vertex properties. - reader.set_row(primitive->get_vertex(i * num_vertices + j)); - - reader.set_column(InternalName::get_vertex()); - LVertex vertex = reader.get_data3(); - egg_vert.set_pos(LCAST(double, vertex * net_mat)); - - if (vertex_data->has_column(InternalName::get_normal())) { - reader.set_column(InternalName::get_normal()); - LNormal normal = reader.get_data3(); - egg_vert.set_normal(LCAST(double, normal * net_mat)); - } - if (has_color_override) { - egg_vert.set_color(color_override); - - } else if (!has_color_off) { - LColor color(1.0f, 1.0f, 1.0f, 1.0f); - if (vertex_data->has_column(InternalName::get_color())) { - reader.set_column(InternalName::get_color()); - color = reader.get_data4(); - } - egg_vert.set_color(LColor(color[0] * color_scale[0], - color[1] * color_scale[1], - color[2] * color_scale[2], - color[3] * color_scale[3])); - } - - if (vertex_data->has_column(InternalName::get_texcoord())) { - reader.set_column(InternalName::get_texcoord()); - LTexCoord uv = reader.get_data2(); - egg_vert.set_uv(LCAST(double, uv)); - } - - EggVertex *new_egg_vert = _vpool->create_unique_vertex(egg_vert); - - if ((vertex_data->has_column(InternalName::get_transform_blend())) && - (jointMap!=NULL) && (transformBlendTable!=NULL)) { - reader.set_column(InternalName::get_transform_blend()); - int idx = reader.get_data1i(); - const TransformBlend &blend = transformBlendTable->get_blend(idx); - int num_weights = blend.get_num_transforms(); - for (int k = 0; k < num_weights; ++k) { - PN_stdfloat weight = blend.get_weight(k); - if (weight!=0) { - const VertexTransform *vertex_transform = blend.get_transform(k); - if (vertex_transform->is_of_type(JointVertexTransform::get_class_type())) { - const JointVertexTransform *joint_vertex_transform = DCAST(const JointVertexTransform, vertex_transform); - - CharacterJointMap::iterator mi = jointMap->find(joint_vertex_transform->get_joint()); - if (mi == jointMap->end()) { - mi = jointMap->insert(CharacterJointMap::value_type(joint_vertex_transform->get_joint(), pvector >())).first; - } - pvector > &joint_vertices = (*mi).second; - joint_vertices.push_back(pair(new_egg_vert, weight)); - } - } - } - } - - egg_prim->add_vertex(new_egg_vert); - } - } -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::recurse_nodes -// Access: Private -// Description: Converts all the children of the indicated node. -//////////////////////////////////////////////////////////////////// -void BamToEgg:: -recurse_nodes(const WorkingNodePath &node_path, EggGroupNode *egg_parent, - bool has_decal) { - PandaNode *node = node_path.node(); - int num_children = node->get_num_children(); - - for (int i = 0; i < num_children; i++) { - PandaNode *child = node->get_child(i); - convert_node(WorkingNodePath(node_path, child), egg_parent, has_decal); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::apply_node_properties -// Access: Public -// Description: Applies any special properties that might be stored -// on the node, like billboarding. Returns true if any -// were applied, false otherwise. -//////////////////////////////////////////////////////////////////// -bool BamToEgg:: -apply_node_properties(EggGroup *egg_group, PandaNode *node, bool allow_backstage) { - bool any_applied = false; - - if (node->is_overall_hidden() && allow_backstage) { - // This node is hidden. We'll go ahead and convert it, but we'll - // put in the "backstage" flag to mean it's not real geometry. - // unless the caller wants to keep it (by setting allow_backstage to false) - egg_group->add_object_type("backstage"); - } - - if (node->has_tags()) { - if (apply_tags(egg_group, node)) { - any_applied = true; - } - } - - if (node->is_of_type(ModelNode::get_class_type())) { - ModelNode *model_node = DCAST(ModelNode, node); - switch (model_node->get_preserve_transform()) { - case ModelNode::PT_none: - case ModelNode::PT_drop_node: - break; - - case ModelNode::PT_net: - egg_group->set_dcs_type(EggGroup::DC_net); - break; - - case ModelNode::PT_local: - egg_group->set_dcs_type(EggGroup::DC_local); - break; - - case ModelNode::PT_no_touch: - egg_group->set_dcs_type(EggGroup::DC_no_touch); - break; - } - } - - const RenderEffects *effects = node->get_effects(); - const RenderEffect *effect = effects->get_effect(BillboardEffect::get_class_type()); - if (effect != (RenderEffect *)NULL) { - const BillboardEffect *bbe = DCAST(BillboardEffect, effect); - if (bbe->get_axial_rotate()) { - egg_group->set_billboard_type(EggGroup::BT_axis); - any_applied = true; - - } else if (bbe->get_eye_relative()) { - egg_group->set_billboard_type(EggGroup::BT_point_camera_relative); - any_applied = true; - - } else { - egg_group->set_billboard_type(EggGroup::BT_point_world_relative); - any_applied = true; - } - } - - const TransformState *transform = node->get_transform(); - if (!transform->is_identity()) { - if (transform->has_components()) { - // If the transform can be represented componentwise, we prefer - // storing it that way in the egg file. - const LVecBase3 &scale = transform->get_scale(); - const LQuaternion &quat = transform->get_quat(); - const LVecBase3 &pos = transform->get_pos(); - if (!scale.almost_equal(LVecBase3(1.0f, 1.0f, 1.0f))) { - egg_group->add_scale3d(LCAST(double, scale)); - } - if (!quat.is_identity()) { - egg_group->add_rotate3d(LCAST(double, quat)); - } - if (!pos.almost_equal(LVecBase3::zero())) { - egg_group->add_translate3d(LCAST(double, pos)); - } - - } else if (transform->has_mat()) { - // Otherwise, we store the raw matrix. - const LMatrix4 &mat = transform->get_mat(); - egg_group->set_transform3d(LCAST(double, mat)); - } - any_applied = true; - } - - return any_applied; -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::apply_tags -// Access: Public -// Description: Applies string tags to the egg file. Returns true if -// any were applied, false otherwise. -//////////////////////////////////////////////////////////////////// -bool BamToEgg:: -apply_tags(EggGroup *egg_group, PandaNode *node) { - ostringstream strm; - char delimiter = '\n'; - string delimiter_str(1, delimiter); - node->list_tags(strm, delimiter_str); - - string data = strm.str(); - if (data.empty()) { - return false; - } - - bool any_applied = false; - - size_t p = 0; - size_t q = data.find(delimiter); - while (q != string::npos) { - string tag = data.substr(p, q); - if (apply_tag(egg_group, node, tag)) { - any_applied = true; - } - p = q + 1; - q = data.find(delimiter, p); - } - - string tag = data.substr(p); - if (apply_tag(egg_group, node, tag)) { - any_applied = true; - } - - return any_applied; -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::apply_tag -// Access: Public -// Description: Applies the named string tags to the egg file. -//////////////////////////////////////////////////////////////////// -bool BamToEgg:: -apply_tag(EggGroup *egg_group, PandaNode *node, const string &tag) { - if (!node->has_tag(tag)) { - return false; - } - - string value = node->get_tag(tag); - egg_group->set_tag(tag, value); - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::get_egg_texture -// Access: Public -// Description: Returns an EggTexture pointer that corresponds to the -// indicated Texture. -//////////////////////////////////////////////////////////////////// -EggTexture *BamToEgg:: -get_egg_texture(Texture *tex) { - if (tex != (Texture *)NULL) { - if (tex->has_filename()) { - Filename filename = _path_replace->convert_path(tex->get_filename()); - EggTexture temp(filename.get_basename_wo_extension(), filename); - if (tex->has_alpha_filename()) { - Filename alpha = _path_replace->convert_path(tex->get_alpha_filename()); - temp.set_alpha_filename(alpha); - } - - switch (tex->get_minfilter()) { - case Texture::FT_nearest: - temp.set_minfilter(EggTexture::FT_nearest); - break; - case Texture::FT_linear: - temp.set_minfilter(EggTexture::FT_linear); - break; - case Texture::FT_nearest_mipmap_nearest: - temp.set_minfilter(EggTexture::FT_nearest_mipmap_nearest); - break; - case Texture::FT_linear_mipmap_nearest: - temp.set_minfilter(EggTexture::FT_linear_mipmap_nearest); - break; - case Texture::FT_nearest_mipmap_linear: - temp.set_minfilter(EggTexture::FT_nearest_mipmap_linear); - break; - case Texture::FT_linear_mipmap_linear: - temp.set_minfilter(EggTexture::FT_linear_mipmap_linear); - break; - - default: - break; - } - - switch (tex->get_magfilter()) { - case Texture::FT_nearest: - temp.set_magfilter(EggTexture::FT_nearest); - break; - case Texture::FT_linear: - temp.set_magfilter(EggTexture::FT_linear); - break; - - default: - break; - } - - switch (tex->get_wrap_u()) { - case Texture::WM_clamp: - temp.set_wrap_u(EggTexture::WM_clamp); - break; - case Texture::WM_repeat: - temp.set_wrap_u(EggTexture::WM_repeat); - break; - - default: - // There are some new wrap options on Texture that aren't yet - // supported in egg. - break; - } - - switch (tex->get_wrap_v()) { - case Texture::WM_clamp: - temp.set_wrap_v(EggTexture::WM_clamp); - break; - case Texture::WM_repeat: - temp.set_wrap_v(EggTexture::WM_repeat); - break; - - default: - // There are some new wrap options on Texture that aren't yet - // supported in egg. - break; - } - - switch (tex->get_format()) { - case Texture::F_red: - temp.set_format(EggTexture::F_red); - break; - case Texture::F_green: - temp.set_format(EggTexture::F_green); - break; - case Texture::F_blue: - temp.set_format(EggTexture::F_blue); - break; - case Texture::F_alpha: - temp.set_format(EggTexture::F_alpha); - break; - case Texture::F_rgb: - temp.set_format(EggTexture::F_rgb); - break; - case Texture::F_rgb5: - temp.set_format(EggTexture::F_rgb5); - break; - case Texture::F_rgb8: - temp.set_format(EggTexture::F_rgb8); - break; - case Texture::F_rgb12: - temp.set_format(EggTexture::F_rgb12); - break; - case Texture::F_rgb332: - temp.set_format(EggTexture::F_rgb332); - break; - case Texture::F_rgba: - temp.set_format(EggTexture::F_rgba); - break; - case Texture::F_rgbm: - temp.set_format(EggTexture::F_rgbm); - break; - case Texture::F_rgba4: - temp.set_format(EggTexture::F_rgba4); - break; - case Texture::F_rgba5: - temp.set_format(EggTexture::F_rgba5); - break; - case Texture::F_rgba8: - temp.set_format(EggTexture::F_rgba8); - break; - case Texture::F_rgba12: - temp.set_format(EggTexture::F_rgba12); - break; - case Texture::F_luminance: - temp.set_format(EggTexture::F_luminance); - break; - case Texture::F_luminance_alpha: - temp.set_format(EggTexture::F_luminance_alpha); - break; - case Texture::F_luminance_alphamask: - temp.set_format(EggTexture::F_luminance_alphamask); - break; - default: - break; - } - - return _textures.create_unique_texture(temp, ~EggTexture::E_tref_name); - } - } - - return NULL; -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::make_egg_polygon -// Access: Public, Static -// Description: A factory function to make a new EggPolygon instance. -//////////////////////////////////////////////////////////////////// -EggPrimitive *BamToEgg:: -make_egg_polygon() { - return new EggPolygon; -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::make_egg_patch -// Access: Public, Static -// Description: A factory function to make a new EggPatch instance. -//////////////////////////////////////////////////////////////////// -EggPrimitive *BamToEgg:: -make_egg_patch() { - return new EggPatch; -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::make_egg_point -// Access: Public, Static -// Description: A factory function to make a new EggPoint instance. -//////////////////////////////////////////////////////////////////// -EggPrimitive *BamToEgg:: -make_egg_point() { - return new EggPoint; -} - -//////////////////////////////////////////////////////////////////// -// Function: BamToEgg::make_egg_line -// Access: Public, Static -// Description: A factory function to make a new EggLine instance. -//////////////////////////////////////////////////////////////////// -EggPrimitive *BamToEgg:: -make_egg_line() { - return new EggLine; -} - int main(int argc, char *argv[]) { // A call to pystub() to force libpystub.so to be linked in. pystub(); diff --git a/pandatool/src/bam/bamToEgg.h b/pandatool/src/bam/bamToEgg.h index 0578b5b37e..9c92dee752 100644 --- a/pandatool/src/bam/bamToEgg.h +++ b/pandatool/src/bam/bamToEgg.h @@ -18,33 +18,6 @@ #include "pandatoolbase.h" #include "somethingToEgg.h" -#include "luse.h" -#include "eggTextureCollection.h" -#include "eggMaterialCollection.h" - -class WorkingNodePath; -class EggGroup; -class EggGroupNode; -class EggVertexPool; -class EggTexture; -class LODNode; -class SequenceNode; -class SwitchNode; -class AnimBundleNode; -class AnimGroup; -class Character; -class PartGroup; -class CollisionNode; -class GeomNode; -class GeomVertexData; -class GeomPrimitive; -class PandaNode; -class RenderState; -class Texture; -class CharacterJoint; -class EggVertex; - -typedef pmap > > CharacterJointMap; //////////////////////////////////////////////////////////////////// // Class : BamToEgg @@ -59,46 +32,6 @@ public: void run(); private: - void convert_node(const WorkingNodePath &node_path, EggGroupNode *egg_parent, - bool has_decal); - void convert_lod_node(LODNode *node, const WorkingNodePath &node_path, - EggGroupNode *egg_parent, bool has_decal); - void convert_sequence_node(SequenceNode *node, const WorkingNodePath &node_path, - EggGroupNode *egg_parent, bool has_decal); - void convert_switch_node(SwitchNode *node, const WorkingNodePath &node_path, - EggGroupNode *egg_parent, bool has_decal); - EggGroupNode *convert_animGroup_node(AnimGroup *animGroup, double fps ); - void convert_anim_node(AnimBundleNode *node, const WorkingNodePath &node_path, - EggGroupNode *egg_parent, bool has_decal); - void convert_character_node(Character *node, const WorkingNodePath &node_path, - EggGroupNode *egg_parent, bool has_decal); - void convert_character_bundle(PartGroup *bundleNode, EggGroupNode *egg_parent, CharacterJointMap *jointMap); - void convert_collision_node(CollisionNode *node, const WorkingNodePath &node_path, - EggGroupNode *egg_parent, bool has_decal); - void convert_geom_node(GeomNode *node, const WorkingNodePath &node_path, - EggGroupNode *egg_parent, bool has_decal, CharacterJointMap *jointMap=NULL); - void convert_primitive(const GeomVertexData *vertex_data, - const GeomPrimitive *primitive, - const RenderState *net_state, - const LMatrix4 &net_mat, EggGroupNode *egg_parent, - CharacterJointMap *jointMap); - - void recurse_nodes(const WorkingNodePath &node_path, EggGroupNode *egg_parent, - bool has_decal); - bool apply_node_properties(EggGroup *egg_group, PandaNode *node, bool allow_backstage = true); - bool apply_tags(EggGroup *egg_group, PandaNode *node); - bool apply_tag(EggGroup *egg_group, PandaNode *node, const string &tag); - - EggTexture *get_egg_texture(Texture *tex); - - static EggPrimitive *make_egg_polygon(); - static EggPrimitive *make_egg_patch(); - static EggPrimitive *make_egg_point(); - static EggPrimitive *make_egg_line(); - - EggVertexPool *_vpool; - EggTextureCollection _textures; - EggMaterialCollection _materials; }; #endif diff --git a/pandatool/src/converter/Sources.pp b/pandatool/src/converter/Sources.pp index 8aa9268885..f6801cf29b 100644 --- a/pandatool/src/converter/Sources.pp +++ b/pandatool/src/converter/Sources.pp @@ -13,9 +13,12 @@ #define SOURCES \ somethingToEggConverter.I somethingToEggConverter.cxx \ - somethingToEggConverter.h + somethingToEggConverter.h \ + eggToSomethingConverter.I eggToSomethingConverter.cxx \ + eggToSomethingConverter.h #define INSTALL_HEADERS \ - somethingToEggConverter.I somethingToEggConverter.h + somethingToEggConverter.I somethingToEggConverter.h \ + eggToSomethingConverter.I eggToSomethingConverter.h #end ss_lib_target diff --git a/pandatool/src/converter/eggToSomethingConverter.I b/pandatool/src/converter/eggToSomethingConverter.I new file mode 100755 index 0000000000..c8c9ae5d91 --- /dev/null +++ b/pandatool/src/converter/eggToSomethingConverter.I @@ -0,0 +1,81 @@ +// Filename: eggToSomethingConverter.I +// Created by: drose (26Sep12) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggToSomethingConverter::clear_error +// Access: Public +// Description: Resets the error flag to the no-error state. +// had_error() will return false until a new error is +// generated. +//////////////////////////////////////////////////////////////////// +INLINE void EggToSomethingConverter:: +clear_error() { + _error = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToSomethingConverter::had_error +// Access: Public +// Description: Returns true if an error was detected during the +// conversion process, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool EggToSomethingConverter:: +had_error() const { + return _error; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToSomethingConverter::clear_egg_data +// Access: Public +// Description: Sets the EggData to NULL and makes the converter +// invalid. +//////////////////////////////////////////////////////////////////// +INLINE void EggToSomethingConverter:: +clear_egg_data() { + set_egg_data((EggData *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToSomethingConverter::get_egg_data +// Access: Public +// Description: Returns the EggData structure. +//////////////////////////////////////////////////////////////////// +INLINE EggData *EggToSomethingConverter:: +get_egg_data() { + return _egg_data; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToSomethingConverter::set_output_units +// Access: Public +// Description: Specifies the units that the EggData has already been +// scaled to. This is informational only; if the target +// file format supports it, this information will be +// written to the header. +//////////////////////////////////////////////////////////////////// +void EggToSomethingConverter:: +set_output_units(DistanceUnit output_units) { + _output_units = output_units; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToSomethingConverter::get_output_units +// Access: Public +// Description: Returns the value supplied to set_output_units(). +//////////////////////////////////////////////////////////////////// +DistanceUnit EggToSomethingConverter:: +get_output_units() const { + return _output_units; +} diff --git a/pandatool/src/converter/eggToSomethingConverter.cxx b/pandatool/src/converter/eggToSomethingConverter.cxx new file mode 100755 index 0000000000..50febd225b --- /dev/null +++ b/pandatool/src/converter/eggToSomethingConverter.cxx @@ -0,0 +1,85 @@ +// Filename: eggToSomethingConverter.cxx +// Created by: drose (26Apr01) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "eggToSomethingConverter.h" + +#include "eggData.h" + +//////////////////////////////////////////////////////////////////// +// Function: EggToSomethingConverter::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggToSomethingConverter:: +EggToSomethingConverter() { + _egg_data = (EggData *)NULL; + _error = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToSomethingConverter::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggToSomethingConverter:: +EggToSomethingConverter(const EggToSomethingConverter ©) { + _egg_data = (EggData *)NULL; + _error = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToSomethingConverter::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +EggToSomethingConverter:: +~EggToSomethingConverter() { + clear_egg_data(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToSomethingConverter::set_egg_data +// Access: Public +// Description: Sets the egg data that will be filled in when +// convert_file() is called. This must be called before +// convert_file(). +//////////////////////////////////////////////////////////////////// +void EggToSomethingConverter:: +set_egg_data(EggData *egg_data) { + _egg_data = egg_data; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToSomethingConverter::get_additional_extensions +// Access: Public, Virtual +// Description: Returns a space-separated list of extension, in +// addition to the one returned by get_extension(), that +// are recognized by this converter. +//////////////////////////////////////////////////////////////////// +string EggToSomethingConverter:: +get_additional_extensions() const { + return string(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToSomethingConverter::supports_compressed +// Access: Published, Virtual +// Description: Returns true if this file type can transparently save +// compressed files (with a .pz extension), false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool EggToSomethingConverter:: +supports_compressed() const { + return false; +} diff --git a/pandatool/src/converter/eggToSomethingConverter.h b/pandatool/src/converter/eggToSomethingConverter.h new file mode 100755 index 0000000000..a9ef4b228e --- /dev/null +++ b/pandatool/src/converter/eggToSomethingConverter.h @@ -0,0 +1,77 @@ +// Filename: eggToSomethingConverter.h +// Created by: drose (26Sep12) +// +//////////////////////////////////////////////////////////////////// +// +// 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 EGGTOSOMETHINGCONVERTER_H +#define EGGTOSOMETHINGCONVERTER_H + +#include "pandatoolbase.h" + +#include "filename.h" +#include "pointerTo.h" +#include "distanceUnit.h" +#include "coordinateSystem.h" + +class EggData; +class EggGroupNode; + +//////////////////////////////////////////////////////////////////// +// Class : EggToSomethingConverter +// Description : This is a base class for a family of converter +// classes that manage a conversion from egg format to +// some other file type. +// +// Classes of this type can be used to implement egg2xxx +// converter programs, as well as LoaderFileTypeXXX +// run-time savers. +//////////////////////////////////////////////////////////////////// +class EggToSomethingConverter { +public: + EggToSomethingConverter(); + EggToSomethingConverter(const EggToSomethingConverter ©); + virtual ~EggToSomethingConverter(); + + virtual EggToSomethingConverter *make_copy()=0; + + INLINE void clear_error(); + INLINE bool had_error() const; + + void set_egg_data(EggData *egg_data); + INLINE void clear_egg_data(); + INLINE EggData *get_egg_data(); + + INLINE void set_output_units(DistanceUnit output_units); + INLINE DistanceUnit get_output_units() const; + INLINE void set_output_coordinate_system(CoordinateSystem output_coordinate_system) const; + INLINE CoordinateSystem get_output_coordinate_system() const; + + virtual string get_name() const=0; + virtual string get_extension() const=0; + virtual string get_additional_extensions() const; + virtual bool supports_compressed() const; + + virtual bool write_file(const Filename &filename)=0; + +protected: + PT(EggData) _egg_data; + DistanceUnit _output_units; + CoordinateSystem _output_coordinate_system; + + bool _error; +}; + +#include "eggToSomethingConverter.I" + +#endif + + diff --git a/pandatool/src/objegg/Sources.pp b/pandatool/src/objegg/Sources.pp index fde6ed7504..276712d8c1 100755 --- a/pandatool/src/objegg/Sources.pp +++ b/pandatool/src/objegg/Sources.pp @@ -15,9 +15,11 @@ #define SOURCES \ config_objegg.cxx config_objegg.h \ - objToEggConverter.cxx objToEggConverter.h + objToEggConverter.cxx objToEggConverter.h \ + eggToObjConverter.cxx eggToObjConverter.h #define INSTALL_HEADERS \ - objToEggConverter.h + objToEggConverter.h \ + eggToObjConverter.h #end ss_lib_target diff --git a/pandatool/src/objegg/eggToObjConverter.cxx b/pandatool/src/objegg/eggToObjConverter.cxx new file mode 100755 index 0000000000..cecfcd2c1f --- /dev/null +++ b/pandatool/src/objegg/eggToObjConverter.cxx @@ -0,0 +1,445 @@ +// 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 "eggData.h" +#include "string_utils.h" +#include "streamReader.h" +#include "virtualFileSystem.h" +#include "eggPolygon.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; + } + + _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 = (void *)(*file) != 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. 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(EggPolygon::get_class_type())) { + EggPolygon *egg_poly = DCAST(EggPolygon, egg_node); + EggPolygon::iterator pi; + for (pi = egg_poly->begin(); pi != egg_poly->end(); ++pi) { + record_vertex(*pi); + } + + } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) { + EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node); + + EggGroupNode::iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + collect_vertices(*ci); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggToObjConverter::write_faces +// Access: Private +// Description: Recursively walks the egg structure again, this time +// writing out the face records for any polygons +// encountered. +//////////////////////////////////////////////////////////////////// +void EggToObjConverter:: +write_faces(ostream &out, EggNode *egg_node) { + if (egg_node->is_of_type(EggPolygon::get_class_type())) { + write_group_reference(out, egg_node); + + EggPolygon *egg_poly = DCAST(EggPolygon, egg_node); + + out << "f"; + EggPolygon::iterator pi; + for (pi = egg_poly->begin(); pi != egg_poly->end(); ++pi) { + VertexDef &vdef = _vmap[(*pi)]; + int vert_index = -1; + int uv_index = -1; + int norm_index = -1; + + if (vdef._vert3_index != -1) { + vert_index = vdef._vert3_index + 1; + } else if (vdef._vert4_index != -1) { + vert_index = vdef._vert4_index + 1 + (int)_unique_vert3.size(); + } + + if (vdef._uv2_index != -1) { + uv_index = vdef._uv2_index + 1; + } else if (vdef._uv3_index != -1) { + uv_index = vdef._uv3_index + 1 + (int)_unique_uv2.size(); + } + + if (vdef._norm_index != -1) { + norm_index = vdef._norm_index + 1; + } + + if (vert_index == -1) { + continue; + } + + if (norm_index != -1) { + if (uv_index != -1) { + out << " " << vert_index << "/" << uv_index << "/" << norm_index; + } else { + out << " " << vert_index << "//" << norm_index; + } + } else if (uv_index != -1) { + out << " " << vert_index << "/" << uv_index; + } else { + out << " " << vert_index; + } + } + out << "\n"; + + } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) { + EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node); + + EggGroupNode::iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + write_faces(out, *ci); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: 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 **)alloca(num_vertices * sizeof(LVecBase4d *)); + memset(vertices, 0, num_vertices * sizeof(LVecBase4d *)); + UniqueVertices::const_iterator ui; + for (ui = unique.begin(); ui != unique.end(); ++ui) { + int index = (*ui).second; + const LVecBase4d &vec = (*ui).first; + nassertv(index >= 0 && index < num_vertices); + nassertv(vertices[index] == NULL); + vertices[index] = &vec; + } + + for (int i = 0; i < num_vertices; ++i) { + out << prefix; + const LVecBase4d &vec = *(vertices[i]); + for (int ci = 0; ci < num_components; ++ci) { + out << " " << vec[ci]; + } + out << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: 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/eggToObjConverter.h b/pandatool/src/objegg/eggToObjConverter.h new file mode 100755 index 0000000000..6aa191e781 --- /dev/null +++ b/pandatool/src/objegg/eggToObjConverter.h @@ -0,0 +1,79 @@ +// Filename: eggToObjConverter.h +// 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." +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGTOOBJCONVERTER_H +#define EGGTOOBJCONVERTER_H + +#include "pandatoolbase.h" + +#include "eggToSomethingConverter.h" +#include "eggVertexPool.h" +#include "eggGroup.h" + +//////////////////////////////////////////////////////////////////// +// Class : EggToObjConverter +// Description : Convert an obj file to egg data. +//////////////////////////////////////////////////////////////////// +class EggToObjConverter : public EggToSomethingConverter { +public: + EggToObjConverter(); + EggToObjConverter(const EggToObjConverter ©); + ~EggToObjConverter(); + + virtual EggToSomethingConverter *make_copy(); + + virtual string get_name() const; + virtual string get_extension() const; + virtual bool supports_compressed() const; + + virtual bool write_file(const Filename &filename); + +private: + typedef pmap UniqueVertices; + class VertexDef { + public: + VertexDef(); + int _vert3_index; + int _vert4_index; + int _uv2_index; + int _uv3_index; + int _norm_index; + }; + typedef pmap VertexMap; + + bool process(const Filename &filename); + + void collect_vertices(EggNode *egg_node); + void write_faces(ostream &out, EggNode *egg_node); + void write_group_reference(ostream &out, EggNode *egg_node); + void get_group_name(string &group_name, EggGroupNode *egg_group); + + void record_vertex(EggVertex *vertex); + int record_unique(UniqueVertices &unique, const LVecBase4d &vec); + int record_unique(UniqueVertices &unique, const LVecBase3d &vec); + int record_unique(UniqueVertices &unique, const LVecBase2d &vec); + int record_unique(UniqueVertices &unique, double pos); + + void write_vertices(ostream &out, const string &prefix, int num_components, + const UniqueVertices &unique); + +private: + bool _triangulate_polygons; + + UniqueVertices _unique_vert3, _unique_vert4, _unique_uv2, _unique_uv3, _unique_norm; + VertexMap _vmap; + EggGroupNode *_current_group; +}; + +#endif diff --git a/pandatool/src/objegg/objToEggConverter.h b/pandatool/src/objegg/objToEggConverter.h index 1d1f56ef17..ebbbde25e9 100755 --- a/pandatool/src/objegg/objToEggConverter.h +++ b/pandatool/src/objegg/objToEggConverter.h @@ -12,8 +12,8 @@ // //////////////////////////////////////////////////////////////////// -#ifndef ObjTOEGGCONVERTER_H -#define ObjTOEGGCONVERTER_H +#ifndef OBJTOEGGCONVERTER_H +#define OBJTOEGGCONVERTER_H #include "pandatoolbase.h" diff --git a/pandatool/src/objprogs/Sources.pp b/pandatool/src/objprogs/Sources.pp index 2f06393e50..5f8cf1f1aa 100755 --- a/pandatool/src/objprogs/Sources.pp +++ b/pandatool/src/objprogs/Sources.pp @@ -22,7 +22,7 @@ #begin bin_target #define TARGET egg2obj - #define LOCAL_LIBS p3eggbase p3progbase + #define LOCAL_LIBS p3objegg p3eggbase p3progbase #define SOURCES \ eggToObj.cxx eggToObj.h diff --git a/pandatool/src/objprogs/eggToObj.cxx b/pandatool/src/objprogs/eggToObj.cxx index f0bd95b649..fd18a1c375 100644 --- a/pandatool/src/objprogs/eggToObj.cxx +++ b/pandatool/src/objprogs/eggToObj.cxx @@ -60,26 +60,10 @@ run() { nout << " (" << num_produced << " triangles produced.)\n"; } - _data->flatten_transforms(); - collect_vertices(_data); + EggToObjConverter saver; + saver.set_egg_data(_data); - ostream &out = get_output(); - _current_group = NULL; - - out << "\n#\n" - << "# obj file generated by the following command:\n" - << "# " << get_exec_command() << "\n" - << "#\n\n"; - - write_vertices(out, "v", 3, _unique_vert3); - write_vertices(out, "v", 4, _unique_vert4); - write_vertices(out, "vt", 2, _unique_uv2); - write_vertices(out, "vt", 3, _unique_uv3); - write_vertices(out, "vn", 3, _unique_norm); - - write_faces(out, _data); - - if (!out) { + if (!saver.write_file(get_output_filename())) { nout << "An error occurred while writing.\n"; exit(1); } @@ -98,293 +82,6 @@ handle_args(ProgramBase::Args &args) { return EggToSomething::handle_args(args); } -//////////////////////////////////////////////////////////////////// -// Function: EggToObj::collect_vertices -// Access: Private -// Description: Recursively walks the egg structure, looking for -// vertices referenced by polygons. Any such vertices -// are added to the vertex tables for writing to the obj -// file. -//////////////////////////////////////////////////////////////////// -void EggToObj:: -collect_vertices(EggNode *egg_node) { - if (egg_node->is_of_type(EggPolygon::get_class_type())) { - EggPolygon *egg_poly = DCAST(EggPolygon, egg_node); - EggPolygon::iterator pi; - for (pi = egg_poly->begin(); pi != egg_poly->end(); ++pi) { - record_vertex(*pi); - } - - } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) { - EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node); - - EggGroupNode::iterator ci; - for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { - collect_vertices(*ci); - } - } -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObj::write_faces -// Access: Private -// Description: Recursively walks the egg structure again, this time -// writing out the face records for any polygons -// encountered. -//////////////////////////////////////////////////////////////////// -void EggToObj:: -write_faces(ostream &out, EggNode *egg_node) { - if (egg_node->is_of_type(EggPolygon::get_class_type())) { - write_group_reference(out, egg_node); - - EggPolygon *egg_poly = DCAST(EggPolygon, egg_node); - - out << "f"; - EggPolygon::iterator pi; - for (pi = egg_poly->begin(); pi != egg_poly->end(); ++pi) { - VertexDef &vdef = _vmap[(*pi)]; - int vert_index = -1; - int uv_index = -1; - int norm_index = -1; - - if (vdef._vert3_index != -1) { - vert_index = vdef._vert3_index + 1; - } else if (vdef._vert4_index != -1) { - vert_index = vdef._vert4_index + 1 + (int)_unique_vert3.size(); - } - - if (vdef._uv2_index != -1) { - uv_index = vdef._uv2_index + 1; - } else if (vdef._uv3_index != -1) { - uv_index = vdef._uv3_index + 1 + (int)_unique_uv2.size(); - } - - if (vdef._norm_index != -1) { - norm_index = vdef._norm_index + 1; - } - - if (vert_index == -1) { - continue; - } - - if (norm_index != -1) { - if (uv_index != -1) { - out << " " << vert_index << "/" << uv_index << "/" << norm_index; - } else { - out << " " << vert_index << "//" << norm_index; - } - } else if (uv_index != -1) { - out << " " << vert_index << "/" << uv_index; - } else { - out << " " << vert_index; - } - } - out << "\n"; - - } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) { - EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node); - - EggGroupNode::iterator ci; - for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { - write_faces(out, *ci); - } - } -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObj::write_group_reference -// Access: Private -// Description: Writes the "g" tag to describe this polygon's group, -// if needed. -//////////////////////////////////////////////////////////////////// -void EggToObj:: -write_group_reference(ostream &out, EggNode *egg_node) { - EggGroupNode *egg_group = egg_node->get_parent(); - if (egg_group == _current_group) { - // Same group we wrote last time. - return; - } - - string group_name; - get_group_name(group_name, egg_group); - if (group_name.empty()) { - out << "g default\n"; - } else { - out << "g" << group_name << "\n"; - } - _current_group = egg_group; -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObj::get_group_name -// Access: Private -// Description: Recursively determines the appropriate string to -// write for the "g" tag to describe a particular -// EggGroupNode. -//////////////////////////////////////////////////////////////////// -void EggToObj:: -get_group_name(string &group_name, EggGroupNode *egg_group) { - string name = trim(egg_group->get_name()); - if (!name.empty()) { - group_name += ' '; - - // Remove nonstandard characters. - for (string::const_iterator ni = name.begin(); ni != name.end(); ++ni) { - char c = (*ni); - if (!isalnum(c)) { - c = '_'; - } - group_name += c; - } - } - - // Now recurse. - EggGroupNode *egg_parent = egg_group->get_parent(); - if (egg_parent != NULL) { - get_group_name(group_name, egg_parent); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObj::record_vertex -// Access: Private -// Description: Adds the indicated EggVertex to the unique vertex -// tables, for writing later by write_vertices(). -//////////////////////////////////////////////////////////////////// -void EggToObj:: -record_vertex(EggVertex *vertex) { - VertexDef &vdef = _vmap[vertex]; - - switch (vertex->get_num_dimensions()) { - case 1: - vdef._vert3_index = record_unique(_unique_vert3, vertex->get_pos1()); - break; - case 2: - vdef._vert3_index = record_unique(_unique_vert3, vertex->get_pos2()); - break; - case 3: - vdef._vert3_index = record_unique(_unique_vert3, vertex->get_pos3()); - break; - case 4: - vdef._vert4_index = record_unique(_unique_vert4, vertex->get_pos4()); - break; - } - - if (vertex->has_uv("")) { - vdef._uv2_index = record_unique(_unique_uv2, vertex->get_uv("")); - } else if (vertex->has_uvw("")) { - vdef._uv3_index = record_unique(_unique_uv3, vertex->get_uvw("")); - } - - if (vertex->has_normal()) { - vdef._norm_index = record_unique(_unique_norm, vertex->get_normal()); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObj::record_unique -// Access: Private -// Description: Records the indicated vertex value, returning the -// shared index if this value already appears elsewhere -// in the table, or the new unique index if this is the -// first time this value appears. -//////////////////////////////////////////////////////////////////// -int EggToObj:: -record_unique(UniqueVertices &unique, const LVecBase4d &vec) { - // We record a zero-based index. Note that we will actually write - // out a one-based index to the obj file, as required by the - // standard. - int index = unique.size(); - UniqueVertices::iterator ui = unique.insert(UniqueVertices::value_type(vec, index)).first; - return (*ui).second; -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObj::record_unique -// Access: Private -// Description: Records the indicated vertex value, returning the -// shared index if this value already appears elsewhere -// in the table, or the new unique index if this is the -// first time this value appears. -//////////////////////////////////////////////////////////////////// -int EggToObj:: -record_unique(UniqueVertices &unique, const LVecBase3d &vec) { - return record_unique(unique, LVecBase4d(vec[0], vec[1], vec[2], 0.0)); -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObj::record_unique -// Access: Private -// Description: Records the indicated vertex value, returning the -// shared index if this value already appears elsewhere -// in the table, or the new unique index if this is the -// first time this value appears. -//////////////////////////////////////////////////////////////////// -int EggToObj:: -record_unique(UniqueVertices &unique, const LVecBase2d &vec) { - return record_unique(unique, LVecBase4d(vec[0], vec[1], 0.0, 0.0)); -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObj::record_unique -// Access: Private -// Description: Records the indicated vertex value, returning the -// shared index if this value already appears elsewhere -// in the table, or the new unique index if this is the -// first time this value appears. -//////////////////////////////////////////////////////////////////// -int EggToObj:: -record_unique(UniqueVertices &unique, double pos) { - return record_unique(unique, LVecBase4d(pos, 0.0, 0.0, 0.0)); -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObj::write_vertices -// Access: Private -// Description: Actually writes the vertex values recorded in the -// indicated table to the obj output stream. -//////////////////////////////////////////////////////////////////// -void EggToObj:: -write_vertices(ostream &out, const string &prefix, int num_components, - const UniqueVertices &unique) { - // First, sort the list into numeric order. - int num_vertices = (int)unique.size(); - const LVecBase4d **vertices = (const LVecBase4d **)alloca(num_vertices * sizeof(LVecBase4d *)); - memset(vertices, 0, num_vertices * sizeof(LVecBase4d *)); - UniqueVertices::const_iterator ui; - for (ui = unique.begin(); ui != unique.end(); ++ui) { - int index = (*ui).second; - const LVecBase4d &vec = (*ui).first; - nassertv(index >= 0 && index < num_vertices); - nassertv(vertices[index] == NULL); - vertices[index] = &vec; - } - - for (int i = 0; i < num_vertices; ++i) { - out << prefix; - const LVecBase4d &vec = *(vertices[i]); - for (int ci = 0; ci < num_components; ++ci) { - out << " " << vec[ci]; - } - out << "\n"; - } -} - -//////////////////////////////////////////////////////////////////// -// Function: EggToObj::VertexDef::Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -EggToObj::VertexDef:: -VertexDef() : - _vert3_index(-1), - _vert4_index(-1), - _uv2_index(-1), - _uv3_index(-1), - _norm_index(-1) -{ -} - int main(int argc, char *argv[]) { // A call to pystub() to force libpystub.so to be linked in. pystub(); diff --git a/pandatool/src/objprogs/eggToObj.h b/pandatool/src/objprogs/eggToObj.h index cd426aa2a9..5719e23893 100644 --- a/pandatool/src/objprogs/eggToObj.h +++ b/pandatool/src/objprogs/eggToObj.h @@ -17,10 +17,7 @@ #include "pandatoolbase.h" #include "eggToSomething.h" -#include "pmap.h" - -class EggNode; -class EggVertex; +#include "eggToObjConverter.h" //////////////////////////////////////////////////////////////////// // Class : EggToObj @@ -35,39 +32,8 @@ public: protected: virtual bool handle_args(Args &args); -private: - typedef pmap UniqueVertices; - class VertexDef { - public: - VertexDef(); - int _vert3_index; - int _vert4_index; - int _uv2_index; - int _uv3_index; - int _norm_index; - }; - typedef pmap VertexMap; - - void collect_vertices(EggNode *egg_node); - void write_faces(ostream &out, EggNode *egg_node); - void write_group_reference(ostream &out, EggNode *egg_node); - void get_group_name(string &group_name, EggGroupNode *egg_group); - - void record_vertex(EggVertex *vertex); - int record_unique(UniqueVertices &unique, const LVecBase4d &vec); - int record_unique(UniqueVertices &unique, const LVecBase3d &vec); - int record_unique(UniqueVertices &unique, const LVecBase2d &vec); - int record_unique(UniqueVertices &unique, double pos); - - void write_vertices(ostream &out, const string &prefix, int num_components, - const UniqueVertices &unique); - private: bool _triangulate_polygons; - - UniqueVertices _unique_vert3, _unique_vert4, _unique_uv2, _unique_uv3, _unique_norm; - VertexMap _vmap; - EggGroupNode *_current_group; }; #endif diff --git a/pandatool/src/ptloader/config_ptloader.cxx b/pandatool/src/ptloader/config_ptloader.cxx index 76d1809aea..5222dae0bf 100644 --- a/pandatool/src/ptloader/config_ptloader.cxx +++ b/pandatool/src/ptloader/config_ptloader.cxx @@ -28,6 +28,7 @@ #include "dxfToEggConverter.h" #include "vrmlToEggConverter.h" #include "objToEggConverter.h" +#include "eggToObjConverter.h" #include "config_xfile.h" #include "xFileToEggConverter.h" @@ -91,8 +92,9 @@ init_libptloader() { XFileToEggConverter *xfile = new XFileToEggConverter; reg->register_type(new LoaderFileTypePandatool(xfile)); - ObjToEggConverter *obj = new ObjToEggConverter; - reg->register_type(new LoaderFileTypePandatool(obj)); + ObjToEggConverter *obj_egg = new ObjToEggConverter; + EggToObjConverter *egg_obj = new EggToObjConverter; + reg->register_type(new LoaderFileTypePandatool(obj_egg, egg_obj)); //#ifdef HAVE_FCOLLADA // DAEToEggConverter *dae = new DAEToEggConverter; diff --git a/pandatool/src/ptloader/loaderFileTypePandatool.cxx b/pandatool/src/ptloader/loaderFileTypePandatool.cxx index 46a9555b7d..7f0fdbf832 100644 --- a/pandatool/src/ptloader/loaderFileTypePandatool.cxx +++ b/pandatool/src/ptloader/loaderFileTypePandatool.cxx @@ -15,8 +15,10 @@ #include "loaderFileTypePandatool.h" #include "config_ptloader.h" #include "somethingToEggConverter.h" +#include "eggToSomethingConverter.h" #include "config_util.h" #include "load_egg_file.h" +#include "save_egg_file.h" #include "eggData.h" #include "loaderOptions.h" #include "bamCacheRecord.h" @@ -29,10 +31,13 @@ TypeHandle LoaderFileTypePandatool::_type_handle; // Description: //////////////////////////////////////////////////////////////////// LoaderFileTypePandatool:: -LoaderFileTypePandatool(SomethingToEggConverter *converter) : - _converter(converter) +LoaderFileTypePandatool(SomethingToEggConverter *loader, + EggToSomethingConverter *saver) : + _loader(loader), _saver(saver) { - converter->set_merge_externals(true); + if (_loader != (SomethingToEggConverter *)NULL) { + _loader->set_merge_externals(true); + } } //////////////////////////////////////////////////////////////////// @@ -51,7 +56,10 @@ LoaderFileTypePandatool:: //////////////////////////////////////////////////////////////////// string LoaderFileTypePandatool:: get_name() const { - return _converter->get_name(); + if (_loader != (SomethingToEggConverter *)NULL) { + return _loader->get_name(); + } + return _saver->get_name(); } //////////////////////////////////////////////////////////////////// @@ -61,7 +69,10 @@ get_name() const { //////////////////////////////////////////////////////////////////// string LoaderFileTypePandatool:: get_extension() const { - return _converter->get_extension(); + if (_loader != (SomethingToEggConverter *)NULL) { + return _loader->get_extension(); + } + return _saver->get_extension(); } //////////////////////////////////////////////////////////////////// @@ -73,7 +84,10 @@ get_extension() const { //////////////////////////////////////////////////////////////////// string LoaderFileTypePandatool:: get_additional_extensions() const { - return _converter->get_additional_extensions(); + if (_loader != (SomethingToEggConverter *)NULL) { + return _loader->get_additional_extensions(); + } + return _saver->get_additional_extensions(); } //////////////////////////////////////////////////////////////////// @@ -85,7 +99,34 @@ get_additional_extensions() const { //////////////////////////////////////////////////////////////////// bool LoaderFileTypePandatool:: supports_compressed() const { - return _converter->supports_compressed(); + if (_loader != (SomethingToEggConverter *)NULL) { + return _loader->supports_compressed(); + } + return _saver->supports_compressed(); +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypePandatool::supports_load +// Access: Published, Virtual +// Description: Returns true if the file type can be used to load +// files, and load_file() is supported. Returns false +// if load_file() is unimplemented and will always fail. +//////////////////////////////////////////////////////////////////// +bool LoaderFileTypePandatool:: +supports_load() const { + return (_loader != NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypePandatool::supports_save +// Access: Published, Virtual +// Description: Returns true if the file type can be used to save +// files, and save_file() is supported. Returns false +// if save_file() is unimplemented and will always fail. +//////////////////////////////////////////////////////////////////// +bool LoaderFileTypePandatool:: +supports_save() const { + return (_saver != NULL); } //////////////////////////////////////////////////////////////////// @@ -108,39 +149,44 @@ resolve_filename(Filename &path) const { PT(PandaNode) LoaderFileTypePandatool:: load_file(const Filename &path, const LoaderOptions &options, BamCacheRecord *record) const { + if (_loader == NULL) { + return NULL; + } + if (record != (BamCacheRecord *)NULL) { record->add_dependent_file(path); } PT(PandaNode) result; + SomethingToEggConverter *loader = _loader->make_copy(); PT(EggData) egg_data = new EggData; - _converter->set_egg_data(egg_data); + loader->set_egg_data(egg_data); DSearchPath file_path; file_path.append_directory(path.get_dirname()); - _converter->get_path_replace()->_path = file_path; + loader->get_path_replace()->_path = file_path; // Convert animation, if the converter supports it. switch (options.get_flags() & LoaderOptions::LF_convert_anim) { case LoaderOptions::LF_convert_anim: - _converter->set_animation_convert(AC_both); + loader->set_animation_convert(AC_both); break; case LoaderOptions::LF_convert_skeleton: - _converter->set_animation_convert(AC_model); + loader->set_animation_convert(AC_model); break; case LoaderOptions::LF_convert_channels: - _converter->set_animation_convert(AC_chan); + loader->set_animation_convert(AC_chan); break; default: break; } - if (_converter->convert_file(path)) { - DistanceUnit input_units = _converter->get_input_units(); + if (loader->convert_file(path)) { + DistanceUnit input_units = loader->get_input_units(); if (input_units != DU_invalid && ptloader_units != DU_invalid && input_units != ptloader_units) { // Convert the file to the units specified by the ptloader-units @@ -160,6 +206,32 @@ load_file(const Filename &path, const LoaderOptions &options, result = load_egg_data(egg_data); } - _converter->clear_egg_data(); + delete loader; + return result.p(); } + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypePandatool::save_file +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +bool LoaderFileTypePandatool:: +save_file(const Filename &path, const LoaderOptions &options, + PandaNode *node) const { + if (_saver == NULL) { + return false; + } + + PT(EggData) egg_data = new EggData; + if (!save_egg_data(egg_data, node)) { + return false; + } + + EggToSomethingConverter *saver = _saver->make_copy(); + saver->set_egg_data(egg_data); + + bool result = saver->write_file(path); + + delete saver; +} diff --git a/pandatool/src/ptloader/loaderFileTypePandatool.h b/pandatool/src/ptloader/loaderFileTypePandatool.h index f96f95a534..943dfc37f4 100644 --- a/pandatool/src/ptloader/loaderFileTypePandatool.h +++ b/pandatool/src/ptloader/loaderFileTypePandatool.h @@ -20,6 +20,7 @@ #include "loaderFileType.h" class SomethingToEggConverter; +class EggToSomethingConverter; //////////////////////////////////////////////////////////////////// // Class : LoaderFileTypePandatool @@ -30,7 +31,8 @@ class SomethingToEggConverter; //////////////////////////////////////////////////////////////////// class EXPCL_PTLOADER LoaderFileTypePandatool : public LoaderFileType { public: - LoaderFileTypePandatool(SomethingToEggConverter *converter); + LoaderFileTypePandatool(SomethingToEggConverter *loader, + EggToSomethingConverter *saver = NULL); virtual ~LoaderFileTypePandatool(); virtual string get_name() const; @@ -38,12 +40,18 @@ public: virtual string get_additional_extensions() const; virtual bool supports_compressed() const; + virtual bool supports_load() const; + virtual bool supports_save() const; + virtual void resolve_filename(Filename &path) const; virtual PT(PandaNode) load_file(const Filename &path, const LoaderOptions &options, BamCacheRecord *record) const; + virtual bool save_file(const Filename &path, const LoaderOptions &options, + PandaNode *node) const; private: - SomethingToEggConverter *_converter; + SomethingToEggConverter *_loader; + EggToSomethingConverter *_saver; public: static TypeHandle get_class_type() {