Panda3D

assimpLoader.cxx

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 }
 All Classes Functions Variables Enumerations