Panda3D
|
00001 // Filename: assimpLoader.cxx 00002 // Created by: rdb (29Mar11) 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 "assimpLoader.h" 00016 00017 #include "geomNode.h" 00018 #include "luse.h" 00019 #include "geomVertexWriter.h" 00020 #include "geomPoints.h" 00021 #include "geomLines.h" 00022 #include "geomTriangles.h" 00023 #include "pnmFileTypeRegistry.h" 00024 #include "pnmImage.h" 00025 #include "materialAttrib.h" 00026 #include "textureAttrib.h" 00027 #include "cullFaceAttrib.h" 00028 #include "lightNode.h" 00029 #include "ambientLight.h" 00030 #include "directionalLight.h" 00031 #include "spotlight.h" 00032 #include "pointLight.h" 00033 #include "look_at.h" 00034 #include "texturePool.h" 00035 00036 #include "pandaIOSystem.h" 00037 #include "pandaLogger.h" 00038 00039 #include "aiPostProcess.h" 00040 00041 //////////////////////////////////////////////////////////////////// 00042 // Function: AssimpLoader::Constructor 00043 // Access: Public 00044 // Description: 00045 //////////////////////////////////////////////////////////////////// 00046 AssimpLoader:: 00047 AssimpLoader() : 00048 _error (false), 00049 _geoms (NULL) { 00050 00051 PandaLogger::set_default(); 00052 _importer.SetIOHandler(new PandaIOSystem); 00053 } 00054 00055 //////////////////////////////////////////////////////////////////// 00056 // Function: AssimpLoader::Destructor 00057 // Access: Public 00058 // Description: 00059 //////////////////////////////////////////////////////////////////// 00060 AssimpLoader:: 00061 ~AssimpLoader() { 00062 _importer.FreeScene(); 00063 } 00064 00065 //////////////////////////////////////////////////////////////////// 00066 // Function: AssimpLoader::get_extensions 00067 // Access: Public 00068 // Description: Returns a space-separated list of extensions that 00069 // Assimp can load, without the leading dots. 00070 //////////////////////////////////////////////////////////////////// 00071 void AssimpLoader:: 00072 get_extensions(string &ext) const { 00073 aiString aexts; 00074 _importer.GetExtensionList(aexts); 00075 00076 // The format is like: *.mdc;*.mdl;*.mesh.xml;*.mot 00077 char *sub = strtok(aexts.data, ";"); 00078 while (sub != NULL) { 00079 ext += sub + 2; 00080 sub = strtok(NULL, ";"); 00081 00082 if (sub != NULL) { 00083 ext += ' '; 00084 } 00085 } 00086 } 00087 00088 //////////////////////////////////////////////////////////////////// 00089 // Function: AssimpLoader::read 00090 // Access: Public 00091 // Description: Reads from the indicated file. 00092 //////////////////////////////////////////////////////////////////// 00093 bool AssimpLoader:: 00094 read(const Filename &filename) { 00095 _filename = filename; 00096 00097 // I really don't know why we need to flip the winding order, 00098 // but otherwise the models I tested with are showing inside out. 00099 _scene = _importer.ReadFile(_filename.c_str(), aiProcess_Triangulate | aiProcess_GenUVCoords | aiProcess_FlipWindingOrder); 00100 if (_scene == NULL) { 00101 _error = true; 00102 return false; 00103 } 00104 00105 _error = false; 00106 return true; 00107 } 00108 00109 //////////////////////////////////////////////////////////////////// 00110 // Function: AssimpLoader::build_graph 00111 // Access: Public 00112 // Description: Converts scene graph structures into a Panda3D 00113 // scene graph, with _root being the root node. 00114 //////////////////////////////////////////////////////////////////// 00115 void AssimpLoader:: 00116 build_graph() { 00117 nassertv(_scene != NULL); // read() must be called first 00118 nassertv(!_error); // and have succeeded 00119 00120 // Protect the import process 00121 MutexHolder holder(_lock); 00122 00123 _root = new ModelRoot(_filename.get_basename()); 00124 00125 // Import all of the embedded textures first. 00126 _textures = new PT(Texture)[_scene->mNumTextures]; 00127 for (size_t i = 0; i < _scene->mNumTextures; ++i) { 00128 load_texture(i); 00129 } 00130 00131 // Then the materials. 00132 _mat_states = new CPT(RenderState)[_scene->mNumMaterials]; 00133 for (size_t i = 0; i < _scene->mNumMaterials; ++i) { 00134 load_material(i); 00135 } 00136 00137 // And then the meshes. 00138 _geoms = new PT(Geom)[_scene->mNumMeshes]; 00139 _geom_matindices = new unsigned int[_scene->mNumMeshes]; 00140 for (size_t i = 0; i < _scene->mNumMeshes; ++i) { 00141 load_mesh(i); 00142 } 00143 00144 // And now the node structure. 00145 if (_scene->mRootNode != NULL) { 00146 load_node(*_scene->mRootNode, _root); 00147 } 00148 00149 // And lastly, the lights. 00150 for (size_t i = 0; i < _scene->mNumLights; ++i) { 00151 load_light(*_scene->mLights[i]); 00152 } 00153 00154 delete[] _textures; 00155 delete[] _mat_states; 00156 delete[] _geoms; 00157 delete[] _geom_matindices; 00158 } 00159 00160 //////////////////////////////////////////////////////////////////// 00161 // Function: AssimpLoader::load_texture 00162 // Access: Private 00163 // Description: Converts an aiTexture into a Texture. 00164 //////////////////////////////////////////////////////////////////// 00165 void AssimpLoader:: 00166 load_texture(size_t index) { 00167 const aiTexture &tex = *_scene->mTextures[index]; 00168 00169 PT(Texture) ptex = new Texture; 00170 00171 if (tex.mHeight == 0) { 00172 // Compressed texture. 00173 assimp_cat.debug() 00174 << "Reading embedded compressed texture with format " << tex.achFormatHint << " and size " << tex.mWidth << "\n"; 00175 stringstream str; 00176 str.write((char*) tex.pcData, tex.mWidth); 00177 00178 if (strncmp(tex.achFormatHint, "dds", 3) == 0) { 00179 ptex->read_dds(str); 00180 00181 } else { 00182 const PNMFileTypeRegistry *reg = PNMFileTypeRegistry::get_global_ptr(); 00183 PNMFileType *ftype; 00184 PNMImage img; 00185 00186 // Work around a bug in Assimp, it sometimes writes jp instead of jpg 00187 if (strncmp(tex.achFormatHint, "jp\0", 3) == 0) { 00188 ftype = reg->get_type_from_extension("jpg"); 00189 } else { 00190 ftype = reg->get_type_from_extension(tex.achFormatHint); 00191 } 00192 00193 if (img.read(str, "", ftype)) { 00194 ptex->load(img); 00195 } else { 00196 ptex = NULL; 00197 } 00198 } 00199 } else { 00200 assimp_cat.debug() 00201 << "Reading embedded raw texture with size " << tex.mWidth << "x" << tex.mHeight << "\n"; 00202 00203 ptex->setup_2d_texture(tex.mWidth, tex.mHeight, Texture::T_unsigned_byte, Texture::F_rgba); 00204 PTA_uchar data = ptex->modify_ram_image(); 00205 00206 size_t p = 0; 00207 for (size_t i = 0; i < tex.mWidth * tex.mHeight; ++i) { 00208 const aiTexel &texel = tex.pcData[i]; 00209 data[p++] = texel.b; 00210 data[p++] = texel.g; 00211 data[p++] = texel.r; 00212 data[p++] = texel.a; 00213 } 00214 } 00215 00216 //ostringstream path; 00217 //path << "/tmp/" << index << ".png"; 00218 //ptex->write(path.str()); 00219 00220 _textures[index] = ptex; 00221 00222 } 00223 00224 //////////////////////////////////////////////////////////////////// 00225 // Function: AssimpLoader::load_texture_stage 00226 // Access: Private 00227 // Description: Converts an aiMaterial into a RenderState. 00228 //////////////////////////////////////////////////////////////////// 00229 void AssimpLoader:: 00230 load_texture_stage(const aiMaterial &mat, const aiTextureType &ttype, CPT(TextureAttrib) &tattr) { 00231 aiString path; 00232 aiTextureMapping mapping; 00233 unsigned int uvindex; 00234 PN_stdfloat blend; 00235 aiTextureOp op; 00236 aiTextureMapMode mapmode; 00237 00238 for (size_t i = 0; i < mat.GetTextureCount(ttype); ++i) { 00239 mat.GetTexture(ttype, i, &path, &mapping, NULL, &blend, &op, &mapmode); 00240 00241 if (AI_SUCCESS != mat.Get(AI_MATKEY_UVWSRC(ttype, i), uvindex)) { 00242 // If there's no texture coordinate set for this texture, 00243 // assume that it's the same as the index on the stack. 00244 //TODO: if there's only one set on the mesh, 00245 // force everything to use just the first stage. 00246 uvindex = i; 00247 } 00248 00249 stringstream str; 00250 str << uvindex; 00251 PT(TextureStage) stage = new TextureStage(str.str()); 00252 if (uvindex > 0) { 00253 stage->set_texcoord_name(InternalName::get_texcoord_name(str.str())); 00254 } 00255 PT(Texture) ptex = NULL; 00256 00257 // I'm not sure if this is the right way to handle it, as 00258 // I couldn't find much information on embedded textures. 00259 if (path.data[0] == '*') { 00260 long num = strtol(path.data + 1, NULL, 10); 00261 ptex = _textures[num]; 00262 00263 } else if (path.length > 0) { 00264 Filename fn = Filename::from_os_specific(string(path.data, path.length)); 00265 00266 // Try to find the file by moving up twice in the hierarchy. 00267 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); 00268 Filename dir (_filename); 00269 _filename.make_canonical(); 00270 dir = _filename.get_dirname(); 00271 00272 // Quake 3 BSP doesn't specify an extension for textures. 00273 if (vfs->is_regular_file(Filename(dir, fn))) { 00274 fn = Filename(dir, fn); 00275 } else if (vfs->is_regular_file(Filename(dir, fn + ".tga"))) { 00276 fn = Filename(dir, fn + ".tga"); 00277 } else if (vfs->is_regular_file(Filename(dir, fn + ".jpg"))) { 00278 fn = Filename(dir, fn + ".jpg"); 00279 } else { 00280 dir = _filename.get_dirname(); 00281 if (vfs->is_regular_file(Filename(dir, fn))) { 00282 fn = Filename(dir, fn); 00283 } else if (vfs->is_regular_file(Filename(dir, fn + ".tga"))) { 00284 fn = Filename(dir, fn + ".tga"); 00285 } else if (vfs->is_regular_file(Filename(dir, fn + ".jpg"))) { 00286 fn = Filename(dir, fn + ".jpg"); 00287 } 00288 } 00289 00290 ptex = TexturePool::load_texture(fn); 00291 } 00292 00293 if (ptex != NULL) { 00294 tattr = DCAST(TextureAttrib, tattr->add_on_stage(stage, ptex)); 00295 } 00296 } 00297 } 00298 00299 //////////////////////////////////////////////////////////////////// 00300 // Function: AssimpLoader::load_material 00301 // Access: Private 00302 // Description: Converts an aiMaterial into a RenderState. 00303 //////////////////////////////////////////////////////////////////// 00304 void AssimpLoader:: 00305 load_material(size_t index) { 00306 const aiMaterial &mat = *_scene->mMaterials[index]; 00307 00308 CPT(RenderState) state = RenderState::make_empty(); 00309 00310 aiColor3D col; 00311 bool have; 00312 int ival; 00313 PN_stdfloat fval; 00314 00315 // XXX a lot of this is untested. 00316 00317 // First do the material attribute. 00318 PT(Material) pmat = new Material; 00319 have = false; 00320 if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_DIFFUSE, col)) { 00321 pmat->set_diffuse(LColor(col.r, col.g, col.b, 1)); 00322 have = true; 00323 } 00324 if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_SPECULAR, col)) { 00325 if (AI_SUCCESS == mat.Get(AI_MATKEY_SHININESS_STRENGTH, fval)) { 00326 pmat->set_specular(LColor(col.r * fval, col.g * fval, col.b * fval, 1)); 00327 } else { 00328 pmat->set_specular(LColor(col.r, col.g, col.b, 1)); 00329 } 00330 have = true; 00331 } 00332 if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_AMBIENT, col)) { 00333 pmat->set_specular(LColor(col.r, col.g, col.b, 1)); 00334 have = true; 00335 } 00336 if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_EMISSIVE, col)) { 00337 pmat->set_emission(LColor(col.r, col.g, col.b, 1)); 00338 have = true; 00339 } 00340 if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_TRANSPARENT, col)) { 00341 //FIXME: ??? 00342 } 00343 if (AI_SUCCESS == mat.Get(AI_MATKEY_SHININESS, fval)) { 00344 pmat->set_shininess(fval); 00345 have = true; 00346 } 00347 if (have) { 00348 state = state->add_attrib(MaterialAttrib::make(pmat)); 00349 } 00350 00351 // Wireframe. 00352 if (AI_SUCCESS == mat.Get(AI_MATKEY_ENABLE_WIREFRAME, ival)) { 00353 if (ival) { 00354 state = state->add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_wireframe)); 00355 } else { 00356 state = state->add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_filled)); 00357 } 00358 } 00359 00360 // Backface culling. Not sure if this is also supposed to 00361 // set the twoside flag in the material, I'm guessing not. 00362 if (AI_SUCCESS == mat.Get(AI_MATKEY_TWOSIDED, ival)) { 00363 if (ival) { 00364 state = state->add_attrib(CullFaceAttrib::make(CullFaceAttrib::M_cull_none)); 00365 } else { 00366 state = state->add_attrib(CullFaceAttrib::make_default()); 00367 } 00368 } 00369 00370 // And let's not forget the textures! 00371 CPT(TextureAttrib) tattr = DCAST(TextureAttrib, TextureAttrib::make()); 00372 load_texture_stage(mat, aiTextureType_DIFFUSE, tattr); 00373 load_texture_stage(mat, aiTextureType_LIGHTMAP, tattr); 00374 if (tattr->get_num_on_stages() > 0) { 00375 state = state->add_attrib(tattr); 00376 } 00377 00378 _mat_states[index] = state; 00379 } 00380 00381 //////////////////////////////////////////////////////////////////// 00382 // Function: AssimpLoader::load_mesh 00383 // Access: Private 00384 // Description: Converts an aiMesh into a Geom. 00385 //////////////////////////////////////////////////////////////////// 00386 void AssimpLoader:: 00387 load_mesh(size_t index) { 00388 const aiMesh &mesh = *_scene->mMeshes[index]; 00389 00390 // Create the vertex format. 00391 PT(GeomVertexArrayFormat) aformat = new GeomVertexArrayFormat; 00392 aformat->add_column(InternalName::get_vertex(), 3, Geom::NT_stdfloat, Geom::C_point); 00393 if (mesh.HasNormals()) { 00394 aformat->add_column(InternalName::get_normal(), 3, Geom::NT_stdfloat, Geom::C_vector); 00395 } 00396 if (mesh.HasVertexColors(0)) { 00397 aformat->add_column(InternalName::get_color(), 4, Geom::NT_stdfloat, Geom::C_color); 00398 } 00399 unsigned int num_uvs = mesh.GetNumUVChannels(); 00400 if (num_uvs > 0) { 00401 // UV sets are named texcoord, texcoord.1, texcoord.2... 00402 aformat->add_column(InternalName::get_texcoord(), 3, Geom::NT_stdfloat, Geom::C_texcoord); 00403 for (unsigned int u = 1; u < num_uvs; ++u) { 00404 ostringstream out; 00405 out << u; 00406 aformat->add_column(InternalName::get_texcoord_name(out.str()), 3, Geom::NT_stdfloat, Geom::C_texcoord); 00407 } 00408 } 00409 //TODO: if there is only one UV set, hackily iterate over the texture stages and clear the texcoord name things 00410 00411 PT(GeomVertexFormat) format = new GeomVertexFormat; 00412 format->add_array(aformat); 00413 00414 // Create the GeomVertexData. 00415 string name (mesh.mName.data, mesh.mName.length); 00416 PT(GeomVertexData) vdata = new GeomVertexData(name, GeomVertexFormat::register_format(format), Geom::UH_static); 00417 vdata->unclean_set_num_rows(mesh.mNumVertices); 00418 00419 // Read out the vertices. 00420 GeomVertexWriter vertex (vdata, InternalName::get_vertex()); 00421 for (size_t i = 0; i < mesh.mNumVertices; ++i) { 00422 const aiVector3D &vec = mesh.mVertices[i]; 00423 vertex.add_data3(vec.x, vec.y, vec.z); 00424 } 00425 00426 // Now the normals, if any. 00427 if (mesh.HasNormals()) { 00428 GeomVertexWriter normal (vdata, InternalName::get_normal()); 00429 for (size_t i = 0; i < mesh.mNumVertices; ++i) { 00430 const aiVector3D &vec = mesh.mNormals[i]; 00431 normal.add_data3(vec.x, vec.y, vec.z); 00432 } 00433 } 00434 00435 // Vertex colors, if any. We only import the first set. 00436 if (mesh.HasVertexColors(0)) { 00437 GeomVertexWriter color (vdata, InternalName::get_color()); 00438 for (size_t i = 0; i < mesh.mNumVertices; ++i) { 00439 const aiColor4D &col = mesh.mColors[0][i]; 00440 color.add_data4(col.r, col.g, col.b, col.a); 00441 } 00442 } 00443 00444 // Now the texture coordinates. 00445 if (num_uvs > 0) { 00446 // UV sets are named texcoord, texcoord.1, texcoord.2... 00447 GeomVertexWriter texcoord0 (vdata, InternalName::get_texcoord()); 00448 for (size_t i = 0; i < mesh.mNumVertices; ++i) { 00449 const aiVector3D &vec = mesh.mTextureCoords[0][i]; 00450 texcoord0.add_data3(vec.x, vec.y, vec.z); 00451 } 00452 for (unsigned int u = 1; u < num_uvs; ++u) { 00453 ostringstream out; 00454 out << u; 00455 GeomVertexWriter texcoord (vdata, InternalName::get_texcoord_name(out.str())); 00456 for (size_t i = 0; i < mesh.mNumVertices; ++i) { 00457 const aiVector3D &vec = mesh.mTextureCoords[u][i]; 00458 texcoord.add_data3(vec.x, vec.y, vec.z); 00459 } 00460 } 00461 } 00462 00463 // Now read out the primitives. 00464 // Keep in mind that we called ReadFile with the aiProcess_Triangulate 00465 // flag earlier, so we don't have to worry about polygons. 00466 PT(GeomPoints) points = new GeomPoints(Geom::UH_static); 00467 PT(GeomLines) lines = new GeomLines(Geom::UH_static); 00468 PT(GeomTriangles) triangles = new GeomTriangles(Geom::UH_static); 00469 00470 // Now add the vertex indices. 00471 for (size_t i = 0; i < mesh.mNumFaces; ++i) { 00472 const aiFace &face = mesh.mFaces[i]; 00473 00474 if (face.mNumIndices == 0) { 00475 // It happens, strangely enough. 00476 continue; 00477 } else if (face.mNumIndices == 1) { 00478 points->add_vertex(face.mIndices[0]); 00479 points->close_primitive(); 00480 } else if (face.mNumIndices == 2) { 00481 lines->add_vertices(face.mIndices[0], face.mIndices[1]); 00482 lines->close_primitive(); 00483 } else if (face.mNumIndices == 3) { 00484 triangles->add_vertices(face.mIndices[0], face.mIndices[1], face.mIndices[2]); 00485 triangles->close_primitive(); 00486 } else { 00487 nassertd(false) continue; 00488 } 00489 } 00490 00491 // Create a geom and add the primitives to it. 00492 PT(Geom) geom = new Geom(vdata); 00493 if (points->get_num_primitives() > 0) { 00494 geom->add_primitive(points); 00495 } 00496 if (lines->get_num_primitives() > 0) { 00497 geom->add_primitive(lines); 00498 } 00499 if (triangles->get_num_primitives() > 0) { 00500 geom->add_primitive(triangles); 00501 } 00502 00503 _geoms[index] = geom; 00504 _geom_matindices[index] = mesh.mMaterialIndex; 00505 } 00506 00507 //////////////////////////////////////////////////////////////////// 00508 // Function: AssimpLoader::load_node 00509 // Access: Private 00510 // Description: Converts an aiNode into a PandaNode. 00511 //////////////////////////////////////////////////////////////////// 00512 void AssimpLoader:: 00513 load_node(const aiNode &node, PandaNode *parent) { 00514 PT(PandaNode) pnode; 00515 00516 // Create the node and give it a name. 00517 string name (node.mName.data, node.mName.length); 00518 if (node.mNumMeshes > 0) { 00519 pnode = new GeomNode(name); 00520 } else { 00521 pnode = new PandaNode(name); 00522 } 00523 parent->add_child(pnode); 00524 00525 // Load in the transformation matrix. 00526 const aiMatrix4x4 &t = node.mTransformation; 00527 if (!t.IsIdentity()) { 00528 LMatrix4 mat(t.a1, t.b1, t.c1, t.d1, 00529 t.a2, t.b2, t.c2, t.d2, 00530 t.a3, t.b3, t.c3, t.d3, 00531 t.a4, t.b4, t.c4, t.d4); 00532 pnode->set_transform(TransformState::make_mat(mat)); 00533 } 00534 00535 for (size_t i = 0; i < node.mNumChildren; ++i) { 00536 load_node(*node.mChildren[i], pnode); 00537 } 00538 00539 if (node.mNumMeshes > 0) { 00540 // Remember, we created this as GeomNode earlier. 00541 PT(GeomNode) gnode = DCAST(GeomNode, pnode); 00542 size_t meshIndex; 00543 00544 // If there's only mesh, don't bother using a per-geom state. 00545 if (node.mNumMeshes == 1) { 00546 meshIndex = node.mMeshes[0]; 00547 gnode->add_geom(_geoms[meshIndex]); 00548 gnode->set_state(_mat_states[_geom_matindices[meshIndex]]); 00549 00550 } else { 00551 for (size_t i = 0; i < node.mNumMeshes; ++i) { 00552 meshIndex = node.mMeshes[i]; 00553 gnode->add_geom(_geoms[node.mMeshes[i]], 00554 _mat_states[_geom_matindices[meshIndex]]); 00555 } 00556 } 00557 } 00558 } 00559 00560 //////////////////////////////////////////////////////////////////// 00561 // Function: AssimpLoader::load_light 00562 // Access: Private 00563 // Description: Converts an aiLight into a LightNode. 00564 //////////////////////////////////////////////////////////////////// 00565 void AssimpLoader:: 00566 load_light(const aiLight &light) { 00567 string name (light.mName.data, light.mName.length); 00568 assimp_cat.debug() << "Found light '" << name << "'\n"; 00569 00570 aiColor3D col; 00571 aiVector3D vec; 00572 00573 PT(LightNode) lnode; 00574 00575 switch (light.mType) { 00576 case aiLightSource_DIRECTIONAL: { 00577 PT(DirectionalLight) dlight = new DirectionalLight(name); 00578 lnode = DCAST(LightNode, dlight); 00579 00580 col = light.mColorSpecular; 00581 dlight->set_specular_color(LColor(col.r, col.g, col.b, 1)); 00582 00583 vec = light.mPosition; 00584 dlight->set_point(LPoint3(vec.x, vec.y, vec.z)); 00585 00586 vec = light.mDirection; 00587 dlight->set_direction(LVector3(vec.x, vec.y, vec.z)); 00588 break; } 00589 00590 case aiLightSource_POINT: { 00591 PT(PointLight) plight = new PointLight(name); 00592 lnode = DCAST(LightNode, plight); 00593 00594 col = light.mColorSpecular; 00595 plight->set_specular_color(LColor(col.r, col.g, col.b, 1)); 00596 00597 vec = light.mPosition; 00598 plight->set_point(LPoint3(vec.x, vec.y, vec.z)); 00599 00600 plight->set_attenuation(LVecBase3(light.mAttenuationConstant, 00601 light.mAttenuationLinear, 00602 light.mAttenuationQuadratic)); 00603 break; } 00604 00605 case aiLightSource_SPOT: { 00606 PT(Spotlight) plight = new Spotlight(name); 00607 lnode = DCAST(LightNode, plight); 00608 00609 col = light.mColorSpecular; 00610 plight->set_specular_color(LColor(col.r, col.g, col.b, 1)); 00611 00612 plight->set_attenuation(LVecBase3(light.mAttenuationConstant, 00613 light.mAttenuationLinear, 00614 light.mAttenuationQuadratic)); 00615 00616 plight->get_lens()->set_fov(light.mAngleOuterCone); 00617 //TODO: translate mAngleInnerCone to an exponent, somehow 00618 00619 // This *should* be about right. 00620 vec = light.mDirection; 00621 LPoint3 pos (light.mPosition.x, light.mPosition.y, light.mPosition.z); 00622 LQuaternion quat; 00623 ::look_at(quat, LPoint3(vec.x, vec.y, vec.z), LVector3::up()); 00624 plight->set_transform(TransformState::make_pos_quat_scale(pos, quat, LVecBase3(1, 1, 1))); 00625 break; } 00626 00627 default: 00628 assimp_cat.warning() << "Light '" << name << "' has an unknown type!\n"; 00629 return; 00630 } 00631 00632 // If there's an ambient color, add it as ambient light. 00633 LVecBase4 ambient (col.r, col.g, col.b, 0); 00634 if (ambient != LVecBase4::zero()) { 00635 PT(AmbientLight) alight = new AmbientLight(name); 00636 col = light.mColorAmbient; 00637 alight->set_color(ambient); 00638 _root->add_child(alight); 00639 } 00640 00641 _root->add_child(lnode); 00642 col = light.mColorDiffuse; 00643 lnode->set_color(LColor(col.r, col.g, col.b, 1)); 00644 }