فهرست منبع

Added Collada converter

rdb 17 سال پیش
والد
کامیت
7e1496141f

+ 27 - 0
pandatool/src/daeegg/Sources.pp

@@ -0,0 +1,27 @@
+#define BUILD_DIRECTORY $[HAVE_FCOLLADA]
+
+#begin lib_target
+  #define USE_PACKAGES fcollada
+  #define TARGET daeegg
+  #define LOCAL_LIBS converter pandatoolbase
+  #define OTHER_LIBS \
+    egg:c pandaegg:m \
+    pandabase:c express:c pandaexpress:m \
+    pipeline:c mathutil:c linmath:c putil:c event:c \
+    panda:m \
+    interrogatedb:c prc:c dconfig:c dtoolconfig:m \
+    dtoolutil:c dtoolbase:c dtool:m
+
+  #define SOURCES \
+    config_daeegg.cxx config_daeegg.h \
+    daeToEggConverter.cxx daeToEggConverter.h \
+    daeCharacter.cxx daeCharacter.h \
+    daeMaterials.cxx daeMaterials.h \
+    pre_fcollada_include.h
+
+  #define INSTALL_HEADERS \
+    config_daeegg.h daeToEggConverter.h \
+    daeCharacter.h daeMaterials.h \
+    pre_fcollada_include.h
+
+#end lib_target

+ 47 - 0
pandatool/src/daeegg/config_daeegg.cxx

