00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
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
00043
00044
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
00057
00058
00059
00060 AssimpLoader::
00061 ~AssimpLoader() {
00062 _importer.FreeScene();
00063 }
00064
00065
00066
00067
00068
00069
00070
00071 void AssimpLoader::
00072 get_extensions(string &ext) const {
00073 aiString aexts;
00074 _importer.GetExtensionList(aexts);
00075
00076
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
00090
00091
00092
00093 bool AssimpLoader::
00094 read(const Filename &filename) {
00095 _filename = filename;
00096
00097
00098
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
00111
00112
00113
00114
00115 void AssimpLoader::
00116 build_graph() {
00117 nassertv(_scene != NULL);
00118 nassertv(!_error);
00119
00120
00121 MutexHolder holder(_lock);
00122
00123 _root = new ModelRoot(_filename.get_basename());
00124
00125
00126 _textures = new PT(Texture)[_scene->mNumTextures];
00127 for (size_t i = 0; i < _scene->mNumTextures; ++i) {
00128 load_texture(i);
00129 }
00130
00131
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
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
00145 if (_scene->mRootNode != NULL) {
00146 load_node(*_scene->mRootNode, _root);
00147 }
00148
00149
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
00162
00163
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
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
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
00217
00218
00219
00220 _textures[index] = ptex;
00221
00222 }
00223
00224
00225
00226
00227
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
00243
00244
00245
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
00258
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
00267 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00268 Filename dir (_filename);
00269 _filename.make_canonical();
00270 dir = _filename.get_dirname();
00271
00272
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
00301
00302
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
00316
00317
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
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
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
00361
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
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
00383
00384
00385
00386 void AssimpLoader::
00387 load_mesh(size_t index) {
00388 const aiMesh &mesh = *_scene->mMeshes[index];
00389
00390
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
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
00410
00411 PT(GeomVertexFormat) format = new GeomVertexFormat;
00412 format->add_array(aformat);
00413
00414
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
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
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
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
00445 if (num_uvs > 0) {
00446
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
00464
00465
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
00471 for (size_t i = 0; i < mesh.mNumFaces; ++i) {
00472 const aiFace &face = mesh.mFaces[i];
00473
00474 if (face.mNumIndices == 0) {
00475
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
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
00509
00510
00511
00512 void AssimpLoader::
00513 load_node(const aiNode &node, PandaNode *parent) {
00514 PT(PandaNode) pnode;
00515
00516
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
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
00541 PT(GeomNode) gnode = DCAST(GeomNode, pnode);
00542 size_t meshIndex;
00543
00544
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
00562
00563
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
00618
00619
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
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 }