Panda3D
 All Classes Functions Variables Enumerations
colladaLoader.cxx
00001 // Filename: colladaLoader.cxx
00002 // Created by: Xidram (21Dec10)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "colladaLoader.h"
00016 #include "virtualFileSystem.h"
00017 #include "luse.h"
00018 #include "string_utils.h"
00019 #include "geomNode.h"
00020 #include "geomVertexWriter.h"
00021 #include "geomTriangles.h"
00022 #include "lightNode.h"
00023 #include "lightAttrib.h"
00024 #include "ambientLight.h"
00025 #include "directionalLight.h"
00026 #include "pointLight.h"
00027 #include "spotlight.h"
00028 
00029 #include "colladaBindMaterial.h"
00030 #include "colladaPrimitive.h"
00031 
00032 // Collada DOM includes.  No other includes beyond this point.
00033 #include "pre_collada_include.h"
00034 #include <dom/domCOLLADA.h>
00035 #include <dom/domNode.h>
00036 #include <dom/domVisual_scene.h>
00037 #include <dom/domTranslate.h>
00038 #include <dom/domRotate.h>
00039 #include <dom/domMatrix.h>
00040 
00041 #if PANDA_COLLADA_VERSION >= 15
00042 #include <dom/domInstance_with_extra.h>
00043 #else
00044 #include <dom/domInstanceWithExtra.h>
00045 #define domInstance_with_extra domInstanceWithExtra
00046 #define domTargetable_floatRef domTargetableFloatRef
00047 #endif
00048 
00049 #define TOSTRING(x) (x == NULL ? "" : x)
00050 
00051 ////////////////////////////////////////////////////////////////////
00052 //     Function: ColladaLoader::Constructor
00053 //  Description:
00054 ////////////////////////////////////////////////////////////////////
00055 ColladaLoader::
00056 ColladaLoader() :
00057   _record (NULL),
00058   _cs (CS_default),
00059   _error (false),
00060   _root (NULL),
00061   _collada (NULL) {
00062 
00063   _dae = new DAE;
00064 }
00065 
00066 ////////////////////////////////////////////////////////////////////
00067 //     Function: ColladaLoader::Destructor
00068 //  Description:
00069 ////////////////////////////////////////////////////////////////////
00070 ColladaLoader::
00071 ~ColladaLoader() {
00072   delete _dae;
00073 }
00074 
00075 ////////////////////////////////////////////////////////////////////
00076 //     Function: ColladaLoader::read
00077 //  Description: Reads from the indicated file.
00078 ////////////////////////////////////////////////////////////////////
00079 bool ColladaLoader::
00080 read(const Filename &filename) {
00081   _filename = filename;
00082 
00083   string data;
00084   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00085 
00086   if (!vfs->read_file(_filename, data, true)) {
00087     collada_cat.error()
00088       << "Error reading " << _filename << "\n";
00089     _error = true;
00090     return false;
00091   }
00092 
00093   _collada = _dae->openFromMemory(_filename.to_os_specific(), data.c_str());
00094   _error = (_collada == NULL);
00095   return !_error;
00096 }
00097 
00098 ////////////////////////////////////////////////////////////////////
00099 //     Function: ColladaLoader::build_graph
00100 //  Description: Converts scene graph structures into a Panda3D
00101 //               scene graph, with _root being the root node.
00102 ////////////////////////////////////////////////////////////////////
00103 void ColladaLoader::
00104 build_graph() {
00105   nassertv(_collada); // read() must be called first
00106   nassertv(!_error);  // and have succeeded
00107 
00108   _root = new ModelRoot(_filename.get_basename());
00109 
00110   domCOLLADA::domScene* scene = _collada->getScene();
00111   domInstance_with_extra* inst = scene->getInstance_visual_scene();
00112   domVisual_scene* vscene = daeSafeCast<domVisual_scene> (inst->getUrl().getElement());
00113   if (vscene) {
00114     load_visual_scene(*vscene, _root);
00115   }
00116 }
00117 
00118 ////////////////////////////////////////////////////////////////////
00119 //     Function: ColladaLoader::load_visual_scene
00120 //  Description: Loads a visual scene structure.
00121 ////////////////////////////////////////////////////////////////////
00122 void ColladaLoader::
00123 load_visual_scene(domVisual_scene& scene, PandaNode *parent) {
00124   // If we already loaded it before, instantiate the stored node.
00125   if (scene.getUserData() != NULL) {
00126     parent->add_child((PandaNode *) scene.getUserData());
00127     return;
00128   }
00129 
00130   PT(PandaNode) pnode = new PandaNode(TOSTRING(scene.getName()));
00131   scene.setUserData((void *) pnode);
00132   parent->add_child(pnode);
00133 
00134   // Load in any tags.
00135   domExtra_Array &extras = scene.getExtra_array();
00136   for (size_t i = 0; i < extras.getCount(); ++i) {
00137     load_tags(*extras[i], pnode);
00138   }
00139 
00140   // Now load in the child nodes.
00141   domNode_Array &nodes = scene.getNode_array();
00142   for (size_t i = 0; i < nodes.getCount(); ++i) {
00143     load_node(*nodes[i], pnode);
00144   }
00145 
00146   // Apply any lights we've encountered to the visual scene.
00147   if (_lights.size() > 0) {
00148     CPT(LightAttrib) lattr = DCAST(LightAttrib, LightAttrib::make());
00149     pvector<LightNode*>::iterator it;
00150     for (it = _lights.begin(); it != _lights.end(); ++it) {
00151       lattr = DCAST(LightAttrib, lattr->add_on_light(*it));
00152     }
00153     pnode->set_state(RenderState::make(lattr));
00154 
00155     _lights.clear();
00156   }
00157 }
00158 
00159 ////////////////////////////////////////////////////////////////////
00160 //     Function: ColladaLoader::load_node
00161 //  Description: Loads a COLLADA <node>.
00162 ////////////////////////////////////////////////////////////////////
00163 void ColladaLoader::
00164 load_node(domNode& node, PandaNode *parent) {
00165   // If we already loaded it before, instantiate the stored node.
00166   if (node.getUserData() != NULL) {
00167     parent->add_child((PandaNode *) node.getUserData());
00168     return;
00169   }
00170 
00171   // Create the node.
00172   PT(PandaNode) pnode;
00173   pnode = new PandaNode(TOSTRING(node.getName()));
00174   node.setUserData((void *) pnode);
00175   parent->add_child(pnode);
00176 
00177   // Apply the transformation elements in reverse order.
00178   LMatrix4f transform (LMatrix4f::ident_mat());
00179 
00180   daeElementRefArray &elements = node.getContents();
00181   for (size_t i = elements.getCount(); i > 0; --i) {
00182     daeElementRef &elem = elements[i - 1];
00183 
00184     switch (elem->getElementType()) {
00185       case COLLADA_TYPE::LOOKAT: {
00186         // Didn't test this, but *should* be right.
00187         domFloat3x3 &l = (daeSafeCast<domLookat>(elem))->getValue();
00188         LPoint3f eye (l[0], l[1], l[2]);
00189         LVector3f up (l[6], l[7], l[8]);
00190         LVector3f forward = LPoint3f(l[3], l[4], l[5]) - eye;
00191         forward.normalize();
00192         LVector3f side = forward.cross(up);
00193         side.normalize();
00194         up = side.cross(forward);
00195         LMatrix4f mat (LMatrix4f::ident_mat());
00196         mat.set_col(0, side);
00197         mat.set_col(1, up);
00198         mat.set_col(2, -forward);
00199         transform *= mat;
00200         transform *= LMatrix4f::translate_mat(-eye);
00201         break;
00202       }
00203       case COLLADA_TYPE::MATRIX: {
00204         domFloat4x4 &m = (daeSafeCast<domMatrix>(elem))->getValue();
00205         transform *= LMatrix4f(
00206           m[0], m[4], m[ 8], m[12],
00207           m[1], m[5], m[ 9], m[13],
00208           m[2], m[6], m[10], m[14],
00209           m[3], m[7], m[11], m[15]);
00210         break;
00211       }
00212       case COLLADA_TYPE::ROTATE: {
00213         domFloat4 &r = (daeSafeCast<domRotate>(elem))->getValue();
00214         transform *= LMatrix4f::rotate_mat(r[3], LVecBase3f(r[0], r[1], r[2]));
00215         break;
00216       }
00217       case COLLADA_TYPE::SCALE: {
00218         domFloat3 &s = (daeSafeCast<domScale>(elem))->getValue();
00219         transform *= LMatrix4f::scale_mat(s[0], s[1], s[2]);
00220         break;
00221       }
00222       case COLLADA_TYPE::SKEW:
00223         //FIXME: implement skew
00224         collada_cat.error() << "<skew> not supported yet\n";
00225         break;
00226       case COLLADA_TYPE::TRANSLATE: {
00227         domFloat3 &t = (daeSafeCast<domTranslate>(elem))->getValue();
00228         transform *= LMatrix4f::translate_mat(t[0], t[1], t[2]);
00229         break;
00230       }
00231     }
00232   }
00233   //TODO: convert coordinate systems
00234   //transform *= LMatrix4f::convert_mat(XXX, _cs);
00235 
00236   // If there's a transform, set it.
00237   if (transform != LMatrix4f::ident_mat()) {
00238     pnode->set_transform(TransformState::make_mat(transform));
00239   }
00240 
00241   // See if this node instantiates any cameras.
00242   domInstance_camera_Array &caminst = node.getInstance_camera_array();
00243   for (size_t i = 0; i < caminst.getCount(); ++i) {
00244     domCamera* target = daeSafeCast<domCamera> (caminst[i]->getUrl().getElement());
00245     load_camera(*target, pnode);
00246   }
00247 
00248   // See if this node instantiates any controllers.
00249   domInstance_controller_Array &ctrlinst = node.getInstance_controller_array();
00250   for (size_t i = 0; i < ctrlinst.getCount(); ++i) {
00251     domController* target = daeSafeCast<domController> (ctrlinst[i]->getUrl().getElement());
00252     //TODO: implement controllers.  For now, let's just read the geometry
00253     if (target->getSkin() != NULL) {
00254       domGeometry* geom = daeSafeCast<domGeometry> (target->getSkin()->getSource().getElement());
00255       //TODO
00256       //load_geometry(*geom, ctrlinst[i]->getBind_material(), pnode);
00257     }
00258   }
00259 
00260   // See if this node instantiates any geoms.
00261   domInstance_geometry_Array &ginst = node.getInstance_geometry_array();
00262   for (size_t i = 0; i < ginst.getCount(); ++i) {
00263     load_instance_geometry(*ginst[i], pnode);
00264   }
00265 
00266   // See if this node instantiates any lights.
00267   domInstance_light_Array &linst = node.getInstance_light_array();
00268   for (size_t i = 0; i < linst.getCount(); ++i) {
00269     domLight* target = daeSafeCast<domLight> (linst[i]->getUrl().getElement());
00270     load_light(*target, pnode);
00271   }
00272 
00273   // And instantiate any <instance_nodes> elements.
00274   domInstance_node_Array &ninst = node.getInstance_node_array();
00275   for (size_t i = 0; i < ninst.getCount(); ++i) {
00276     domNode* target = daeSafeCast<domNode> (ninst[i]->getUrl().getElement());
00277     load_node(*target, pnode);
00278   }
00279 
00280   // Now load in the child nodes.
00281   domNode_Array &nodes = node.getNode_array();
00282   for (size_t i = 0; i < nodes.getCount(); ++i) {
00283     load_node(*nodes[i], pnode);
00284   }
00285 
00286   // Load in any tags.
00287   domExtra_Array &extras = node.getExtra_array();
00288   for (size_t i = 0; i < extras.getCount(); ++i) {
00289     load_tags(*extras[i], pnode);
00290     //TODO: load SI_Visibility under XSI profile
00291     //TODO: support OpenSceneGraph's switch nodes
00292   }
00293 }
00294 
00295 ////////////////////////////////////////////////////////////////////
00296 //     Function: ColladaLoader::load_tags
00297 //  Description: Loads tags specified in an <extra> element.
00298 ////////////////////////////////////////////////////////////////////
00299 void ColladaLoader::
00300 load_tags(domExtra &extra, PandaNode *node) {
00301   domTechnique_Array &techniques = extra.getTechnique_array();
00302 
00303   for (size_t t = 0; t < techniques.getCount(); ++t) {
00304     if (cmp_nocase(techniques[t]->getProfile(), "PANDA3D") == 0) {
00305       const daeElementRefArray &children = techniques[t]->getChildren();
00306 
00307       for (size_t c = 0; c < children.getCount(); ++c) {
00308         daeElement &child = *children[c];
00309 
00310         if (cmp_nocase(child.getElementName(), "tag") == 0) {
00311           const string &name = child.getAttribute("name");
00312           if (name.size() > 0) {
00313             node->set_tag(name, child.getCharData());
00314           } else {
00315             collada_cat.warning() << "Ignoring <tag> without name attribute\n";
00316           }
00317         } else if (cmp_nocase(child.getElementName(), "param") == 0) {
00318           collada_cat.error() <<
00319             "Unknown <param> attribute in PANDA3D technique. "
00320             "Did you mean to use <tag> instead?\n";
00321         }
00322       }
00323     }
00324   }
00325 }
00326 
00327 ////////////////////////////////////////////////////////////////////
00328 //     Function: ColladaLoader::load_camera
00329 //  Description: Loads a COLLADA <camera> as a Camera object.
00330 ////////////////////////////////////////////////////////////////////
00331 void ColladaLoader::
00332 load_camera(domCamera &cam, PandaNode *parent) {
00333   // If we already loaded it before, instantiate the stored node.
00334   if (cam.getUserData() != NULL) {
00335     parent->add_child((PandaNode *) cam.getUserData());
00336     return;
00337   }
00338 
00339   //TODO
00340 }
00341 
00342 ////////////////////////////////////////////////////////////////////
00343 //     Function: ColladaLoader::load_instance_geometry
00344 //  Description: Loads a COLLADA <instance_geometry> as a GeomNode
00345 //               object.
00346 ////////////////////////////////////////////////////////////////////
00347 void ColladaLoader::
00348 load_instance_geometry(domInstance_geometry &inst, PandaNode *parent) {
00349   // If we already loaded it before, instantiate the stored node.
00350   if (inst.getUserData() != NULL) {
00351     parent->add_child((PandaNode *) inst.getUserData());
00352     return;
00353   }
00354 
00355   domGeometry* geom = daeSafeCast<domGeometry> (inst.getUrl().getElement());
00356   nassertv(geom != NULL);
00357 
00358   // Create the node.
00359   PT(GeomNode) gnode = new GeomNode(TOSTRING(geom->getName()));
00360   inst.setUserData((void *) gnode);
00361   parent->add_child(gnode);
00362 
00363   domBind_materialRef bind_mat = inst.getBind_material();
00364   ColladaBindMaterial cbm;
00365   if (bind_mat != NULL) {
00366     cbm.load_bind_material(*bind_mat);
00367   }
00368 
00369   load_geometry(*geom, gnode, cbm);
00370 
00371   // Load in any tags.
00372   domExtra_Array &extras = geom->getExtra_array();
00373   for (size_t i = 0; i < extras.getCount(); ++i) {
00374     load_tags(*extras[i], gnode);
00375   }
00376 }
00377 
00378 ////////////////////////////////////////////////////////////////////
00379 //     Function: ColladaLoader::load_geometry
00380 //  Description: Loads a COLLADA <geometry> and adds the primitives
00381 //               to the given GeomNode object.
00382 ////////////////////////////////////////////////////////////////////
00383 void ColladaLoader::
00384 load_geometry(domGeometry &geom, GeomNode *gnode, ColladaBindMaterial &bind_mat) {
00385   domMesh* mesh = geom.getMesh();
00386   if (mesh == NULL) {
00387     //TODO: support non-mesh geometry.
00388     return;
00389   }
00390 
00391   //TODO: support other than just triangles.
00392   domLines_Array &lines_array = mesh->getLines_array();
00393   for (size_t i = 0; i < lines_array.getCount(); ++i) {
00394     PT(ColladaPrimitive) prim = ColladaPrimitive::from_dom(*lines_array[i]);
00395     if (prim != NULL) {
00396       gnode->add_geom(prim->get_geom());
00397     }
00398   }
00399 
00400   domLinestrips_Array &linestrips_array = mesh->getLinestrips_array();
00401   for (size_t i = 0; i < linestrips_array.getCount(); ++i) {
00402     PT(ColladaPrimitive) prim = ColladaPrimitive::from_dom(*linestrips_array[i]);
00403     if (prim != NULL) {
00404       gnode->add_geom(prim->get_geom());
00405     }
00406   }
00407 
00408   domPolygons_Array &polygons_array = mesh->getPolygons_array();
00409   for (size_t i = 0; i < polygons_array.getCount(); ++i) {
00410     PT(ColladaPrimitive) prim = ColladaPrimitive::from_dom(*polygons_array[i]);
00411     if (prim != NULL) {
00412       gnode->add_geom(prim->get_geom());
00413     }
00414   }
00415 
00416   domPolylist_Array &polylist_array = mesh->getPolylist_array();
00417   for (size_t i = 0; i < polylist_array.getCount(); ++i) {
00418     PT(ColladaPrimitive) prim = ColladaPrimitive::from_dom(*polylist_array[i]);
00419     if (prim != NULL) {
00420       gnode->add_geom(prim->get_geom());
00421     }
00422   }
00423 
00424   domTriangles_Array &triangles_array = mesh->getTriangles_array();
00425   for (size_t i = 0; i < triangles_array.getCount(); ++i) {
00426     PT(ColladaPrimitive) prim = ColladaPrimitive::from_dom(*triangles_array[i]);
00427     if (prim != NULL) {
00428       gnode->add_geom(prim->get_geom());
00429     }
00430   }
00431 
00432   domTrifans_Array &trifans_array = mesh->getTrifans_array();
00433   for (size_t i = 0; i < trifans_array.getCount(); ++i) {
00434     PT(ColladaPrimitive) prim = ColladaPrimitive::from_dom(*trifans_array[i]);
00435     if (prim != NULL) {
00436       gnode->add_geom(prim->get_geom());
00437     }
00438   }
00439 
00440   domTristrips_Array &tristrips_array = mesh->getTristrips_array();
00441   for (size_t i = 0; i < tristrips_array.getCount(); ++i) {
00442     PT(ColladaPrimitive) prim = ColladaPrimitive::from_dom(*tristrips_array[i]);
00443     if (prim != NULL) {
00444       gnode->add_geom(prim->get_geom());
00445     }
00446   }
00447 }
00448 
00449 ////////////////////////////////////////////////////////////////////
00450 //     Function: ColladaLoader::load_light
00451 //  Description: Loads a COLLADA <light> as a LightNode object.
00452 ////////////////////////////////////////////////////////////////////
00453 void ColladaLoader::
00454 load_light(domLight &light, PandaNode *parent) {
00455   // If we already loaded it before, instantiate the stored node.
00456   if (light.getUserData() != NULL) {
00457     parent->add_child((PandaNode *) light.getUserData());
00458     return;
00459   }
00460 
00461   PT(LightNode) lnode;
00462   domLight::domTechnique_common &tc = *light.getTechnique_common();
00463 
00464   // Check for an ambient light.
00465   domLight::domTechnique_common::domAmbientRef ambient = tc.getAmbient();
00466   if (ambient != NULL) {
00467     PT(AmbientLight) alight = new AmbientLight(TOSTRING(light.getName()));
00468     lnode = DCAST(LightNode, alight);
00469 
00470     domFloat3 &color = ambient->getColor()->getValue();
00471     alight->set_color(LColor(color[0], color[1], color[2], 1.0));
00472   }
00473 
00474   // Check for a directional light.
00475   domLight::domTechnique_common::domDirectionalRef directional = tc.getDirectional();
00476   if (directional != NULL) {
00477     PT(DirectionalLight) dlight = new DirectionalLight(TOSTRING(light.getName()));
00478     lnode = DCAST(LightNode, dlight);
00479 
00480     domFloat3 &color = directional->getColor()->getValue();
00481     dlight->set_color(LColor(color[0], color[1], color[2], 1.0));
00482     dlight->set_direction(LVector3f(0, 0, -1));
00483   }
00484 
00485   // Check for a point light.
00486   domLight::domTechnique_common::domPointRef point = tc.getPoint();
00487   if (point != NULL) {
00488     PT(PointLight) plight = new PointLight(TOSTRING(light.getName()));
00489     lnode = DCAST(LightNode, plight);
00490 
00491     domFloat3 &color = point->getColor()->getValue();
00492     plight->set_color(LColor(color[0], color[1], color[2], 1.0));
00493 
00494     LVecBase3f atten (1.0f, 0.0f, 0.0f);
00495     domTargetable_floatRef fval = point->getConstant_attenuation();
00496     if (fval != NULL) {
00497       atten[0] = fval->getValue();
00498     }
00499     fval = point->getLinear_attenuation();
00500     if (fval != NULL) {
00501       atten[1] = fval->getValue();
00502     }
00503     fval = point->getQuadratic_attenuation();
00504     if (fval != NULL) {
00505       atten[2] = fval->getValue();
00506     }
00507 
00508     plight->set_attenuation(atten);
00509   }
00510 
00511   // Check for a spot light.
00512   domLight::domTechnique_common::domSpotRef spot = tc.getSpot();
00513   if (spot != NULL) {
00514     PT(Spotlight) slight = new Spotlight(TOSTRING(light.getName()));
00515     lnode = DCAST(LightNode, slight);
00516 
00517     domFloat3 &color = spot->getColor()->getValue();
00518     slight->set_color(LColor(color[0], color[1], color[2], 1.0));
00519 
00520     LVecBase3f atten (1.0f, 0.0f, 0.0f);
00521     domTargetable_floatRef fval = spot->getConstant_attenuation();
00522     if (fval != NULL) {
00523       atten[0] = fval->getValue();
00524     }
00525     fval = spot->getLinear_attenuation();
00526     if (fval != NULL) {
00527       atten[1] = fval->getValue();
00528     }
00529     fval = spot->getQuadratic_attenuation();
00530     if (fval != NULL) {
00531       atten[2] = fval->getValue();
00532     }
00533 
00534     slight->set_attenuation(atten);
00535 
00536     fval = spot->getFalloff_angle();
00537     if (fval != NULL) {
00538       slight->get_lens()->set_fov(fval->getValue());
00539     } else {
00540       slight->get_lens()->set_fov(180.0f);
00541     }
00542 
00543     fval = spot->getFalloff_exponent();
00544     if (fval != NULL) {
00545       slight->set_exponent(fval->getValue());
00546     } else {
00547       slight->set_exponent(0.0f);
00548     }
00549   }
00550 
00551   if (lnode == NULL) {
00552     return;
00553   }
00554   parent->add_child(lnode);
00555   _lights.push_back(lnode);
00556   light.setUserData((void*) lnode);
00557 
00558   // Load in any tags.
00559   domExtra_Array &extras = light.getExtra_array();
00560   for (size_t i = 0; i < extras.getCount(); ++i) {
00561     load_tags(*extras[i], lnode);
00562   }
00563 }
 All Classes Functions Variables Enumerations