@@ -0,0 +1,47 @@
+// Filename: config_daeegg.cxx
+// Created by:  pro-rsoft (30Oct08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "config_daeegg.h"
+#include "daeCharacter.h"
+#include "daeMaterials.h"
+
+#include "dconfig.h"
+
+Configure(config_daeegg);
+NotifyCategoryDef(daeegg, "");
+
+ConfigureFn(config_daeegg) {
+  init_libdaeegg();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: init_libdaeegg
+//  Description: Initializes the library.  This must be called at
+//               least once before any of the functions or classes in
+//               this library can be used.  Normally it will be
+//               called by the static initializers and need not be
+//               called explicitly, but special cases exist.
+////////////////////////////////////////////////////////////////////
+void
+init_libdaeegg() {
+  static bool initialized = false;
+  if (initialized) {
+    return;
+  }
+  initialized = true;
+  
+  DaeCharacter::init_type();
+  DaeMaterials::init_type();
+}
+

+ 25 - 0
pandatool/src/daeegg/config_daeegg.h

@@ -0,0 +1,25 @@
+// Filename: config_daeegg.h
+// Created by:  pro-rsoft (30Oct08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIG_DAEEGG_H
+#define CONFIG_DAEEGG_H
+
+#include "pandatoolbase.h"
+#include "notifyCategoryProxy.h"
+
+NotifyCategoryDeclNoExport(daeegg);
+
+extern void init_libdaeegg();
+
+#endif

+ 107 - 0
pandatool/src/daeegg/daeCharacter.cxx

@@ -0,0 +1,107 @@
+// Filename: daeCharacter.cxx
+// Created by:  pro-rsoft (24Nov08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "daeCharacter.h"
+#include "config_daeegg.h"
+#include "pt_EggVertex.h"
+#include "eggXfmSAnim.h"
+
+#include "FCDocument/FCDocument.h"
+#include "FCDocument/FCDController.h"
+#include "FCDocument/FCDSceneNodeTools.h"
+
+TypeHandle DaeCharacter::_type_handle;
+
+// Useful conversion stuff
+#define TO_VEC3(v) (LVecBase3d(v[0], v[1], v[2]))
+#define TO_VEC4(v) (LVecBase4d(v[0], v[1], v[2], v[3]))
+#define TO_COLOR(v) (Colorf(v[0], v[1], v[2], v[3]))
+#define FROM_VEC3(v) (FMVector3(v[0], v[1], v[2]))
+#define FROM_VEC4(v) (FMVector4(v[0], v[1], v[2], v[3]))
+#define FROM_MAT4(v) (FMMatrix44(v.getData()))
+#define FROM_FSTRING(fs) (string(fs.c_str()))
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeCharacter::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DaeCharacter::
+DaeCharacter(const string name, const FCDControllerInstance* controller_instance) {
+  _controller_instance = (FCDControllerInstance*) controller_instance;
+  _name = name;
+  _frame_rate = 0;
+  _skin_controller = NULL;
+  // If it's a skin controller, add the controller joints.
+  FCDController* controller = (FCDController*) controller_instance->GetEntity();
+  if (controller == NULL) return;
+  if (controller->IsSkin()) {
+    _skin_controller = controller->GetSkinController();
+    if (_skin_controller == NULL) return;
+    for (size_t j = 0; j < _skin_controller->GetJointCount(); ++j) {
+      _controller_joints[FROM_FSTRING(_skin_controller->GetJoint(j)->GetId())] = (FCDSkinControllerJoint*) _skin_controller->GetJoint(j);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeCharacter::as_egg_bundle
+//       Access: Public
+//  Description: Returns the character as a <Bundle> element,
+//               suited for the animation table.
+////////////////////////////////////////////////////////////////////
+PT(EggTable) DaeCharacter::
+as_egg_bundle() {
+  PT(EggTable) bundle = new EggTable(_name);
+  bundle->set_table_type(EggTable::TT_bundle);
+  PT(EggTable) skeleton = new EggTable("<skeleton>");
+  skeleton->set_table_type(EggTable::TT_table);
+  bundle->add_child(skeleton);
+  // Loop through the joint hierarchy
+  FCDSceneNodeList roots = _controller_instance->FindSkeletonNodes();
+  for (FCDSceneNodeList::iterator it = roots.begin(); it != roots.end(); ++it) {
+    process_joint(skeleton, *it);
+  }
+  return bundle;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeCharacter::process_joint
+//       Access: Public
+//  Description: Processes a joint node and its transforms.
+////////////////////////////////////////////////////////////////////
+void DaeCharacter::
+process_joint(PT(EggTable) parent, FCDSceneNode* node) {
+  nassertv(node != NULL);
+  string node_id = FROM_FSTRING(node->GetDaeId());
+  PT(EggTable) joint = new EggTable(node_id);
+  joint->set_table_type(EggTable::TT_table);
+  parent->add_child(joint);
+  PT(EggXfmSAnim) xform = new EggXfmSAnim("xform");
+  joint->add_child(xform);
+  xform->set_fps(_frame_rate);
+  // Generate the sampled animation and loop through the matrices
+  FCDSceneNodeTools::GenerateSampledAnimation(node);
+  FMMatrix44List matrices = FCDSceneNodeTools::GetSampledAnimationMatrices();
+  for (FMMatrix44List::const_iterator it = matrices.begin(); it != matrices.end(); ++it) {
+    LMatrix4d matr = DAEToEggConverter::convert_matrix(*it);
+    assert(xform->add_data(matr));
+  }
+  // Loop through the children joints
+  for (size_t ch = 0; ch < node->GetChildrenCount(); ++ch) {
+    if (node->GetChild(ch)->IsJoint()) {
+      process_joint(joint, node->GetChild(ch));
+    }
+  }
+}

+ 65 - 0
pandatool/src/daeegg/daeCharacter.h

@@ -0,0 +1,65 @@
+// Filename: daeCharacter.h
+// Created by:  pro-rsoft (24Nov08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "pandatoolbase.h"
+#include "typedReferenceCount.h"
+#include "typeHandle.h"
+#include "eggTable.h"
+#include "daeToEggConverter.h"
+
+#include "pre_fcollada_include.h"
+#include "FCollada.h"
+#include "FCDocument/FCDSceneNode.h"
+#include "FCDocument/FCDControllerInstance.h"
+#include "FCDocument/FCDSkinController.h"
+
+#ifndef DAECHARACTER_H
+#define DAECHARACTER_H
+
+////////////////////////////////////////////////////////////////////
+//       Class : DaeCharacter
+// Description : Class representing an animated character.
+////////////////////////////////////////////////////////////////////
+class DaeCharacter : public TypedReferenceCount {
+public:
+  DaeCharacter(const string name, const FCDControllerInstance* controller_instance);
+  PT(EggTable) as_egg_bundle();
+  void process_joint(PT(EggTable) parent, FCDSceneNode* node);
+  
+private:
+  int _frame_rate;
+  string _name;
+  FCDControllerInstance* _controller_instance;
+  FCDSkinController* _skin_controller;
+  pmap<string, FCDSkinControllerJoint*> _controller_joints;
+  
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedReferenceCount::init_type();
+    register_type(_type_handle, "DaeCharacter",
+                  TypedReferenceCount::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif

+ 403 - 0
pandatool/src/daeegg/daeMaterials.cxx

@@ -0,0 +1,403 @@
+// Filename: daeMaterials.cxx
+// Created by:  pro-rsoft (03Oct08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "daeMaterials.h"
+#include "config_daeegg.h"
+
+#include "FCDocument/FCDocument.h"
+#include "FCDocument/FCDMaterial.h"
+#include "FCDocument/FCDEffect.h"
+#include "FCDocument/FCDTexture.h"
+#include "FCDocument/FCDEffectParameterSampler.h"
+#include "FCDocument/FCDImage.h"
+
+#include "filename.h"
+#include "string_utils.h"
+
+TypeHandle DaeMaterials::_type_handle;
+
+// Useful conversion stuff
+#define CONV_VEC3(v) (LVecBase3d(v[0], v[1], v[2]))
+#define CONV_VEC4(v) (LVecBase4d(v[0], v[1], v[2], v[3]))
+#define CONV_COLOR(v) (Colorf(v[0], v[1], v[2], v[3]))
+#define FROM_FSTRING(fs) (string(fs.c_str()))
+
+// luminance function, based on the ISO/CIE color standards
+// see ITU-R Recommendation BT.709-4
+#define luminance(c) ((c[0] * 0.212671 + c[1] * 0.715160 + c[2] * 0.072169))
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeMaterials::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DaeMaterials::
+DaeMaterials(const FCDGeometryInstance* geometry_instance) {
+  for (size_t mi = 0; mi < geometry_instance->GetMaterialInstanceCount(); ++mi) {
+    add_material_instance(geometry_instance->GetMaterialInstance(mi));
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeMaterials::add_material_instance
+//       Access: Public
+//  Description: Adds a material instance. Normally automatically
+//               done by constructor.
+////////////////////////////////////////////////////////////////////
+void DaeMaterials::add_material_instance(const FCDMaterialInstance* instance) {
+  nassertv(instance != NULL);
+  const string semantic (FROM_FSTRING(instance->GetSemantic()));
+  if (_materials.count(semantic) > 0) {
+    daeegg_cat.warning() << "Ignoring duplicate material with semantic" << semantic << endl;
+    return;
+  }
+  _materials[semantic] = new DaeMaterial();
+  
+  // Load in the uvsets
+  for (size_t vib = 0; vib < instance->GetVertexInputBindingCount(); ++vib) {
+    const FCDMaterialInstanceBindVertexInput* mivib = instance->GetVertexInputBinding(vib);
+    assert(mivib != NULL);
+    _materials[semantic]->_uvsets[mivib->inputSet] = FROM_FSTRING(mivib->semantic);
+  }
+  
+  // Handle the material stuff
+  daeegg_cat.spam() << "Trying to process material with semantic " << semantic << endl;
+  PT_EggMaterial egg_material = new EggMaterial(semantic);
+  pvector<PT_EggTexture> egg_textures;
+  const FCDEffect* effect = instance->GetMaterial()->GetEffect();
+  if (effect == NULL) {
+    daeegg_cat.debug() << "Ignoring material (semantic: " << semantic << ") without assigned effect" << endl;
+  } else {
+    // Grab the common profile effect
+    const FCDEffectStandard* effect_common = (FCDEffectStandard *)effect->FindProfile(FUDaeProfileType::COMMON);
+    if (effect_common == NULL) {
+      daeegg_cat.debug() << "Ignoring effect referenced by material with semantic " << semantic
+                         << " because it has no common profile" << endl;
+    } else {
+      daeegg_cat.spam() << "Processing effect, material semantic is " << semantic << endl;
+      // Set the material parameters
+      egg_material->set_amb(CONV_COLOR(effect_common->GetAmbientColor()));
+      ////TODO: find a better way for transparency
+      //LVecBase4f diffuse = CONV_COLOR(effect_common->GetDiffuseColor());
+      //diffuse.set_w(diffuse.get_w() * (1.0f - effect_common->GetOpacity()));
+      //egg_material->set_diff(diffuse);
+      egg_material->set_diff(CONV_COLOR(effect_common->GetDiffuseColor()));
+      egg_material->set_emit(CONV_COLOR(effect_common->GetEmissionColor()) * effect_common->GetEmissionFactor());
+      egg_material->set_shininess(effect_common->GetShininess());
+      egg_material->set_spec(CONV_COLOR(effect_common->GetSpecularColor()));
+      // Now try to load in the textures
+      process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::DIFFUSE, EggTexture::ET_modulate);
+      process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::BUMP, EggTexture::ET_normal);
+      process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::SPECULAR, EggTexture::ET_modulate_gloss);
+      process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::SPECULAR_LEVEL, EggTexture::ET_gloss);
+      process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::OPACITY, EggTexture::ET_unspecified, EggTexture::F_alpha);
+      process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::TRANSPARENT, EggTexture::ET_unspecified, EggTexture::F_alpha);
+      process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::EMISSION, EggTexture::ET_add);
+      // Now, calculate the color blend stuff.
+      _materials[semantic]->_blend = convert_blend(effect_common->GetTransparencyMode(),
+                                        CONV_COLOR(effect_common->GetTranslucencyColor()),
+                                                   effect_common->GetTranslucencyFactor());
+    }
+    // Find an <extra> tag to support some extra stuff from extensions
+    process_extra(semantic, effect->GetExtra());
+  }
+  daeegg_cat.spam() << "Found " << egg_textures.size() << " textures in material" << endl;
+  _materials[semantic]->_egg_material = egg_material;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeMaterials::process_texture_bucket
+//       Access: Private
+//  Description: Processes the given texture bucket and gives
+//               the textures in it the given envtype and format.
+////////////////////////////////////////////////////////////////////
+void DaeMaterials::
+process_texture_bucket(const string semantic, const FCDEffectStandard* effect_common, FUDaeTextureChannel::Channel bucket, EggTexture::EnvType envtype, EggTexture::Format format) {
+  for (size_t tx = 0; tx < effect_common->GetTextureCount(bucket); ++tx) {
+    const FCDImage* image = effect_common->GetTexture(bucket, tx)->GetImage();
+    if (image == NULL) {
+      daeegg_cat.warning() << "Texture references a nonexisting image!" << endl;
+    } else {
+      const FCDEffectParameterSampler* sampler = effect_common->GetTexture(bucket, tx)->GetSampler();
+      // FCollada only supplies absolute paths. We need to grab the document
+      // location ourselves and make the image path absolute.
+      Filename texpath;
+      if (image->GetDocument()) {
+        Filename docpath = Filename::from_os_specific(FROM_FSTRING(image->GetDocument()->GetFileUrl()));
+        docpath.make_canonical();
+        texpath = Filename::from_os_specific(FROM_FSTRING(image->GetFilename()));
+        texpath.make_canonical();
+        texpath.make_relative_to(docpath.get_dirname(), true);
+        daeegg_cat.debug() << "Found texture with path " << texpath << endl;
+      } else {
+        // Never mind.
+        texpath = Filename::from_os_specific(FROM_FSTRING(image->GetFilename()));
+      }
+      PT_EggTexture egg_texture = new EggTexture(FROM_FSTRING(image->GetDaeId()), texpath.to_os_generic());
+      // Find a set of UV coordinates
+      const FCDEffectParameterInt* uvset = effect_common->GetTexture(bucket, tx)->GetSet();
+      if (uvset != NULL) {
+        if (_materials[semantic]->_uvsets.count(uvset->GetValue()) == 0) {
+          daeegg_cat.warning() << "Texture references a nonexisting UV coordinate set!" << endl;
+        } else {
+          egg_texture->set_uv_name(_materials[semantic]->_uvsets[uvset->GetValue()]);
+        }
+      }
+      // Apply sampler stuff
+      if (sampler != NULL) {
+        egg_texture->set_texture_type(convert_texture_type(sampler->GetSamplerType()));
+        egg_texture->set_wrap_u(convert_wrap_mode(sampler->GetWrapS()));
+        if (sampler->GetSamplerType() != FCDEffectParameterSampler::SAMPLER1D) {
+          egg_texture->set_wrap_v(convert_wrap_mode(sampler->GetWrapT()));
+        }
+        if (sampler->GetSamplerType() == FCDEffectParameterSampler::SAMPLER3D) {
+          egg_texture->set_wrap_w(convert_wrap_mode(sampler->GetWrapP()));
+        }
+        egg_texture->set_minfilter(convert_filter_type(sampler->GetMinFilter()));
+        egg_texture->set_magfilter(convert_filter_type(sampler->GetMagFilter()));
+        if (envtype != EggTexture::ET_unspecified) {
+          egg_texture->set_env_type(envtype);
+        }
+        if (format != EggTexture::F_unspecified) {
+          egg_texture->set_format(format);
+        }
+      }
+      _materials[semantic]->_egg_textures.push_back(egg_texture);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeMaterials::process_extra
+//       Access: Private
+//  Description: Processes the extra data in the given <extra> tag.
+//               If the given element is NULL, it just silently
+//               returns.
+////////////////////////////////////////////////////////////////////
+void DaeMaterials::
+process_extra(const string semantic, const FCDExtra* extra) {
+  if (extra == NULL) return;
+  const FCDEType* etype = extra->GetDefaultType();
+  if (etype == NULL) return;
+  for (size_t et = 0; et < etype->GetTechniqueCount(); ++et) {
+    const FCDENode* enode = ((const FCDENode*)(etype->GetTechnique(et)))->FindChildNode("double_sided");
+    if (enode != NULL) {
+      if (trim(enode->GetContent()) == "1") {
+        _materials[semantic]->_double_sided = true;
+      } else if (trim(enode->GetContent()) == "0") {
+        _materials[semantic]->_double_sided = false;
+      } else {
+        daeegg_cat.warning() << "Expected <double_sided> tag to be either 1 or 0, found '" << enode->GetContent() << "' instead" << endl;
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeMaterials::apply_to
+//       Access: Public
+//  Description: Applies the stuff to the given EggPrimitive.
+////////////////////////////////////////////////////////////////////
+void DaeMaterials::
+apply_to(const string semantic, const PT(EggPrimitive) to) {
+  if (_materials.count(semantic) > 0) {
+    to->set_material(_materials[semantic]->_egg_material);
+    for (pvector<PT_EggTexture>::iterator it = _materials[semantic]->_egg_textures.begin(); it != _materials[semantic]->_egg_textures.end(); ++it) {
+      daeegg_cat.spam() << "Applying texture " << (*it)->get_name() << " from material with semantic " << semantic << endl;
+      to->add_texture(*it);
+    }
+    to->set_bface_flag(_materials[semantic]->_double_sided);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeMaterials::apply_to
+//       Access: Public
+//  Description: Applies the colorblend stuff to the given EggGroup.
+////////////////////////////////////////////////////////////////////
+void DaeMaterials::
+apply_to(const string semantic, const PT(EggGroup) to) {
+  if (_materials.count(semantic) > 0) {
+    PT(DaeBlendSettings) blend = _materials[semantic]->_blend;
+    if (blend->_enabled) {
+      to->set_blend_mode(EggGroup::BM_add);
+      to->set_blend_color(blend->_color);
+      to->set_blend_operand_a(blend->_operand_a);
+      to->set_blend_operand_b(blend->_operand_b);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeMaterials::get_uvset_name
+//       Access: Public
+//  Description: Returns the name of the uvset with the specified
+//               FCollada input set, or an empty string if the given
+//               material has no input set.
+////////////////////////////////////////////////////////////////////
+const string DaeMaterials::
+get_uvset_name(const string semantic, int32 input_set) {
+  if (_materials.count(semantic) > 0 && _materials[semantic]->_uvsets.count(input_set)) {
+    return _materials[semantic]->_uvsets[input_set];
+  }
+  return "";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeMaterials::convert_texture_type
+//       Access: Public, Static
+//  Description: Converts an FCollada sampler type to the EggTexture
+//               texture type equivalent.
+////////////////////////////////////////////////////////////////////
+EggTexture::TextureType DaeMaterials::
+convert_texture_type(const FCDEffectParameterSampler::SamplerType orig_type) {
+  switch (orig_type) {
+    case FCDEffectParameterSampler::SAMPLER1D:
+      return EggTexture::TT_1d_texture;
+    case FCDEffectParameterSampler::SAMPLER2D:
+      return EggTexture::TT_2d_texture;
+    case FCDEffectParameterSampler::SAMPLER3D:
+      return EggTexture::TT_3d_texture;
+    case FCDEffectParameterSampler::SAMPLERCUBE:
+      return EggTexture::TT_cube_map;
+    default:
+      daeegg_cat.warning() << "Invalid sampler type found" << endl;
+  }
+  return EggTexture::TT_unspecified;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeMaterials::convert_wrap_mode
+//       Access: Public, Static
+//  Description: Converts an FCollada wrap mode to the
+//               EggTexture wrap mode equivalent.
+////////////////////////////////////////////////////////////////////
+EggTexture::WrapMode DaeMaterials::
+convert_wrap_mode(const FUDaeTextureWrapMode::WrapMode orig_mode) {
+  switch (orig_mode) {
+    case FUDaeTextureWrapMode::NONE:
+      //FIXME: this shouldnt be unspecified
+      return EggTexture::WM_unspecified;
+    case FUDaeTextureWrapMode::WRAP:
+      return EggTexture::WM_repeat;
+    case FUDaeTextureWrapMode::MIRROR:
+      return EggTexture::WM_mirror;
+    case FUDaeTextureWrapMode::CLAMP:
+      return EggTexture::WM_clamp;
+    case FUDaeTextureWrapMode::BORDER:
+      return EggTexture::WM_border_color;
+    case FUDaeTextureWrapMode::UNKNOWN:
+      return EggTexture::WM_unspecified;
+    default:
+      daeegg_cat.warning() << "Invalid wrap mode found: " << FUDaeTextureWrapMode::ToString(orig_mode) << endl;
+  }
+  return EggTexture::WM_unspecified;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeMaterials::convert_filter_type
+//       Access: Public, Static
+//  Description: Converts an FCollada filter function to the
+//               EggTexture wrap type equivalent.
+////////////////////////////////////////////////////////////////////
+EggTexture::FilterType DaeMaterials::
+convert_filter_type(const FUDaeTextureFilterFunction::FilterFunction orig_type) {
+  switch (orig_type) {
+    case FUDaeTextureFilterFunction::NONE:
+      //FIXME: this shouldnt be unspecified
+      return EggTexture::FT_unspecified;
+    case FUDaeTextureFilterFunction::NEAREST:
+      return EggTexture::FT_nearest;
+    case FUDaeTextureFilterFunction::LINEAR:
+      return EggTexture::FT_linear;
+    case FUDaeTextureFilterFunction::NEAREST_MIPMAP_NEAREST:
+      return EggTexture::FT_nearest_mipmap_nearest;
+    case FUDaeTextureFilterFunction::LINEAR_MIPMAP_NEAREST:
+      return EggTexture::FT_linear_mipmap_nearest;
+    case FUDaeTextureFilterFunction::NEAREST_MIPMAP_LINEAR:
+      return EggTexture::FT_nearest_mipmap_linear;
+    case FUDaeTextureFilterFunction::LINEAR_MIPMAP_LINEAR:
+      return EggTexture::FT_linear_mipmap_linear;
+    case FUDaeTextureFilterFunction::UNKNOWN:
+      return EggTexture::FT_unspecified;
+    default:
+      daeegg_cat.warning() << "Unknown filter type found: " << FUDaeTextureFilterFunction::ToString(orig_type) << endl;
+  }
+  return EggTexture::FT_unspecified;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DaeMaterials::convert_blend
+//       Access: Private, Static
+//  Description: Converts collada blend attribs to Panda's equivalents.
+////////////////////////////////////////////////////////////////////
+PT(DaeMaterials::DaeBlendSettings) DaeMaterials::
+convert_blend(FCDEffectStandard::TransparencyMode mode, Colorf transparent, double transparency) {
+  // Create the DaeBlendSettings and fill it with some defaults.
+  PT(DaeBlendSettings) blend = new DaeBlendSettings();
+  blend->_enabled = true;
+  blend->_color = Colorf::zero();
+  blend->_operand_a = EggGroup::BO_unspecified;
+  blend->_operand_b = EggGroup::BO_unspecified;
+  
+  // First fill in the color value.
+  if (mode == FCDEffectStandard::A_ONE) {// || mode == FCDEffectStandard::A_ZERO) {
+    double value = transparent[3] * transparency;
+    blend->_color = Colorf(value, value, value, value);
+  } else if (mode == FCDEffectStandard::RGB_ZERO) {//|| mode == FCDEffectStandard::RGB_ONE) {
+    blend->_color = transparent * transparency;
+    blend->_color[3] = luminance(blend->_color);
+  } else {
+    daeegg_cat.error() << "Unknown opaque type found!" << endl;
+    blend->_enabled = false;
+    return blend;
+  }
+  
+  // Now figure out the operands.
+  if (mode == FCDEffectStandard::RGB_ZERO) {// || mode == FCDEffectStandard::A_ZERO) {
+    blend->_operand_a = EggGroup::BO_constant_color;
+    blend->_operand_b = EggGroup::BO_one_minus_constant_color;
+  } else if (mode == FCDEffectStandard::A_ONE) {// || mode == FCDEffectStandard::RGB_ONE) {
+    blend->_operand_a = EggGroup::BO_one_minus_constant_color;
+    blend->_operand_b = EggGroup::BO_constant_color;
+  } else {
+    daeegg_cat.error() << "Unknown opaque type found!" << endl;
+    blend->_enabled = false;
+    return blend;
+  }
+  
+  // See if we can optimize out the color.
+  if (blend->_operand_a == EggGroup::BO_constant_color) {
+    if (blend->_color[0] == blend->_color[1] == blend->_color[2] == blend->_color[3] == 0) {
+      blend->_operand_a = EggGroup::BO_zero;
+    }
+    if (blend->_color[0] == blend->_color[1] == blend->_color[2] == blend->_color[3] == 1) {
+      blend->_operand_a = EggGroup::BO_one;
+    }
+  }
+  if (blend->_operand_b == EggGroup::BO_one_minus_constant_color) {
+    if (blend->_color[0] == blend->_color[1] == blend->_color[2] == blend->_color[3] == 0) {
+      blend->_operand_b = EggGroup::BO_one;
+    }
+    if (blend->_color[0] == blend->_color[1] == blend->_color[2] == blend->_color[3] == 1) {
+      blend->_operand_b = EggGroup::BO_zero;
+    }
+  }
+  
+  // See if we can entirely disable the blend.
+  if (blend->_operand_a == EggGroup::BO_one && blend->_operand_b == EggGroup::BO_zero) {
+    blend->_enabled = false;
+  }
+  return blend;
+}

+ 97 - 0
pandatool/src/daeegg/daeMaterials.h

@@ -0,0 +1,97 @@
+// Filename: daeMaterials.h
+// Created by:  pro-rsoft (03Oct08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 DAEMATERIALS_H
+#define DAEMATERIALS_H
+
+#include "pandatoolbase.h"
+#include "eggMaterial.h"
+#include "eggTexture.h"
+#include "eggPrimitive.h"
+#include "eggGroup.h"
+#include "pointerTo.h"
+#include "pt_EggTexture.h"
+#include "pt_EggMaterial.h"
+
+#include "pre_fcollada_include.h"
+#include "FCollada.h"
+#include "FCDocument/FCDGeometryInstance.h"
+#include "FCDocument/FCDMaterialInstance.h"
+#include "FCDocument/FCDEffectStandard.h"
+#include "FCDocument/FCDEffectParameterSampler.h"
+#include "FCDocument/FCDExtra.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : DaeMaterials
+// Description : This class is seperated from the converter file
+//               because otherwise it would get too big and
+//               needlessly complicated.
+////////////////////////////////////////////////////////////////////
+class DaeMaterials : public TypedReferenceCount {
+public:
+  DaeMaterials(const FCDGeometryInstance* geometry_instance);
+  virtual ~DaeMaterials() {};
+  
+  void add_material_instance(const FCDMaterialInstance* instance);
+  void apply_to(const string semantic, const PT(EggPrimitive) to);
+  void apply_to(const string semantic, const PT(EggGroup) to);
+  const string get_uvset_name(const string semantic, int32 input_set);
+  
+  static EggTexture::TextureType convert_texture_type(const FCDEffectParameterSampler::SamplerType orig_type);
+  static EggTexture::WrapMode convert_wrap_mode(const FUDaeTextureWrapMode::WrapMode orig_mode);
+  static EggTexture::FilterType convert_filter_type(const FUDaeTextureFilterFunction::FilterFunction orig_type);
+  
+private:
+  // Holds stuff for color blend attribs.
+  struct DaeBlendSettings : public ReferenceCount {
+    bool _enabled;
+    Colorf _color;
+    EggGroup::BlendOperand _operand_a;
+    EggGroup::BlendOperand _operand_b;
+  };
+  
+  // Holds stuff for an individual material.
+  struct DaeMaterial : public ReferenceCount {
+    pvector<PT_EggTexture> _egg_textures;
+    PT_EggMaterial _egg_material;
+    bool _double_sided;
+    pmap<int32, string> _uvsets;
+    PT(DaeBlendSettings) _blend;
+  };
+  
+  void process_texture_bucket(const string semantic, const FCDEffectStandard* effect_common, FUDaeTextureChannel::Channel bucket, EggTexture::EnvType envtype = EggTexture::ET_unspecified, EggTexture::Format format = EggTexture::F_unspecified);
+  void process_extra(const string semantic, const FCDExtra* extra);
+  static PT(DaeBlendSettings) convert_blend(FCDEffectStandard::TransparencyMode mode, Colorf transparent, double transparency);
+  
+  pmap<const string, PT(DaeMaterial)> _materials;
+  
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedReferenceCount::init_type();
+    register_type(_type_handle, "DaeMaterials",
+                  TypedReferenceCount::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#endif

+ 614 - 0
pandatool/src/daeegg/daeToEggConverter.cxx

@@ -0,0 +1,614 @@
+// Filename: daeToEggConverter.cxx
+// Created by:  pro-rsoft (08May08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "daeToEggConverter.h"
+#include "config_daeegg.h"
+#include "daeCharacter.h"
+#include "dcast.h"
+#include "string_utils.h"
+#include "eggData.h"
+#include "eggPrimitive.h"
+#include "eggLine.h"
+#include "eggPolygon.h"
+#include "eggTriangleFan.h"
+#include "eggTriangleStrip.h"
+#include "eggPoint.h"
+#include "eggXfmSAnim.h"
+#include "eggSAnimData.h"
+#include "pt_EggVertex.h"
+
+#include "FCDocument/FCDAsset.h"
+#include "FCDocument/FCDocumentTools.h"
+#include "FCDocument/FCDSceneNode.h"
+#include "FCDocument/FCDSceneNodeTools.h"
+#include "FCDocument/FCDGeometry.h"
+#include "FCDocument/FCDGeometryInstance.h"
+#include "FCDocument/FCDGeometryPolygons.h"
+#include "FCDocument/FCDGeometrySource.h"
+#include "FCDocument/FCDSkinController.h"
+#include "FCDocument/FCDController.h"
+#include "FCDocument/FCDControllerInstance.h"
+#include "FCDocument/FCDMorphController.h"
+#include "FCDocument/FCDMaterialInstance.h"
+#include "FCDocument/FCDExtra.h"
+#include "FCDocument/FCDEffect.h"
+#include "FCDocument/FCDEffectStandard.h"
+
+// Useful conversion stuff
+#define TO_VEC3(v) (LVecBase3d(v[0], v[1], v[2]))
+#define TO_VEC4(v) (LVecBase4d(v[0], v[1], v[2], v[3]))
+#define TO_COLOR(v) (Colorf(v[0], v[1], v[2], v[3]))
+#define FROM_VEC3(v) (FMVector3(v[0], v[1], v[2]))
+#define FROM_VEC4(v) (FMVector4(v[0], v[1], v[2], v[3]))
+#define FROM_MAT4(v) (FMMatrix44(v.getData()))
+#define FROM_FSTRING(fs) (string(fs.c_str()))
+
+////////////////////////////////////////////////////////////////////
+//     Function: DAEToEggConverter::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DAEToEggConverter::
+DAEToEggConverter() {
+  _document = NULL;
+  _table = NULL;
+  _frame_rate = -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DAEToEggConverter::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DAEToEggConverter::
+DAEToEggConverter(const DAEToEggConverter &copy) :
+  SomethingToEggConverter(copy)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DAEToEggConverter::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DAEToEggConverter::
+~DAEToEggConverter() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DAEToEggConverter::make_copy
+//       Access: Public, Virtual
+//  Description: Allocates and returns a new copy of the converter.
+////////////////////////////////////////////////////////////////////
+SomethingToEggConverter *DAEToEggConverter::
+make_copy() {
+  return new DAEToEggConverter(*this);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DAEToEggConverter::get_name
+//       Access: Public, Virtual
+//  Description: Returns the English name of the file type this
+//               converter supports.
+////////////////////////////////////////////////////////////////////
+string DAEToEggConverter::
+get_name() const {
+  return "COLLADA";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DAEToEggConverter::get_extension
+//       Access: Public, Virtual
+//  Description: Returns the common extension of the file type this
+//               converter supports.
+////////////////////////////////////////////////////////////////////
+string DAEToEggConverter::
+get_extension() const {
+  return "dae";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DAEToEggConverter::convert_file
+//       Access: Public, Virtual
+//  Description: Handles the reading of the input file and converting
+//               it to egg.  Returns true if successful, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool DAEToEggConverter::
+convert_file(const Filename &filename) {
+  // Reset stuff
+  clear_error();
+  _joints.clear();
+  _vertex_pools.clear();
+  _skeletons.clear();
+  _frame_rate = -1;
+  
+  // The default coordinate system is Y-up
+  if (_egg_data->get_coordinate_system() == CS_default) {
+    _egg_data->set_coordinate_system(CS_yup_right);
+  }
+  
+  // Read the file
+  FCollada::Initialize();
+  _document = FCollada::LoadDocument(filename.to_os_specific().c_str());
+  if(_document == NULL) {
+    daeegg_cat.error() << "Failed to load document: " << _error_handler.GetErrorString() << endl;
+    FCollada::Release();
+    return false;
+  }
+  // Make sure the file uses consistent coordinate system and length
+  FCDocumentTools::StandardizeUpAxisAndLength(_document);
+  
+  _table = new EggTable();
+  _table->set_table_type(EggTable::TT_table);
+  // Process the stuff
+  process_asset();
+  preprocess();
+  FCDSceneNode* visual_scene = _document->GetVisualSceneInstance();
+  if (visual_scene != NULL) {
+    // First check for an <extra> tag
+    const FCDExtra* extra = visual_scene->GetExtra();
+    //FIXME: eek this looks horrid
+    if (extra != NULL) {
+      const FCDEType* etype = extra->GetDefaultType();
+      if (etype != NULL) {
+        const FCDENode* enode = (const FCDENode*) etype->FindTechnique("MAX3D");
+        if (enode != NULL) {
+          enode = enode->FindChildNode("frame_rate");
+          if (enode != NULL && !string_to_int(enode->GetContent(), _frame_rate)) {
+            daeegg_cat.warning() << "Invalid integer in <frame_rate> tag: '" << enode->GetContent() << "'" << endl;
+    } } } }
+    // Now loop through the children
+    for (size_t ch = 0; ch < visual_scene->GetChildrenCount(); ++ch) {
+      process_node(DCAST(EggGroupNode, _egg_data), visual_scene->GetChild(ch));
+    }
+  }
+  SAFE_DELETE(visual_scene);
+  
+  _egg_data->add_child(_table);
+  
+  // Clean up and return
+  SAFE_DELETE(_document);
+  FCollada::Release();
+  return true;
+}
+
+void DAEToEggConverter::process_asset() {
+  if (_document->GetAsset() == NULL) return;
+  // Read out the coordinate system
+  FMVector3 up_axis (_document->GetAsset()->GetUpAxis());
+  if (up_axis == FMVector3(0, 1, 0)) {
+    _egg_data->set_coordinate_system(CS_yup_right);
+  } else if (up_axis == FMVector3(0, 0, 1)) {
+    _egg_data->set_coordinate_system(CS_zup_right);
+  } else {
+    _egg_data->set_coordinate_system(CS_invalid);
+    daeegg_cat.warning() << "Unrecognized coordinate system!\n";
+  }
+}
+
+// This function lists all the joints and referenced skeletons
+void DAEToEggConverter::preprocess(const FCDSceneNode* node) {
+  // If the node is NULL, take the visual scene instance.
+  if (node == NULL) {
+    assert(_document != NULL);
+    _skeletons.clear();
+    _joints.clear();
+    node = _document->GetVisualSceneInstance();
+  }
+  if (node == NULL) return;
+  if (node->IsJoint()) {
+    _joints[FROM_FSTRING(node->GetDaeId())] = NULL;
+  }
+  // Loop through the instances first.
+  for (size_t in = 0; in < node->GetInstanceCount(); ++in) {
+    if (node->GetInstance(in)->GetType() == FCDEntityInstance::CONTROLLER) {
+      // Loop through the skeleton roots now.
+      FCDSceneNodeList roots = ((FCDControllerInstance*) node->GetInstance(in))->FindSkeletonNodes();
+      for (FCDSceneNodeList::iterator it = roots.begin(); it != roots.end(); ++it) {
+        daeegg_cat.spam() << "Found referenced skeleton root " << FROM_FSTRING((*it)->GetDaeId()) << endl;
+        _skeletons.push_back(FROM_FSTRING((*it)->GetDaeId()));
+      }
+    }
+  }
+  // Now loop through the children and recurse.
+  for (size_t ch = 0; ch < node->GetChildrenCount(); ++ch) {
+    preprocess(node->GetChild(ch));
+  }
+}
+
+// Process the node. If forced is true, it will even process it if its known to be a skeleton root.
+void DAEToEggConverter::process_node(PT(EggGroupNode) parent, const FCDSceneNode* node, bool forced) {
+  nassertv(node != NULL);
+  string node_id = FROM_FSTRING(node->GetDaeId());
+  daeegg_cat.spam() << "Processing node with ID '" << node_id << "'" << endl;
+  // Important! If it's known to be a skeleton root, ignore it for now, unless we're processing forced.
+  if (!forced && count(_skeletons.begin(), _skeletons.end(), node_id) > 0) {
+    daeegg_cat.spam() << "Ignoring skeleton root node with ID '" << node_id << "', we'll process it later" << endl;
+    return;
+  }
+  // Create an egg group for this node
+  PT(EggGroup) node_group = new EggGroup(node_id);
+  parent->add_child(node_group);
+  // Check if its a joint
+  if (node->IsJoint()) {
+    node_group->set_group_type(EggGroup::GT_joint);
+    _joints[node_id] = node_group;
+  }
+  // Loop through the transforms and apply them
+  for (size_t tr = 0; tr < node->GetTransformCount(); ++tr) {
+    apply_transform(node_group, node->GetTransform(tr));
+  }
+  // Loop through the instances and process them
+  for (size_t in = 0; in < node->GetInstanceCount(); ++in) {
+    process_instance(node_group, node->GetInstance(in));
+  }
+  // Loop through the children and recursively process them
+  for (size_t ch = 0; ch < node->GetChildrenCount(); ++ch) {
+    process_node(DCAST(EggGroupNode, node_group), node->GetChild(ch));
+  }
+  // Loop through any possible scene node instances and process those, too.
+  for (size_t in = 0; in < node->GetInstanceCount(); ++in) {
+    if (node->GetInstance(in)->GetEntity() && node->GetInstance(in)->GetEntity()->GetType() == FCDEntity::SCENE_NODE) {
+      process_node(DCAST(EggGroupNode, node_group), (const FCDSceneNode*) node->GetInstance(in)->GetEntity());
+    }
+  }
+}
+
+void DAEToEggConverter::process_instance(PT(EggGroup) parent, const FCDEntityInstance* instance) {
+  nassertv(instance != NULL);
+  nassertv(instance->GetEntity() != NULL);
+  // Check what kind of instance this is
+  switch (instance->GetType()) {
+    case FCDEntityInstance::GEOMETRY: {
+      const FCDGeometry* geometry = (const FCDGeometry*) instance->GetEntity();
+      assert(geometry != NULL);
+      if (geometry->IsMesh()) {
+        // Now, handle the mesh.
+        process_mesh(parent, geometry->GetMesh(), new DaeMaterials((const FCDGeometryInstance*) instance));
+      }
+      if (geometry->IsSpline()) {
+        process_spline(parent, FROM_FSTRING(geometry->GetDaeId()), const_cast<FCDGeometrySpline*> (geometry->GetSpline()));
+      }
+      break; }
+    case FCDEntityInstance::CONTROLLER: {
+      // Add the dart tag and process the controller instance
+      parent->set_dart_type(EggGroup::DT_default);
+      process_controller(parent, (const FCDControllerInstance*) instance);
+      break; }
+    case FCDEntityInstance::MATERIAL:
+      // We don't process this directly, handled per-geometry instead.
+      break;
+    case FCDEntityInstance::SIMPLE: {
+      // Grab the entity and check it's type.
+      const FCDEntity* entity = instance->GetEntity();
+      if (entity->GetType() != FCDEntity::SCENE_NODE) {
+        daeegg_cat.warning() << "Unsupported entity type found" << endl;
+      }
+      break; }
+    default:
+      daeegg_cat.warning() << "Unsupported instance type found" << endl;
+  }
+}
+
+// Processes the given mesh.
+void DAEToEggConverter::process_mesh(PT(EggGroup) parent, const FCDGeometryMesh* mesh, PT(DaeMaterials) materials) {
+  nassertv(mesh != NULL);
+  // Create the egg stuff to hold this mesh
+  PT(EggGroup) mesh_group = new EggGroup(FROM_FSTRING(mesh->GetDaeId()));
+  parent->add_child(mesh_group);
+  PT(EggVertexPool) mesh_pool = new EggVertexPool(FROM_FSTRING(mesh->GetDaeId()));
+  mesh_group->add_child(mesh_pool);
+  _vertex_pools[FROM_FSTRING(mesh->GetDaeId())] = mesh_pool;
+  // First retrieve the vertex source
+  if (mesh->GetSourceCount() == 0) return;
+  const FCDGeometrySource* vsource = mesh->FindSourceByType(FUDaeGeometryInput::POSITION);
+  if (vsource == NULL) return;
+  // Stores which group holds the primitives.
+  PT(EggGroup) primitiveholder;
+  // Loop through the polygon groups and add them
+  for (size_t gr = 0; gr < mesh->GetPolygonsCount(); ++gr) {
+    const FCDGeometryPolygons* polygons = mesh->GetPolygons(gr);
+    primitiveholder = mesh_group;
+    // If we have materials, make a group for each material. Then, apply the material's per-group stuff.
+    if (materials != NULL && (!polygons->GetMaterialSemantic().empty()) && mesh->GetPolygonsCount() > 1) {
+      primitiveholder = new EggGroup(FROM_FSTRING(mesh->GetDaeId()) + FROM_FSTRING(polygons->GetMaterialSemantic()));
+      mesh_group->add_child(primitiveholder);
+    }
+    // Apply the per-group data of the materials, if we have it.
+    if (materials != NULL) {
+      materials->apply_to(FROM_FSTRING(polygons->GetMaterialSemantic()), primitiveholder);
+    }
+    // Find the position sources
+    const FCDGeometryPolygonsInput* pinput = polygons->FindInput(FUDaeGeometryInput::POSITION);
+    assert(pinput != NULL);
+    const uint32* indices = pinput->GetIndices();
+    // Find the normal sources
+    const FCDGeometrySource* nsource = mesh->FindSourceByType(FUDaeGeometryInput::NORMAL);
+    const FCDGeometryPolygonsInput* ninput = polygons->FindInput(FUDaeGeometryInput::NORMAL);
+    const uint32* nindices;
+    if (ninput != NULL) nindices = ninput->GetIndices();
+    // Find texcoord sources
+    const FCDGeometrySource* tcsource = mesh->FindSourceByType(FUDaeGeometryInput::TEXCOORD);
+    const FCDGeometryPolygonsInput* tcinput = polygons->FindInput(FUDaeGeometryInput::TEXCOORD);
+    const uint32* tcindices;
+    if (tcinput != NULL) tcindices = tcinput->GetIndices();
+    // Find vcolor sources
+    const FCDGeometrySource* csource = mesh->FindSourceByType(FUDaeGeometryInput::COLOR);
+    const FCDGeometryPolygonsInput* cinput = polygons->FindInput(FUDaeGeometryInput::COLOR);
+    const uint32* cindices;
+    if (cinput != NULL) cindices = cinput->GetIndices();
+    // Find binormal sources
+    const FCDGeometrySource* bsource = mesh->FindSourceByType(FUDaeGeometryInput::TEXBINORMAL);
+    const FCDGeometryPolygonsInput* binput = polygons->FindInput(FUDaeGeometryInput::TEXBINORMAL);
+    const uint32* bindices;
+    if (binput != NULL) bindices = binput->GetIndices();
+    // Find tangent sources
+    const FCDGeometrySource* tsource = mesh->FindSourceByType(FUDaeGeometryInput::TEXTANGENT);
+    const FCDGeometryPolygonsInput* tinput = polygons->FindInput(FUDaeGeometryInput::TEXTANGENT);
+    const uint32* tindices;
+    if (tinput != NULL) tindices = tinput->GetIndices();
+    // Get a name for potential texture coordinate sets
+    string uvsetname ("");
+    if (materials != NULL && tcinput != NULL) {
+      uvsetname = materials->get_uvset_name(FROM_FSTRING(polygons->GetMaterialSemantic()), tcinput->GetSet());
+    }
+    // Loop through the indices and add the vertices.
+    for (size_t ix = 0; ix < pinput->GetIndexCount(); ++ix) {
+      PT_EggVertex vertex = mesh_pool->make_new_vertex();
+      const float* data = &vsource->GetData()[indices[ix]*3];
+      vertex->set_pos(LPoint3d(data[0], data[1], data[2]));
+      // Process the normal
+      if (nsource != NULL && ninput != NULL) {
+        assert(nsource->GetStride() == 3);
+        data = &nsource->GetData()[nindices[ix]*3];
+        vertex->set_normal(LVecBase3d(data[0], data[1], data[2]));
+      }
+      // Process the texcoords
+      if (tcsource != NULL && tcinput != NULL) {
+        assert(tcsource->GetStride() == 2 || tcsource->GetStride() == 3);
+        data = &tcsource->GetData()[tcindices[ix]*tcsource->GetStride()];
+        if (tcsource->GetStride() == 2) {
+          vertex->set_uv(uvsetname, LPoint2d(data[0], data[1]));
+        } else {
+          vertex->set_uvw(uvsetname, LPoint3d(data[0], data[1], data[2]));
+        }
+      }
+      // Process the color
+      if (csource != NULL && cinput != NULL) {
+        assert(csource->GetStride() == 4);
+        data = &csource->GetData()[cindices[ix]*4];
+        vertex->set_color(Colorf(data[0], data[1], data[2], data[3]));
+      }
+      // Possibly add a UV object
+      if ((bsource != NULL && binput != NULL) || (tsource != NULL && tinput != NULL)) {
+        PT(EggVertexUV) vertex_uv;
+        if (tcsource != NULL && tcinput != NULL) {
+          assert(tcsource->GetStride() == 2 || tcsource->GetStride() == 3);
+          data = &tcsource->GetData()[tcindices[ix]*tcsource->GetStride()];
+          if (tcsource->GetStride() == 2) {
+            vertex_uv = new EggVertexUV(uvsetname, TexCoordd(data[0], data[1]));
+          } else {
+            vertex_uv = new EggVertexUV(uvsetname, TexCoord3d(data[0], data[1], data[2]));
+          }
+        } else {
+          vertex_uv = new EggVertexUV(uvsetname, TexCoordd());
+        }
+        vertex->set_uv_obj(vertex_uv);
+        if (bsource != NULL && binput != NULL) {
+          assert(bsource->GetStride() == 3);
+          data = &bsource->GetData()[bindices[ix]*3];
+          vertex_uv->set_binormal(LVecBase3d(data[0], data[1], data[2]));
+        }
+        if (tsource != NULL && tinput != NULL) {
+          assert(tsource->GetStride() == 3);
+          data = &tsource->GetData()[tindices[ix]*3];
+          vertex_uv->set_tangent(LVecBase3d(data[0], data[1], data[2]));
+        }
+      }
+      vertex->transform(parent->get_node_to_vertex());
+    }
+    // Now loop through the faces
+    for (size_t fa = polygons->GetFaceOffset(); fa < polygons->GetFaceOffset() + polygons->GetFaceCount(); ++fa) {
+      PT(EggPrimitive) primitive = NULL;
+      // Create a primitive that matches the fcollada type
+      switch (polygons->GetPrimitiveType()) {
+        case FCDGeometryPolygons::LINES:
+          primitive = new EggLine();
+          break;
+        case FCDGeometryPolygons::POLYGONS:
+          primitive = new EggPolygon();
+          break;
+        case FCDGeometryPolygons::TRIANGLE_FANS:
+          primitive = new EggTriangleFan();
+          break;
+        case FCDGeometryPolygons::TRIANGLE_STRIPS:
+          primitive = new EggTriangleStrip();
+          break;
+        case FCDGeometryPolygons::POINTS:
+          primitive = new EggPoint();
+          break;
+        case FCDGeometryPolygons::LINE_STRIPS:
+          daeegg_cat.warning() << "Linestrips not yet supported!" << endl;
+          break;
+        default:
+          daeegg_cat.warning() << "Unsupported primitive type found!\n";
+      }
+      if (primitive != NULL) {
+        primitiveholder->add_child(primitive);
+        if (materials != NULL) {
+          materials->apply_to(FROM_FSTRING(polygons->GetMaterialSemantic()), primitive);
+        }
+        for (size_t ve = polygons->GetFaceVertexOffset(fa); ve < polygons->GetFaceVertexOffset(fa) + polygons->GetFaceVertexCount(fa); ++ve) {
+          assert(mesh_pool->has_vertex(ve));
+          primitive->add_vertex(mesh_pool->get_vertex(ve));
+        }
+      }
+    }
+  }
+}
+
+void DAEToEggConverter::process_spline(PT(EggGroup) parent, const string group_name, FCDGeometrySpline* geometry_spline) {
+  assert(geometry_spline != NULL);
+  PT(EggGroup) result = new EggGroup(group_name);
+  parent->add_child(result);
+  //TODO: if its not a nurbs, make it convert between the types
+  if (geometry_spline->GetType() != FUDaeSplineType::NURBS) {
+    daeegg_cat.warning() << "Only NURBS curves are supported (yet)!" << endl;
+  } else {
+    // Loop through the splines
+    for (size_t sp = 0; sp < geometry_spline->GetSplineCount(); ++sp) {
+      process_spline(result, geometry_spline->GetSpline(sp));
+    }
+  }
+}
+
+void DAEToEggConverter::process_spline(PT(EggGroup) parent, const FCDSpline* spline) {
+  assert(spline != NULL);
+  nassertv(spline->GetSplineType() == FUDaeSplineType::NURBS);
+  // Now load in the nurbs curve to the egg library
+  PT(EggNurbsCurve) nurbs_curve = new EggNurbsCurve(FROM_FSTRING(spline->GetName()));
+  parent->add_child(nurbs_curve);
+  //TODO: what value is this?
+  nurbs_curve->setup(0, ((const FCDNURBSSpline*) spline)->GetKnotCount());
+  for (size_t kn = 0; kn < ((const FCDNURBSSpline*) spline)->GetKnotCount(); ++kn) {
+    const float* knot = ((const FCDNURBSSpline*) spline)->GetKnot(kn);
+    assert(knot != NULL);
+    nurbs_curve->set_knot(kn, *knot);
+  }
+  for (size_t cv = 0; cv < spline->GetCVCount(); ++cv) {
+    PT_EggVertex c_vtx = new EggVertex();
+    c_vtx->set_pos(TO_VEC3(*spline->GetCV(cv)));
+    c_vtx->transform(parent->get_node_to_vertex());
+    nurbs_curve->add_vertex(c_vtx);
+  }
+}
+
+void DAEToEggConverter::process_controller(PT(EggGroup) parent, const FCDControllerInstance* instance) {
+  assert(instance != NULL);
+  const FCDController* controller = (const FCDController*) instance->GetEntity();
+  assert(controller != NULL);
+  PT(EggVertexPool) vertex_pool = NULL;
+  // Add the skin geometry
+  const FCDGeometry* geometry = controller->GetBaseGeometry();
+  if (geometry != NULL) {
+    if (geometry->IsMesh()) {
+      process_mesh(parent, geometry->GetMesh(), new DaeMaterials((const FCDGeometryInstance*) instance));
+      if (_vertex_pools.count(FROM_FSTRING(geometry->GetMesh()->GetDaeId()))) {
+        vertex_pool = _vertex_pools[FROM_FSTRING(geometry->GetMesh()->GetDaeId())];
+      }
+    }
+    if (geometry->IsSpline()) {
+      process_spline(parent, FROM_FSTRING(geometry->GetDaeId()), const_cast<FCDGeometrySpline*> (geometry->GetSpline()));
+    }
+  }
+  // Add the joint hierarchy
+  FCDSceneNodeList roots = (const_cast<FCDControllerInstance*> (instance))->FindSkeletonNodes();
+  for (FCDSceneNodeList::iterator it = roots.begin(); it != roots.end(); ++it) {
+    process_node(DCAST(EggGroupNode, parent), *it, true);
+  }
+  if (controller->IsSkin()) {
+    // Load in the vertex influences first
+    pmap<int32, pvector<pair<PT_EggVertex, float> > > influences;
+    if (vertex_pool) {
+      for (size_t in = 0; in < controller->GetSkinController()->GetInfluenceCount(); ++in) {
+        assert(vertex_pool->has_vertex(in));
+        for (size_t pa = 0; pa < controller->GetSkinController()->GetVertexInfluence(in)->GetPairCount(); ++pa) {
+          const FCDJointWeightPair* jwpair = controller->GetSkinController()->GetVertexInfluence(in)->GetPair(pa);
+          influences[jwpair->jointIndex].push_back(pair<PT_EggVertex, float> (vertex_pool->get_vertex(in), jwpair->weight));
+        }
+      }
+    }
+    // Loop through the joints in the vertex influences
+    for (pmap<int32, pvector<pair<PT_EggVertex, float> > >::iterator it = influences.begin(); it != influences.end(); ++it) {
+      if (it->first == -1) {
+        daeegg_cat.warning() << "Ignoring vertex influence with negative joint index\n";
+        //FIXME: Why are there joints with index -1
+      } else {
+        const string joint_id = FROM_FSTRING(controller->GetSkinController()->GetJoint(it->first)->GetId());
+        //TODO: what if the joints have just not been defined yet?
+        if (_joints.count(joint_id) > 0) {
+          if (_joints[joint_id]) {
+            for (pvector<pair<PT_EggVertex, float> >::iterator vi = it->second.begin(); vi != it->second.end(); ++vi) {
+              _joints[joint_id]->ref_vertex(vi->first, vi->second);
+            }
+          } else {
+            daeegg_cat.warning() << "Unprocessed joint being referenced: '" << joint_id << "'" << endl;
+          }
+        } else {
+          daeegg_cat.warning() << "Unknown joint being referenced: '" << joint_id << "'" << endl;
+        }
+      }
+    }
+  }
+  if (controller->IsMorph()) {
+    assert(controller != NULL);
+    const FCDMorphController* morph_controller = controller->GetMorphController();
+    assert(morph_controller != NULL);
+    PT(EggTable) bundle = new EggTable(parent->get_name());
+    bundle->set_table_type(EggTable::TT_bundle);
+    PT(EggTable) morph = new EggTable("morph");
+    morph->set_table_type(EggTable::TT_table);
+    bundle->add_child(morph);
+    // Loop through the morph targets.
+    for (size_t mt = 0; mt < morph_controller->GetTargetCount(); ++mt) {
+      const FCDMorphTarget* morph_target = morph_controller->GetTarget(mt);
+      assert(morph_target != NULL);
+      PT(EggSAnimData) target = new EggSAnimData(FROM_FSTRING(morph_target->GetGeometry()->GetDaeId()));
+      if (morph_target->IsAnimated()) {
+        //TODO
+      } else {
+        target->add_data(morph_target->GetWeight());
+      }
+      morph->add_child(target);
+    }
+  }
+  
+  // Get a <Bundle> for the character and add it to the table
+  PT(DaeCharacter) character = new DaeCharacter(parent->get_name(), instance);
+  _table->add_child(character->as_egg_bundle());
+}
+
+LMatrix4d DAEToEggConverter::convert_matrix(const FMMatrix44& matrix) {
+  LMatrix4d result = LMatrix4d::zeros_mat();
+  for (char x = 0; x < 4; ++x) {
+    for (char y = 0; y < 4; ++y) {
+      result(x, y) = matrix[x][y];
+    }
+  }
+  return result;
+}
+
+void DAEToEggConverter::apply_transform(const PT(EggGroup) to, const FCDTransform* from) {
+  assert(from != NULL);
+  assert(to != NULL);
+  switch (from->GetType()) {
+    case FCDTransform::TRANSLATION:
+      to->add_translate3d(TO_VEC3(((FCDTTranslation*) from)->GetTranslation()));
+      break;
+    case FCDTransform::ROTATION:
+      to->add_rotate3d(((FCDTRotation*) from)->GetAngle(), TO_VEC3(((FCDTRotation*) from)->GetAxis()));
+      break;
+    case FCDTransform::SCALE:
+      to->add_scale3d(TO_VEC3(((FCDTScale*) from)->GetScale()));
+      break;
+    case FCDTransform::MATRIX:
+      to->add_matrix4(convert_matrix(((FCDTMatrix*) from)->GetTransform()));
+      break;
+    default:
+      // We don't know this, so let FCollada convert it into a matrix
+      to->add_matrix4(convert_matrix(from->ToMatrix()));
+  }
+}

+ 84 - 0
pandatool/src/daeegg/daeToEggConverter.h

@@ -0,0 +1,84 @@
+// Filename: daeToEggConverter.h
+// Created by:  pro-rsoft (08May08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 DAETOEGGCONVERTER_H
+#define DAETOEGGCONVERTER_H
+
+#include "pandatoolbase.h"
+#include "somethingToEggConverter.h"
+#include "eggGroup.h"
+#include "eggMaterial.h"
+#include "eggTexture.h"
+#include "eggTable.h"
+#include "eggNurbsCurve.h"
+
+#include "pre_fcollada_include.h"
+#include "FCollada.h"
+#include "FCDocument/FCDocument.h"
+#include "FCDocument/FCDTransform.h"
+#include "FCDocument/FCDEntityInstance.h"
+#include "FCDocument/FCDControllerInstance.h"
+#include "FCDocument/FCDGeometryMesh.h"
+#include "FCDocument/FCDGeometrySpline.h"
+#include "FCDocument/FCDMaterial.h"
+#include "FMath/FMMatrix44.h"
+
+#include "daeMaterials.h"
+#include "pvector.h" // Include last
+
+////////////////////////////////////////////////////////////////////
+//       Class : DAEToEggConverter
+// Description : This class supervises the construction of an
+//               EggData structure from a DAE file.
+////////////////////////////////////////////////////////////////////
+class DAEToEggConverter : public SomethingToEggConverter {
+public:
+  DAEToEggConverter();
+  DAEToEggConverter(const DAEToEggConverter &copy);
+  ~DAEToEggConverter();
+  
+  virtual SomethingToEggConverter *make_copy();
+  
+  virtual string get_name() const;
+  virtual string get_extension() const;
+  
+  virtual bool convert_file(const Filename &filename);
+
+private:
+  
+  PT(EggTable) _table;
+  FCDocument* _document;
+  FUErrorSimpleHandler _error_handler;
+  pmap<const string, PT(EggGroup)> _joints;
+  pmap<const string, PT(EggVertexPool)> _vertex_pools;
+  pvector<string> _skeletons;
+  int _frame_rate;
+  
+  void process_asset();
+  void preprocess(const FCDSceneNode* node = NULL);
+  void process_node(PT(EggGroupNode) parent, const FCDSceneNode* node, bool forced = false);
+  void process_instance(PT(EggGroup) parent, const FCDEntityInstance* instance);
+  void process_mesh(PT(EggGroup) parent, const FCDGeometryMesh* mesh, PT(DaeMaterials) materials);
+  void process_spline(PT(EggGroup) parent, const string group_name, FCDGeometrySpline* geometry_spline);
+  void process_spline(PT(EggGroup) parent, const FCDSpline* spline);
+  void process_controller(PT(EggGroup) parent, const FCDControllerInstance* instance);
+  //void process_table_joint(PT(EggTable) parent, FCDSceneNode* node);
+  
+  static LMatrix4d convert_matrix(const FMMatrix44& matrix);
+  void apply_transform(const PT(EggGroup) to, const FCDTransform* from);
+  
+  friend class DaeCharacter;
+};
+
+#endif

+ 5 - 0
pandatool/src/daeegg/daeegg_composite1.cxx

@@ -0,0 +1,5 @@
+
+#include "config_daeegg.cxx"
+#include "daeCharacter.cxx"
+#include "daeMaterials.cxx"
+#include "daeToEggConverter.cxx"

+ 26 - 0
pandatool/src/daeegg/pre_fcollada_include.h

@@ -0,0 +1,26 @@
+// Filename: pre_fcollada_include.h
+// Created by:  pro-rsoft (04Oct08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+// This file defines some stuff that need to be defined before
+// one includes FCollada.h
+
+// FCollada expects LINUX to be defined on linux
+#ifdef IS_LINUX
+  #ifndef LINUX
+    #define LINUX
+  #endif
+#endif
+
+#define NO_LIBXML
+#define FCOLLADA_NOMINMAX

+ 25 - 0
pandatool/src/daeprogs/Sources.pp

@@ -0,0 +1,25 @@
+#define BUILD_DIRECTORY $[HAVE_FCOLLADA]
+
+#define OTHER_LIBS \
+  egg:c pandaegg:m \
+  pipeline:c pnmimage:c putil:c event:c mathutil:c linmath:c panda:m \
+  pandabase:c express:c pandaexpress:m \
+  interrogatedb:c dtoolutil:c dtoolbase:c prc:c dconfig:c dtoolconfig:m dtool:m pystub
+
+#begin bin_target
+  #define TARGET dae2egg
+  #define LOCAL_LIBS daeegg eggbase progbase
+
+  #define SOURCES \
+    daeToEgg.cxx daeToEgg.h
+
+#end bin_target
+
+#begin bin_target
+  #define TARGET egg2dae
+  #define LOCAL_LIBS daeegg eggbase progbase
+
+  #define SOURCES \
+    eggToDAE.cxx eggToDAE.h
+
+#end bin_target

+ 74 - 0
pandatool/src/daeprogs/daeToEgg.cxx

@@ -0,0 +1,74 @@
+// Filename: daeToEgg.cxx
+// Created by:  pro-rsoft (08May08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "daeToEgg.h"
+
+#include "daeToEggConverter.h"
+#include "pystub.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: DAEToEgg::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DAEToEgg::
+DAEToEgg():
+  SomethingToEgg("COLLADA", ".dae")
+{
+  add_units_options();
+  add_normals_options();
+  add_transform_options();
+
+  set_program_description
+    ("This program converts .dae files (COLLADA Digital Asset Exchange) to .egg.");
+
+  _coordinate_system = CS_yup_right;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DAEToEgg::run
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DAEToEgg::
+run() {
+  nout << "Reading " << _input_filename << "\n";
+
+  _data->set_coordinate_system(_coordinate_system);
+
+  DAEToEggConverter converter;
+  converter.set_egg_data(_data);
+  converter._allow_errors = _allow_errors;
+
+  apply_parameters(converter);
+
+  if (!converter.convert_file(_input_filename)) {
+    nout << "Errors in conversion.\n";
+    exit(1);
+  }
+
+  write_egg_file();
+  nout << "\n";
+}
+
+
+int main(int argc, char *argv[]) {
+  // A call to pystub() to force libpystub.so to be linked in.
+  pystub();
+
+  DAEToEgg prog;
+  prog.parse_command_line(argc, argv);
+  prog.run();
+  return 0;
+}

+ 35 - 0
pandatool/src/daeprogs/daeToEgg.h

@@ -0,0 +1,35 @@
+// Filename: daeToEgg.h
+// Created by:  pro-rsoft (08May08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 DAETOEGG_H
+#define DAETOEGG_H
+
+#include "pandatoolbase.h"
+
+#include "somethingToEgg.h"
+#include "daeToEggConverter.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : DAEToEgg
+// Description : A program to read a DAE file and generate an egg
+//               file.
+////////////////////////////////////////////////////////////////////
+class DAEToEgg : public SomethingToEgg {
+public:
+  DAEToEgg();
+
+  void run();
+};
+
+#endif

+ 182 - 0
pandatool/src/daeprogs/eggToDAE.cxx

@@ -0,0 +1,182 @@
+// Filename: eggToDAE.cxx
+// Created by:  pro-rsoft (04Oct08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "eggToDAE.h"
+#include "dcast.h"
+#include "pystub.h"
+#include "pandaVersion.h"
+
+#include "FCDocument/FCDocument.h"
+#include "FCDocument/FCDAsset.h"
+#include "FCDocument/FCDTransform.h"
+
+// Useful conversion stuff
+#define TO_VEC3(v) (LVecBase3d(v[0], v[1], v[2]))
+#define TO_VEC4(v) (LVecBase4d(v[0], v[1], v[2], v[3]))
+#define TO_COLOR(v) (Colorf(v[0], v[1], v[2], v[3]))
+#define FROM_VEC3(v) (FMVector3(v[0], v[1], v[2]))
+#define FROM_VEC4(v) (FMVector4(v[0], v[1], v[2], v[3]))
+#define FROM_MAT4(v) (FMMatrix44(v.get_data()))
+#define FROM_FSTRING(fs) (fs.c_str())
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggToDAE::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+EggToDAE::
+EggToDAE() :
+  EggToSomething("COLLADA", ".dae", true, false)
+{
+  set_binary_output(false);
+  set_program_description
+    ("This program converts files from the egg format to the COLLADA "
+     ".dae (Digital Asset Exchange) format.");
+
+  _document = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggToDAE::run
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void EggToDAE::
+run() {
+  nassertv(has_output_filename());
+  nassertv(_data != NULL);
+  
+  FCollada::Initialize();
+  _document = FCollada::NewTopDocument();
+  
+  // Add the contributor part to the asset
+  FCDAssetContributor* contributor = _document->GetAsset()->AddContributor();
+  const char* user_name = getenv("USER");
+  if (user_name == NULL) user_name = getenv("USERNAME");
+  if (user_name != NULL) contributor->SetAuthor(TO_FSTRING(user_name));
+  //contributor->SetSourceData();
+  char authoring_tool[1024];
+  snprintf(authoring_tool, 1024, "Panda3D %s eggToDAE converter | FCollada v%d.%02d", PANDA_VERSION_STR, FCOLLADA_VERSION >> 16, FCOLLADA_VERSION & 0xFFFF);
+  authoring_tool[1023] = 0;
+  contributor->SetAuthoringTool(TO_FSTRING(authoring_tool));
+  
+  // Set coordinate system
+  switch (_data->get_coordinate_system()) {
+    case CS_zup_right:
+      _document->GetAsset()->SetUpAxis(FMVector3::ZAxis);
+      break;
+    case CS_yup_right:
+      _document->GetAsset()->SetUpAxis(FMVector3::YAxis);
+      break;
+  }
+  
+  // Now actually start processing the data.
+  FCDSceneNode* visual_scene = _document->AddVisualScene();
+  for (EggGroupNode::iterator it = _data->begin(); it != _data->end(); ++it) {
+    if ((*it)->is_of_type(EggGroup::get_class_type())) {
+      process_node(visual_scene, DCAST(EggGroup, *it));
+    }
+  }
+  
+  // We're done here.
+  FCollada::SaveDocument(_document, get_output_filename().to_os_specific().c_str());
+  SAFE_DELETE(_document);
+  FCollada::Release();
+  
+  //if (!out) {
+  //  nout << "An error occurred while writing.\n";
+  //  exit(1);
+  //}
+}
+
+void EggToDAE::process_node(FCDSceneNode* parent, const PT(EggGroup) node) {
+  assert(node != NULL);
+  FCDSceneNode* scene_node = parent->AddChildNode();
+  // Set the parameters
+  scene_node->SetDaeId(node->get_name().c_str());
+  scene_node->SetJointFlag(node->is_joint());
+  // Apply the transforms
+  apply_transform(scene_node, node);
+  // Recursively process sub-nodes
+  for (EggGroupNode::iterator it = node->begin(); it != node->end(); ++it) {
+    if ((*it)->is_of_type(EggGroup::get_class_type())) {
+      process_node(scene_node, DCAST(EggGroup, *it));
+    }
+  }
+}
+
+void EggToDAE::apply_transform(FCDSceneNode* to, const PT(EggGroup) from) {
+  assert(to != NULL);
+  assert(from != NULL);
+  for (int co = 0; co < from->get_num_components(); ++co) {
+    switch (from->get_component_type(co)) {
+      case EggTransform::CT_translate2d:
+        cerr << "Warning: ignoring non-supported 2d translation\n";
+        break;
+      case EggTransform::CT_rotate2d:
+        cerr << "Warning: ignoring non-supported 2d rotation\n";
+        break;
+      case EggTransform::CT_scale2d:
+        cerr << "Warning: ignoring non-supported 2d scaling\n";
+        break;
+      case EggTransform::CT_matrix3:
+        cerr << "Warning: ignoring non-supported 2d matrix\n";
+        break;
+      case EggTransform::CT_translate3d: {
+        FCDTTranslation* new_transform = (FCDTTranslation*) to->AddTransform(FCDTransform::TRANSLATION);
+        new_transform->SetTranslation(FROM_VEC3(from->get_component_vec3(co)));
+        break; }
+      case EggTransform::CT_rotate3d: {
+        FCDTRotation* new_transform = (FCDTRotation*) to->AddTransform(FCDTransform::ROTATION);
+        new_transform->SetRotation(FROM_VEC3(from->get_component_vec3(co)), from->get_component_number(co));
+        break; }
+      case EggTransform::CT_scale3d: {
+        FCDTScale* new_transform = (FCDTScale*) to->AddTransform(FCDTransform::SCALE);
+        new_transform->SetScale(FROM_VEC3(from->get_component_vec3(co)));
+        break; }
+      case EggTransform::CT_matrix4: {
+        FCDTMatrix* new_transform = (FCDTMatrix*) to->AddTransform(FCDTransform::MATRIX);
+        new_transform->SetTransform(FROM_MAT4(from->get_component_mat4(co)));
+        break; }
+      case EggTransform::CT_rotx: {
+        FCDTRotation* new_transform = (FCDTRotation*) to->AddTransform(FCDTransform::ROTATION);
+        new_transform->SetRotation(FMVector3::XAxis, from->get_component_number(co));
+        break; }
+      case EggTransform::CT_roty: {
+        FCDTRotation* new_transform = (FCDTRotation*) to->AddTransform(FCDTransform::ROTATION);
+        new_transform->SetRotation(FMVector3::YAxis, from->get_component_number(co));
+        break; }
+      case EggTransform::CT_rotz: {
+        FCDTRotation* new_transform = (FCDTRotation*) to->AddTransform(FCDTransform::ROTATION);
+        new_transform->SetRotation(FMVector3::ZAxis, from->get_component_number(co));
+        break; }
+      case EggTransform::CT_uniform_scale: {
+        FCDTScale* new_transform = (FCDTScale*) to->AddTransform(FCDTransform::SCALE);
+        new_transform->SetScale(from->get_component_number(co), from->get_component_number(co), from->get_component_number(co));
+        break; }
+      default:
+        cerr << "Warning: ignoring invalid transform\n";
+    }
+  }
+}
+
+int main(int argc, char *argv[]) {
+  // A call to pystub() to force libpystub.so to be linked in.
+  pystub();
+
+  EggToDAE prog;
+  prog.parse_command_line(argc, argv);
+  prog.run();
+  return 0;
+}

+ 46 - 0
pandatool/src/daeprogs/eggToDAE.h

@@ -0,0 +1,46 @@
+// Filename: eggToDAE.h
+// Created by:  pro-rsoft (04Oct08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 EGGTODAE_H
+#define EGGTODAE_H
+
+#include "pandatoolbase.h"
+#include "eggToSomething.h"
+#include "eggGroup.h"
+#include "eggTransform.h"
+
+#include "pre_fcollada_include.h"
+#include "FCollada.h"
+#include "FCDocument/FCDSceneNode.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : EggToDAE
+// Description : A program to read an egg file and write a DAE file.
+////////////////////////////////////////////////////////////////////
+class EggToDAE : public EggToSomething {
+public:
+  EggToDAE();
+
+  void run();
+
+private:
+  FCDocument* _document;
+  
+  void process_node(FCDSceneNode* parent, const PT(EggGroup) node);
+  void apply_transform(FCDSceneNode* to, const PT(EggGroup) from);
+
+};
+
+#endif
+