47#include <assimp/postprocess.h>
49#ifndef AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR
50#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0
53#ifndef AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR
54#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0
57#ifndef AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR
58#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0
61#ifndef AI_MATKEY_GLTF_ALPHAMODE
62#define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0
65#ifndef AI_MATKEY_GLTF_ALPHACUTOFF
66#define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0
70#ifndef AI_MATKEY_BASE_COLOR
71#define AI_MATKEY_BASE_COLOR AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR
74#ifndef AI_MATKEY_METALLIC_FACTOR
75#define AI_MATKEY_METALLIC_FACTOR AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR
78#ifndef AI_MATKEY_ROUGHNESS_FACTOR
79#define AI_MATKEY_ROUGHNESS_FACTOR AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR
82using std::ostringstream;
83using std::stringstream;
91 : joint_vertex_xform(joint_vertex_xform), weight(weight)
113 _importer.FreeScene();
123 _importer.GetExtensionList(aexts);
126 char *sub = strtok(aexts.data,
";");
127 while (sub !=
nullptr) {
129 sub = strtok(
nullptr,
";");
131 if (sub !=
nullptr) {
142 _filename = filename;
144 unsigned int flags = aiProcess_Triangulate | aiProcess_GenUVCoords;
146 if (assimp_calc_tangent_space) {
147 flags |= aiProcess_CalcTangentSpace;
149 if (assimp_join_identical_vertices) {
150 flags |= aiProcess_JoinIdenticalVertices;
152 if (assimp_improve_cache_locality) {
153 flags |= aiProcess_ImproveCacheLocality;
155 if (assimp_remove_redundant_materials) {
156 flags |= aiProcess_RemoveRedundantMaterials;
158 if (assimp_fix_infacing_normals) {
159 flags |= aiProcess_FixInfacingNormals;
161 if (assimp_optimize_meshes) {
162 flags |= aiProcess_OptimizeMeshes;
164 if (assimp_optimize_graph) {
165 flags |= aiProcess_OptimizeGraph;
167 if (assimp_flip_winding_order) {
168 flags |= aiProcess_FlipWindingOrder;
170 if (assimp_gen_normals) {
171 if (assimp_smooth_normal_angle == 0.0) {
172 flags |= aiProcess_GenNormals;
175 flags |= aiProcess_GenSmoothNormals;
176 _importer.SetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,
177 assimp_smooth_normal_angle);
181 _scene = _importer.ReadFile(_filename.c_str(), flags);
182 if (_scene ==
nullptr) {
197 nassertv(_scene !=
nullptr);
206 _textures =
new PT(
Texture)[_scene->mNumTextures];
207 for (
size_t i = 0; i < _scene->mNumTextures; ++i) {
212 _mat_states =
new CPT(
RenderState)[_scene->mNumMaterials];
213 for (
size_t i = 0; i < _scene->mNumMaterials; ++i) {
218 _geoms =
new Geoms[_scene->mNumMeshes];
219 for (
size_t i = 0; i < _scene->mNumMeshes; ++i) {
224 if (_scene->mRootNode !=
nullptr) {
225 load_node(*_scene->mRootNode, _root);
229 for (
size_t i = 0; i < _scene->mNumLights; ++i) {
230 load_light(*_scene->mLights[i]);
234 delete[] _mat_states;
241const aiNode *AssimpLoader::
242find_node(
const aiNode &root,
const aiString &name) {
245 if (root.mName == name) {
248 for (
size_t i = 0; i < root.mNumChildren; ++i) {
249 node = find_node(*root.mChildren[i], name);
263load_texture(
size_t index) {
264 const aiTexture &tex = *_scene->mTextures[index];
268 if (tex.mHeight == 0) {
270 if (assimp_cat.is_debug()) {
272 <<
"Reading embedded compressed texture with format "
273 << tex.achFormatHint <<
" and size " << tex.mWidth <<
"\n";
276 str.write((
char*) tex.pcData, tex.mWidth);
278 if (strncmp(tex.achFormatHint,
"dds", 3) == 0) {
287 if (strncmp(tex.achFormatHint,
"jp\0", 3) == 0) {
293 if (img.
read(str,
"", ftype)) {
300 if (assimp_cat.is_debug()) {
302 <<
"Reading embedded raw texture with size "
303 << tex.mWidth <<
"x" << tex.mHeight <<
"\n";
306 ptex->setup_2d_texture(tex.mWidth, tex.mHeight, Texture::T_unsigned_byte, Texture::F_rgba);
307 PTA_uchar data = ptex->modify_ram_image();
310 for (
size_t i = 0; i < tex.mWidth * tex.mHeight; ++i) {
311 const aiTexel &texel = tex.pcData[i];
322 _textures[index] = ptex;
330load_texture_stage(
const aiMaterial &mat,
const aiTextureType &ttype,
334 aiTextureMapping mapping;
335 unsigned int uvindex;
338 aiTextureMapMode mapmode[3];
340 for (
size_t i = 0; i < mat.GetTextureCount(ttype); ++i) {
341 mat.GetTexture(ttype, i, &path, &mapping,
nullptr, &blend, &op, mapmode);
343 if (AI_SUCCESS != mat.Get(AI_MATKEY_UVWSRC(ttype, i), uvindex)) {
350 if (ttype == aiTextureType_DIFFUSE && i == 1) {
355 if (AI_SUCCESS == mat.Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR, col)) {
360 std::string uvindex_str = format_string(uvindex);
362 stage->set_mode(mode);
364 stage->set_texcoord_name(InternalName::get_texcoord_name(uvindex_str));
370 if (path.data[0] ==
'*') {
371 long num = strtol(path.data + 1,
nullptr, 10);
372 ptex = _textures[num];
374 }
else if (path.length > 0) {
404 if (ptex !=
nullptr) {
406 switch (mapmode[0]) {
407 case aiTextureMapMode_Wrap:
408 ptex->set_wrap_u(SamplerState::WM_repeat);
410 case aiTextureMapMode_Clamp:
411 ptex->set_wrap_u(SamplerState::WM_clamp);
413 case aiTextureMapMode_Decal:
414 ptex->set_wrap_u(SamplerState::WM_border_color);
415 ptex->set_border_color(LColor(0, 0, 0, 0));
417 case aiTextureMapMode_Mirror:
418 ptex->set_wrap_u(SamplerState::WM_mirror);
423 switch (mapmode[1]) {
424 case aiTextureMapMode_Wrap:
425 ptex->set_wrap_v(SamplerState::WM_repeat);
427 case aiTextureMapMode_Clamp:
428 ptex->set_wrap_v(SamplerState::WM_clamp);
430 case aiTextureMapMode_Decal:
431 ptex->set_wrap_v(SamplerState::WM_border_color);
432 ptex->set_border_color(LColor(0, 0, 0, 0));
434 case aiTextureMapMode_Mirror:
435 ptex->set_wrap_v(SamplerState::WM_mirror);
440 switch (mapmode[2]) {
441 case aiTextureMapMode_Wrap:
442 ptex->set_wrap_w(SamplerState::WM_repeat);
444 case aiTextureMapMode_Clamp:
445 ptex->set_wrap_w(SamplerState::WM_clamp);
447 case aiTextureMapMode_Decal:
448 ptex->set_wrap_w(SamplerState::WM_border_color);
449 ptex->set_border_color(LColor(0, 0, 0, 0));
451 case aiTextureMapMode_Mirror:
452 ptex->set_wrap_w(SamplerState::WM_mirror);
458 tattr = DCAST(
TextureAttrib, tattr->add_on_stage(stage, ptex));
461 aiUVTransform transform;
462 if (AI_SUCCESS == mat.Get(AI_MATKEY_UVTRANSFORM(ttype, i), transform)) {
464 PN_stdfloat rcos, rsin;
465 csincos(-transform.mRotation, &rsin, &rcos);
466 transform.mTranslation.x -= (0.5 * transform.mScaling.x) * (-rcos + rsin + 1);
467 transform.mTranslation.y -= ((0.5 * transform.mScaling.y) * (rsin + rcos - 1)) + 1 - transform.mScaling.y;
470 LMatrix3::translate_mat(0, -1) *
471 LMatrix3::scale_mat(transform.mScaling.x, transform.mScaling.y) *
472 LMatrix3::rotate_mat(rad_2_deg(-transform.mRotation)) *
473 LMatrix3::translate_mat(transform.mTranslation.x, 1 + transform.mTranslation.y);
476 TransformState::make_mat3(matrix);
479 ? TexMatrixAttrib::make(stage, std::move(cstate))
480 : tmattr->add_stage(stage,
std::move(cstate));
491load_material(
size_t index) {
492 const aiMaterial &mat = *_scene->mMaterials[index];
494 CPT(
RenderState) state = RenderState::make_empty();
506 if (AI_SUCCESS == mat.Get(AI_MATKEY_BASE_COLOR, col)) {
510 else if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_DIFFUSE, col)) {
511 pmat->set_diffuse(LColor(col.r, col.g, col.b, 1));
514 if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_SPECULAR, col)) {
515 if (AI_SUCCESS == mat.Get(AI_MATKEY_SHININESS_STRENGTH, fval)) {
516 pmat->set_specular(LColor(col.r * fval, col.g * fval, col.b * fval, 1));
518 pmat->set_specular(LColor(col.r, col.g, col.b, 1));
529 if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_AMBIENT, col)) {
530 pmat->set_specular(LColor(col.r, col.g, col.b, 1));
533 if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_EMISSIVE, col)) {
534 pmat->set_emission(LColor(col.r, col.g, col.b, 1));
537 if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_TRANSPARENT, col)) {
540 if (AI_SUCCESS == mat.Get(AI_MATKEY_SHININESS, fval)) {
541 pmat->set_shininess(fval);
544 if (AI_SUCCESS == mat.Get(AI_MATKEY_METALLIC_FACTOR, fval)) {
545 pmat->set_metallic(fval);
548 if (AI_SUCCESS == mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, fval)) {
549 pmat->set_roughness(fval);
552 if (AI_SUCCESS == mat.Get(AI_MATKEY_REFRACTI, fval)) {
553 pmat->set_refractive_index(fval);
556 else if (pmat->has_metallic()) {
558 pmat->set_refractive_index(1.5);
561 state = state->add_attrib(MaterialAttrib::make(pmat));
565 if (AI_SUCCESS == mat.Get(AI_MATKEY_ENABLE_WIREFRAME, ival)) {
567 state = state->add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_wireframe));
569 state = state->add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_filled));
575 if (AI_SUCCESS == mat.Get(AI_MATKEY_TWOSIDED, ival)) {
577 state = state->add_attrib(CullFaceAttrib::make(CullFaceAttrib::M_cull_none));
579 state = state->add_attrib(CullFaceAttrib::make_default());
585 if (AI_SUCCESS == mat.Get(AI_MATKEY_GLTF_ALPHAMODE, alpha_mode)) {
586 if (strcmp(alpha_mode.C_Str(),
"MASK") == 0) {
587 PN_stdfloat cutoff = 0.5;
588 mat.Get(AI_MATKEY_GLTF_ALPHACUTOFF, cutoff);
589 state = state->add_attrib(AlphaTestAttrib::make(AlphaTestAttrib::M_greater_equal, cutoff));
591 else if (strcmp(alpha_mode.C_Str(),
"BLEND") == 0) {
592 state = state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
599 load_texture_stage(mat, aiTextureType_DIFFUSE, TextureStage::M_modulate, tattr, tmattr);
604 if (mat.GetTextureCount(aiTextureType_UNKNOWN) > 0) {
605 load_texture_stage(mat, aiTextureType_UNKNOWN, TextureStage::M_selector, tattr, tmattr);
607 load_texture_stage(mat, aiTextureType_LIGHTMAP, TextureStage::M_modulate, tattr, tmattr);
610 load_texture_stage(mat, aiTextureType_NORMALS, TextureStage::M_normal, tattr, tmattr);
611 load_texture_stage(mat, aiTextureType_EMISSIVE, TextureStage::M_emission, tattr, tmattr);
612 load_texture_stage(mat, aiTextureType_HEIGHT, TextureStage::M_height, tattr, tmattr);
614 state = state->add_attrib(tattr);
616 if (tmattr !=
nullptr) {
617 state = state->add_attrib(tmattr);
620 _mat_states[index] = std::move(state);
628 const aiMatrix4x4 &t = node.mTransformation;
629 LMatrix4 mat(t.a1, t.b1, t.c1, t.d1,
630 t.a2, t.b2, t.c2, t.d2,
631 t.a3, t.b3, t.c3, t.d3,
632 t.a4, t.b4, t.c4, t.d4);
635 if (assimp_cat.is_debug()) {
637 <<
"Creating joint for: " << node.mName.C_Str() <<
"\n";
640 for (
size_t i = 0; i < node.mNumChildren; ++i) {
641 if (_bonemap.find(node.mChildren[i]->mName.C_Str()) != _bonemap.end()) {
642 create_joint(character, bundle, joint, *node.mChildren[i]);
651create_anim_channel(
const aiAnimation &anim,
AnimBundle *bundle,
AnimGroup *parent,
const aiNode &node) {
655 aiNodeAnim *node_anim =
nullptr;
656 for (
size_t i = 0; i < anim.mNumChannels; ++i) {
657 if (anim.mChannels[i]->mNodeName == node.mName) {
658 node_anim = anim.mChannels[i];
663 if (assimp_cat.is_debug()) {
665 <<
"Found channel for node: " << node.mName.C_Str() <<
"\n";
674 PTA_stdfloat tablex = PTA_stdfloat::empty_array(node_anim->mNumPositionKeys);
675 PTA_stdfloat tabley = PTA_stdfloat::empty_array(node_anim->mNumPositionKeys);
676 PTA_stdfloat tablez = PTA_stdfloat::empty_array(node_anim->mNumPositionKeys);
677 for (
size_t i = 0; i < node_anim->mNumPositionKeys; ++i) {
678 tablex[i] = node_anim->mPositionKeys[i].mValue.x;
679 tabley[i] = node_anim->mPositionKeys[i].mValue.y;
680 tablez[i] = node_anim->mPositionKeys[i].mValue.z;
682 group->set_table(
'x', tablex);
683 group->set_table(
'y', tabley);
684 group->set_table(
'z', tablez);
687 PTA_stdfloat tableh = PTA_stdfloat::empty_array(node_anim->mNumRotationKeys);
688 PTA_stdfloat tablep = PTA_stdfloat::empty_array(node_anim->mNumRotationKeys);
689 PTA_stdfloat tabler = PTA_stdfloat::empty_array(node_anim->mNumRotationKeys);
690 for (
size_t i = 0; i < node_anim->mNumRotationKeys; ++i) {
691 aiQuaternion ai_quat = node_anim->mRotationKeys[i].mValue;
692 LVecBase3 hpr = LQuaternion(ai_quat.w, ai_quat.x, ai_quat.y, ai_quat.z).get_hpr();
693 tableh[i] = hpr.get_x();
694 tablep[i] = hpr.get_y();
695 tabler[i] = hpr.get_z();
697 group->set_table(
'h', tableh);
698 group->set_table(
'p', tablep);
699 group->set_table(
'r', tabler);
702 PTA_stdfloat tablei = PTA_stdfloat::empty_array(node_anim->mNumScalingKeys);
703 PTA_stdfloat tablej = PTA_stdfloat::empty_array(node_anim->mNumScalingKeys);
704 PTA_stdfloat tablek = PTA_stdfloat::empty_array(node_anim->mNumScalingKeys);
705 for (
size_t i = 0; i < node_anim->mNumScalingKeys; ++i) {
706 tablei[i] = node_anim->mScalingKeys[i].mValue.x;
707 tablej[i] = node_anim->mScalingKeys[i].mValue.y;
708 tablek[i] = node_anim->mScalingKeys[i].mValue.z;
710 group->set_table(
'i', tablei);
711 group->set_table(
'j', tablej);
712 group->set_table(
'k', tablek);
714 else if (assimp_cat.is_debug()) {
716 <<
"No channel found for node: " << node.mName.C_Str() <<
"\n";
720 for (
size_t i = 0; i < node.mNumChildren; ++i) {
721 if (_bonemap.find(node.mChildren[i]->mName.C_Str()) != _bonemap.end()) {
722 create_anim_channel(anim, bundle, group, *node.mChildren[i]);
731load_mesh(
size_t index) {
732 const aiMesh &mesh = *_scene->mMeshes[index];
736 if (mesh.HasBones()) {
737 if (assimp_cat.is_debug()) {
739 <<
"Creating character for " << mesh.mName.C_Str() <<
"\n";
743 for (
size_t i = 0; i < mesh.mNumBones; ++i) {
744 const aiBone &bone = *mesh.mBones[i];
745 const aiNode *node = find_node(*_scene->mRootNode, bone.mName);
746 _bonemap[bone.mName.C_Str()] = node;
750 character =
new Character(mesh.mName.C_Str());
754 for (
size_t i = 0; i < mesh.mNumBones; ++i) {
755 const aiBone &bone = *mesh.mBones[i];
758 const aiNode *root = _bonemap[bone.mName.C_Str()];
759 while (root->mParent && _bonemap.find(root->mParent->mName.C_Str()) != _bonemap.end()) {
760 root = root->mParent;
764 if (character->
find_joint(root->mName.C_Str())) {
768 create_joint(character, bundle, skeleton, *root);
776 for (
size_t i = 0; i < mesh.mNumBones; ++i) {
777 const aiBone &bone = *mesh.mBones[i];
779 if (joint ==
nullptr) {
780 if (assimp_cat.is_debug()) {
782 <<
"Could not find joint for bone: " << bone.mName.C_Str() <<
"\n";
789 for (
size_t j = 0; j < bone.mNumWeights; ++j) {
790 const aiVertexWeight &weight = bone.mWeights[j];
792 bone_weights[weight.mVertexId].push_back(BoneWeight(jvt, weight.mWeight));
799 aformat->
add_column(InternalName::get_vertex(), 3, Geom::NT_stdfloat, Geom::C_point);
800 if (mesh.HasNormals()) {
801 aformat->add_column(InternalName::get_normal(), 3, Geom::NT_stdfloat, Geom::C_normal);
803 if (mesh.HasVertexColors(0)) {
804 aformat->add_column(InternalName::get_color(), 4, Geom::NT_stdfloat, Geom::C_color);
806 unsigned int num_uvs = mesh.GetNumUVChannels();
809 aformat->add_column(InternalName::get_texcoord(), 3, Geom::NT_stdfloat, Geom::C_texcoord);
810 for (
unsigned int u = 1; u < num_uvs; ++u) {
813 aformat->add_column(InternalName::get_texcoord_name(out.str()), 3, Geom::NT_stdfloat, Geom::C_texcoord);
816 if (mesh.HasTangentsAndBitangents()) {
817 aformat->add_column(InternalName::get_tangent(), 3, Geom::NT_stdfloat, Geom::C_vector);
818 aformat->add_column(InternalName::get_binormal(), 3, Geom::NT_stdfloat, Geom::C_vector);
822 tb_aformat->
add_column(InternalName::make(
"transform_blend"), 1, Geom::NT_uint16, Geom::C_index);
825 for (
size_t i = 0; i < _scene->mNumAnimations; ++i) {
826 aiAnimation &ai_anim = *_scene->mAnimations[i];
827 bool convert_anim =
false;
829 if (assimp_cat.is_debug()) {
831 <<
"Checking to see if anim (" << ai_anim.mName.C_Str()
832 <<
") matches character (" << mesh.mName.C_Str() <<
")\n";
834 for (
size_t j = 0; j < ai_anim.mNumChannels; ++j) {
835 if (assimp_cat.is_debug()) {
837 <<
"Searching for " << ai_anim.mChannels[j]->mNodeName.C_Str()
838 <<
" in bone map" <<
"\n";
840 if (_bonemap.find(ai_anim.mChannels[j]->mNodeName.C_Str()) != _bonemap.end()) {
847 if (assimp_cat.is_debug()) {
849 <<
"Found animation (" << ai_anim.mName.C_Str() <<
") for character ("
850 << mesh.mName.C_Str() <<
")\n";
854 unsigned int frames = 0;
855 for (
size_t j = 0; j < ai_anim.mNumChannels; ++j) {
856 if (ai_anim.mChannels[j]->mNumPositionKeys > frames) {
857 frames = ai_anim.mChannels[j]->mNumPositionKeys;
859 if (ai_anim.mChannels[j]->mNumRotationKeys > frames) {
860 frames = ai_anim.mChannels[j]->mNumRotationKeys;
862 if (ai_anim.mChannels[j]->mNumScalingKeys > frames) {
863 frames = ai_anim.mChannels[j]->mNumScalingKeys;
866 PN_stdfloat fps = frames / (ai_anim.mTicksPerSecond * ai_anim.mDuration);
867 if (assimp_cat.is_debug()) {
869 <<
"FPS " << fps <<
"\n";
871 <<
"Frames " << frames <<
"\n";
877 for (
size_t i = 0; i < mesh.mNumBones; ++i) {
878 const aiBone &bone = *mesh.mBones[i];
881 const aiNode *root = _bonemap[bone.mName.C_Str()];
882 while (root->mParent && _bonemap.find(root->mParent->mName.C_Str()) != _bonemap.end()) {
883 root = root->mParent;
887 if (root->mName == bone.mName) {
888 create_anim_channel(ai_anim, bundle, skeleton, *root);
892 character->add_child(bundle_node);
904 format->add_array(tb_aformat);
908 format->set_animation(aspec);
912 string name (mesh.mName.data, mesh.mName.length);
915 vdata->set_transform_blend_table(tbtable);
917 vdata->unclean_set_num_rows(mesh.mNumVertices);
921 for (
size_t i = 0; i < mesh.mNumVertices; ++i) {
922 const aiVector3D &vec = mesh.mVertices[i];
923 vertex.set_data3(vec.x, vec.y, vec.z);
927 if (mesh.HasNormals()) {
929 for (
size_t i = 0; i < mesh.mNumVertices; ++i) {
930 const aiVector3D &vec = mesh.mNormals[i];
931 normal.set_data3(vec.x, vec.y, vec.z);
936 if (mesh.HasVertexColors(0)) {
938 for (
size_t i = 0; i < mesh.mNumVertices; ++i) {
939 const aiColor4D &col = mesh.mColors[0][i];
940 color.set_data4(col.r, col.g, col.b, col.a);
948 for (
size_t i = 0; i < mesh.mNumVertices; ++i) {
949 const aiVector3D &vec = mesh.mTextureCoords[0][i];
950 texcoord0.set_data3(vec.x, vec.y, vec.z);
952 for (
unsigned int u = 1; u < num_uvs; ++u) {
955 GeomVertexWriter texcoord (vdata, InternalName::get_texcoord_name(out.str()));
956 for (
size_t i = 0; i < mesh.mNumVertices; ++i) {
957 const aiVector3D &vec = mesh.mTextureCoords[u][i];
958 texcoord.set_data3(vec.x, vec.y, vec.z);
964 if (mesh.HasTangentsAndBitangents()) {
967 for (
size_t i = 0; i < mesh.mNumVertices; ++i) {
968 const aiVector3D &tvec = mesh.mTangents[i];
969 const aiVector3D &bvec = mesh.mBitangents[i];
970 tangent.set_data3(tvec.x, tvec.y, tvec.z);
971 binormal.set_data3(bvec.x, bvec.y, bvec.z);
977 GeomVertexWriter transform_blend (vdata, InternalName::get_transform_blend());
979 for (
size_t i = 0; i < mesh.mNumVertices; ++i) {
982 for (
size_t j = 0; j < bone_weights[i].size(); ++j) {
983 tblend.
add_transform(bone_weights[i][j].joint_vertex_xform, bone_weights[i][j].weight);
985 transform_blend.set_data1i(tbtable->add_blend(tblend));
999 for (
size_t i = 0; i < mesh.mNumFaces; ++i) {
1000 const aiFace &face = mesh.mFaces[i];
1002 if (face.mNumIndices == 0) {
1005 }
else if (face.mNumIndices == 1) {
1006 points->add_vertex(face.mIndices[0]);
1007 points->close_primitive();
1008 }
else if (face.mNumIndices == 2) {
1009 lines->add_vertices(face.mIndices[0], face.mIndices[1]);
1010 lines->close_primitive();
1011 }
else if (face.mNumIndices == 3) {
1012 triangles->add_vertices(face.mIndices[0], face.mIndices[1], face.mIndices[2]);
1013 triangles->close_primitive();
1015 nassertd(
false) continue;
1020 Geoms &geoms = _geoms[index];
1021 geoms._mat_index = mesh.mMaterialIndex;
1023 if (points->get_num_primitives() > 0) {
1024 geoms._points =
new Geom(vdata);
1025 geoms._points->add_primitive(points);
1027 if (lines->get_num_primitives() > 0) {
1028 geoms._lines =
new Geom(vdata);
1029 geoms._lines->add_primitive(lines);
1031 if (triangles->get_num_primitives() > 0) {
1032 geoms._triangles =
new Geom(vdata);
1033 geoms._triangles->add_primitive(triangles);
1037 _charmap[mesh.mName.C_Str()] = character;
1045load_node(
const aiNode &node,
PandaNode *parent) {
1050 if (_bonemap.find(node.mName.C_Str()) != _bonemap.end()) {
1055 string name (node.mName.data, node.mName.length);
1056 if (node.mNumMeshes > 0) {
1063 if (parent == _root && assimp_collapse_dummy_root_node &&
1064 _charmap.find(node.mName.C_Str()) == _charmap.end() &&
1065 (name.empty() || name[0] ==
'$' || name ==
"RootNode" || name ==
"ROOT" || name ==
"Root" || (name.size() > 2 && name[0] ==
'<' && name[name.size() - 1] ==
'>') || name == _root->get_name())) {
1073 if (_charmap.find(node.mName.C_Str()) != _charmap.end()) {
1074 character = _charmap[node.mName.C_Str()];
1075 parent->add_child(character);
1077 else if (parent != pnode) {
1078 parent->add_child(pnode);
1081 if (node.mMetaData !=
nullptr) {
1082 for (
unsigned i = 0; i < node.mMetaData->mNumProperties; ++i) {
1083 const aiMetadataEntry &entry = node.mMetaData->mValues[i];
1085 switch (entry.mType) {
1089 case (aiMetadataType)1:
1090 value = format_string(*
static_cast<int32_t *
>(entry.mData));
1093 value = format_string(*
static_cast<uint64_t *
>(entry.mData));
1096 value = format_string(*
static_cast<float *
>(entry.mData));
1100 const aiString *str =
static_cast<const aiString *
>(entry.mData);
1101 value = std::string(str->data, str->length);
1108 if (entry.mType == (aiMetadataType)4) {
1109 value = format_string(*
static_cast<double *
>(entry.mData));
1114 const aiString &key = node.mMetaData->mKeys[i];
1115 pnode->set_tag(std::string(key.data, key.length), std::move(value));
1120 const aiMatrix4x4 &t = node.mTransformation;
1121 if (!t.IsIdentity()) {
1122 LMatrix4 mat(t.a1, t.b1, t.c1, t.d1,
1123 t.a2, t.b2, t.c2, t.d2,
1124 t.a3, t.b3, t.c3, t.d3,
1125 t.a4, t.b4, t.c4, t.d4);
1126 pnode->set_transform(TransformState::make_mat(mat));
1129 for (
size_t i = 0; i < node.mNumChildren; ++i) {
1130 load_node(*node.mChildren[i], pnode);
1133 if (node.mNumMeshes > 0) {
1139 if (node.mNumMeshes == 1) {
1140 meshIndex = node.mMeshes[0];
1141 const Geoms &geoms = _geoms[meshIndex];
1142 if (geoms._points !=
nullptr) {
1143 gnode->add_geom(geoms._points);
1145 if (geoms._lines !=
nullptr) {
1146 gnode->add_geom(geoms._lines);
1148 if (geoms._triangles !=
nullptr) {
1149 gnode->add_geom(geoms._triangles);
1151 gnode->set_state(_mat_states[geoms._mat_index]);
1153 for (
size_t i = 0; i < node.mNumMeshes; ++i) {
1154 meshIndex = node.mMeshes[i];
1155 const Geoms &geoms = _geoms[meshIndex];
1156 const RenderState *state = _mat_states[geoms._mat_index];
1157 if (geoms._points !=
nullptr) {
1158 gnode->add_geom(geoms._points, state);
1160 if (geoms._lines !=
nullptr) {
1161 gnode->add_geom(geoms._lines, state);
1163 if (geoms._triangles !=
nullptr) {
1164 gnode->add_geom(geoms._triangles, state);
1170 if (assimp_cat.is_debug()) {
1171 assimp_cat.debug() <<
"Adding char to geom\n";
1173 character->add_child(gnode);
1182load_light(
const aiLight &light) {
1183 string name (light.mName.data, light.mName.length);
1184 if (assimp_cat.is_debug()) {
1185 assimp_cat.debug() <<
"Found light '" << name <<
"'\n";
1191 switch (light.mType) {
1192 case aiLightSource_DIRECTIONAL: {
1194 _root->add_child(dlight);
1196 col = light.mColorDiffuse;
1197 dlight->set_color(LColor(col.r, col.g, col.b, 1));
1199 col = light.mColorSpecular;
1200 dlight->set_specular_color(LColor(col.r, col.g, col.b, 1));
1202 vec = light.mPosition;
1203 dlight->set_point(LPoint3(vec.x, vec.y, vec.z));
1205 vec = light.mDirection;
1206 dlight->set_direction(LVector3(vec.x, vec.y, vec.z));
1209 case aiLightSource_POINT: {
1211 _root->add_child(plight);
1213 col = light.mColorDiffuse;
1214 plight->set_color(LColor(col.r, col.g, col.b, 1));
1216 col = light.mColorSpecular;
1217 plight->set_specular_color(LColor(col.r, col.g, col.b, 1));
1219 vec = light.mPosition;
1220 plight->set_point(LPoint3(vec.x, vec.y, vec.z));
1222 plight->set_attenuation(LVecBase3(light.mAttenuationConstant,
1223 light.mAttenuationLinear,
1224 light.mAttenuationQuadratic));
1227 case aiLightSource_SPOT: {
1229 _root->add_child(plight);
1231 col = light.mColorDiffuse;
1232 plight->set_color(LColor(col.r, col.g, col.b, 1));
1234 col = light.mColorSpecular;
1235 plight->set_specular_color(LColor(col.r, col.g, col.b, 1));
1237 plight->set_attenuation(LVecBase3(light.mAttenuationConstant,
1238 light.mAttenuationLinear,
1239 light.mAttenuationQuadratic));
1241 plight->get_lens()->set_fov(light.mAngleOuterCone);
1245 vec = light.mDirection;
1246 LPoint3 pos (light.mPosition.x, light.mPosition.y, light.mPosition.z);
1248 ::look_at(quat, LPoint3(vec.x, vec.y, vec.z), LVector3::up());
1249 plight->set_transform(TransformState::make_pos_quat_scale(pos, quat, LVecBase3(1, 1, 1)));
1259 assimp_cat.warning() <<
"Light '" << name <<
"' has an unknown type!\n";
1264 col = light.mColorAmbient;
1265 LVecBase4 ambient (col.r, col.g, col.b, 0);
1266 if (ambient != LVecBase4::zero()) {
1268 alight->set_color(ambient);
1269 _root->add_child(alight);
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A light source that seems to illuminate all points in space at once.
This is a node that contains a pointer to an AnimBundle.
This is the root of an AnimChannel hierarchy.
An animation channel that issues a matrix each frame, read from a table such as might have been read ...
This is the base class for AnimChannel and AnimBundle.
void get_extensions(std::string &ext) const
Returns a space-separated list of extensions that Assimp can load, without the leading dots.
bool read(const Filename &filename)
Reads from the indicated file.
void build_graph()
Converts scene graph structures into a Panda3D scene graph, with _root being the root node.
The collection of all the joints and sliders in the character.
This represents one joint of the character's animation, containing an animating transform matrix.
An animated character, with skeleton-morph animation and either soft- skinned or hard-skinned vertice...
CharacterJoint * find_joint(const std::string &name) const
Returns a pointer to the joint with the given name, if there is such a joint, or NULL if there is no ...
A light shining from infinitely far away in a particular direction, like sunlight.
The name of a file, such as a texture file or an Egg file.
std::string get_basename() const
Returns the basename part of the filename.
bool make_canonical()
Converts this filename to a canonical name by replacing the directory part with the fully-qualified d...
static Filename from_os_specific(const std::string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes,...
std::string get_dirname() const
Returns the directory part of the filename.
Defines a series of disconnected line segments.
A node that holds Geom objects, renderable pieces of geometry.
Defines a series of disconnected points.
Defines a series of disconnected triangles.
This object describes how the vertex animation, if any, represented in a GeomVertexData is encoded.
void set_panda()
Specifies that vertex animation is to be performed by Panda.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
A container for geometry primitives.
Defines the way an object appears in the presence of lighting.
set_base_color
Specifies the base color of the material.
A node of this type is created automatically at the root of each model file that is loaded.
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
This class maintains the set of all known PNMFileTypes in the universe.
static PNMFileTypeRegistry * get_global_ptr()
Returns a pointer to the global PNMFileTypeRegistry object.
PNMFileType * get_type_from_extension(const std::string &filename) const
Tries to determine what the PNMFileType is likely to be for a particular image file based on its exte...
This is the base class of a family of classes that represent particular image file types that PNMImag...
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
bool read(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Reads the indicated image filename.
Custom implementation of Assimp::IOSystem.
static void set_default()
Makes sure there's a global PandaLogger object and makes sure that it is Assimp's default logger.
A basic node of the scene graph or data graph.
This is the base class for PartRoot and MovingPart.
A light originating from a single point in space, and shining in all directions.
This is the base class for a number of render attributes (other than transform) that may be set on sc...
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
static SparseArray lower_on(int on_bits)
Returns a SparseArray whose lower on_bits bits are on.
A light originating from a single point in space, and shining in a particular direction,...
Applies a transform matrix to UV's before they are rendered.
Indicates the set of TextureStages and their associated Textures that should be applied to (or remove...
get_num_on_stages
Returns the number of stages that are turned on by the attribute.
static Texture * load_texture(const Filename &filename, int primary_file_num_channels=0, bool read_mipmaps=false, const LoaderOptions &options=LoaderOptions())
Loads the given filename up into a texture, if it has not already been loaded, and returns the new te...
Defines the properties of a named stage of the multitexture pipeline.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
A hierarchy of directories and files that appears to be one continuous file system,...
bool is_regular_file(const Filename &filename) const
Convenience function; returns true if the named file exists as a regular file in the virtual file sys...
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
This is our own Panda specialization on the default STL vector.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.