Panda3D
|
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 }