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;
87 CPT(JointVertexTransform) joint_vertex_xform;
90 BoneWeight(CPT(JointVertexTransform) joint_vertex_xform,
float weight)
91 : joint_vertex_xform(joint_vertex_xform), weight(weight)
94typedef pvector<BoneWeight> BoneWeightList;
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);
203 _root =
new ModelRoot(_filename.get_basename());
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];
266 PT(Texture) ptex =
new Texture;
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);
361 PT(TextureStage) stage =
new TextureStage(uvindex_str);
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) {
379 Filename dir (_filename);
380 _filename.make_canonical();
381 dir = _filename.get_dirname();
385 fn = Filename(dir, fn);
387 fn = Filename(dir, fn +
".tga");
389 fn = Filename(dir, fn +
".jpg");
391 dir = _filename.get_dirname();
393 fn = Filename(dir, fn);
395 fn = Filename(dir, fn +
".tga");
397 fn = Filename(dir, fn +
".jpg");
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);
478 CPT(RenderAttrib) new_attr = (tmattr ==
nullptr)
480 : tmattr->
add_stage(stage, std::move(cstate));
481 tmattr = DCAST(TexMatrixAttrib, std::move(new_attr));
491load_material(
size_t index) {
492 const aiMaterial &mat = *_scene->mMaterials[index];
494 CPT(RenderState) state = RenderState::make_empty();
504 PT(Material) pmat =
new Material;
506 if (AI_SUCCESS == mat.Get(AI_MATKEY_BASE_COLOR, col)) {
507 pmat->set_base_color(LColor(col.r, col.g, col.b, col.a));
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);
565 if (AI_SUCCESS == mat.Get(AI_MATKEY_ENABLE_WIREFRAME, ival)) {
575 if (AI_SUCCESS == mat.Get(AI_MATKEY_TWOSIDED, ival)) {
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);
591 else if (strcmp(alpha_mode.C_Str(),
"BLEND") == 0) {
598 CPT(TexMatrixAttrib) tmattr;
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);
633 PT(CharacterJoint) joint =
new CharacterJoint(character, bundle, parent, node.mName.C_Str(), mat);
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) {
652 PT(AnimChannelMatrixXfmTable) group =
new AnimChannelMatrixXfmTable(parent, node.mName.C_Str());
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];
735 PT(Character) character =
nullptr;
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());
751 PT(CharacterJointBundle) bundle = character->get_bundle(0);
752 PT(PartGroup) skeleton =
new PartGroup(bundle,
"<skeleton>");
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);
773 PT(TransformBlendTable) tbtable =
new TransformBlendTable;
774 pvector<BoneWeightList> bone_weights(mesh.mNumVertices);
776 for (
size_t i = 0; i < mesh.mNumBones; ++i) {
777 const aiBone &bone = *mesh.mBones[i];
778 CharacterJoint *joint = character->
find_joint(bone.mName.C_Str());
779 if (joint ==
nullptr) {
780 if (assimp_cat.is_debug()) {
782 <<
"Could not find joint for bone: " << bone.mName.C_Str() <<
"\n";
787 CPT(JointVertexTransform) jvt =
new JointVertexTransform(joint);
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));
798 PT(GeomVertexArrayFormat) aformat =
new GeomVertexArrayFormat;
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);
821 PT(GeomVertexArrayFormat) tb_aformat =
new GeomVertexArrayFormat;
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";
874 PT(AnimBundle) bundle =
new AnimBundle(mesh.mName.C_Str(), fps, frames);
875 PT(AnimGroup) skeleton =
new AnimGroup(bundle,
"<skeleton>");
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);
891 PT(AnimBundleNode) bundle_node =
new AnimBundleNode(bone.mName.C_Str(), bundle);
901 PT(GeomVertexFormat) format =
new GeomVertexFormat;
902 format->add_array(aformat);
904 format->add_array(tb_aformat);
906 GeomVertexAnimationSpec aspec;
908 format->set_animation(aspec);
912 string name (mesh.mName.data, mesh.mName.length);
913 PT(GeomVertexData) vdata =
new GeomVertexData(name, GeomVertexFormat::register_format(format), Geom::UH_static);
915 vdata->set_transform_blend_table(tbtable);
917 vdata->unclean_set_num_rows(mesh.mNumVertices);
920 GeomVertexWriter vertex (vdata, InternalName::get_vertex());
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()) {
928 GeomVertexWriter normal (vdata, InternalName::get_normal());
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)) {
937 GeomVertexWriter color (vdata, InternalName::get_color());
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);
947 GeomVertexWriter texcoord0 (vdata, InternalName::get_texcoord());
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()) {
965 GeomVertexWriter tangent (vdata, InternalName::get_tangent());
966 GeomVertexWriter binormal (vdata, InternalName::get_binormal());
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) {
980 TransformBlend tblend;
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));
994 PT(GeomPoints) points =
new GeomPoints(Geom::UH_static);
995 PT(GeomLines) lines =
new GeomLines(Geom::UH_static);
996 PT(GeomTriangles) triangles =
new GeomTriangles(Geom::UH_static);
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) {
1046 PT(PandaNode) pnode;
1047 PT(Character) character;
1050 if (_bonemap.find(node.mName.C_Str()) != _bonemap.end()) {
1055 string name (node.mName.data, node.mName.length);
1056 if (node.mNumMeshes > 0) {
1057 pnode =
new GeomNode(name);
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())) {
1069 pnode =
new PandaNode(name);
1073 if (_charmap.find(node.mName.C_Str()) != _charmap.end()) {
1074 character = _charmap[node.mName.C_Str()];
1077 else if (parent != 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) {
1135 PT(GeomNode) gnode = DCAST(GeomNode, pnode);
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";
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: {
1193 PT(DirectionalLight) dlight =
new DirectionalLight(name);
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: {
1210 PT(PointLight) plight =
new PointLight(name);
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: {
1228 PT(Spotlight) plight =
new Spotlight(name);
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()) {
1267 PT(AmbientLight) alight =
new AmbientLight(name);
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.
static ConstPointerTo< RenderAttrib > make(PandaCompareFunc mode, PN_stdfloat reference_alpha)
Constructs a new AlphaTestAttrib object.
This is the root of an AnimChannel hierarchy.
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.
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 ...
static ConstPointerTo< RenderAttrib > make_default()
Returns a RenderAttrib that corresponds to whatever the standard default properties for render attrib...
static ConstPointerTo< RenderAttrib > make(Mode mode=M_cull_clockwise)
Constructs a new CullFaceAttrib object that specifies how to cull geometry.
The name of a file, such as a texture file or an Egg file.
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,...
void set_panda()
Specifies that vertex animation is to be performed by Panda.
static ConstPointerTo< RenderAttrib > make(Material *material)
Constructs a new MaterialAttrib object suitable for rendering the indicated material onto geometry.
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 ...
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...
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.
void add_child(PandaNode *child_node, int sort=0, Thread *current_thread=Thread::get_current_thread())
Adds a new child to the node.
This is the base class for PartRoot and MovingPart.
static ConstPointerTo< RenderAttrib > make(Mode mode, PN_stdfloat thickness=1.0f, bool perspective=false, const LColor &wireframe_color=LColor::zero())
Constructs a new RenderModeAttrib object that specifies whether to draw polygons in the normal,...
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.
Applies a transform matrix to UV's before they are rendered.
static ConstPointerTo< RenderAttrib > make()
Constructs a TexMatrixAttrib that applies no stages at all.
ConstPointerTo< RenderAttrib > add_stage(TextureStage *stage, const TransformState *transform, int override=0) const
Returns a new TexMatrixAttrib just like this one, with the indicated transform for the given stage.
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 ConstPointerTo< RenderAttrib > make()
Constructs a new TextureAttrib object that does nothing.
ConstPointerTo< RenderAttrib > add_on_stage(TextureStage *stage, Texture *tex, int override=0) const
Returns a new TextureAttrib, just like this one, but with the indicated stage added to the list of st...
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...
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
static ConstPointerTo< RenderAttrib > make(Mode mode)
Constructs a new TransparencyAttrib object.
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.
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.