From 09a81e0d6870e15de834abba3a18f65fd81535f7 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Nov 2022 19:31:54 +0100 Subject: [PATCH 01/14] assimp: Fix memory corruption in load_texture_stage() --- pandatool/src/assimp/assimpLoader.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandatool/src/assimp/assimpLoader.cxx b/pandatool/src/assimp/assimpLoader.cxx index 8547bb7c56..316781d50b 100644 --- a/pandatool/src/assimp/assimpLoader.cxx +++ b/pandatool/src/assimp/assimpLoader.cxx @@ -291,10 +291,10 @@ load_texture_stage(const aiMaterial &mat, const aiTextureType &ttype, CPT(Textur unsigned int uvindex; float blend; aiTextureOp op; - aiTextureMapMode mapmode; + aiTextureMapMode mapmode[3]; for (size_t i = 0; i < mat.GetTextureCount(ttype); ++i) { - mat.GetTexture(ttype, i, &path, &mapping, nullptr, &blend, &op, &mapmode); + mat.GetTexture(ttype, i, &path, &mapping, nullptr, &blend, &op, mapmode); if (AI_SUCCESS != mat.Get(AI_MATKEY_UVWSRC(ttype, i), uvindex)) { // If there's no texture coordinate set for this texture, assume that From 2226f8de9e57b804ac505e8a69825c509cc59c97 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Nov 2022 19:35:10 +0100 Subject: [PATCH 02/14] assimp: Add support for texture wrapping modes --- pandatool/src/assimp/assimpLoader.cxx | 53 +++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/pandatool/src/assimp/assimpLoader.cxx b/pandatool/src/assimp/assimpLoader.cxx index 316781d50b..e2c0ff63d5 100644 --- a/pandatool/src/assimp/assimpLoader.cxx +++ b/pandatool/src/assimp/assimpLoader.cxx @@ -348,6 +348,59 @@ load_texture_stage(const aiMaterial &mat, const aiTextureType &ttype, CPT(Textur } if (ptex != nullptr) { + // Apply the mapping modes. + switch (mapmode[0]) { + case aiTextureMapMode_Wrap: + ptex->set_wrap_u(SamplerState::WM_repeat); + break; + case aiTextureMapMode_Clamp: + ptex->set_wrap_u(SamplerState::WM_clamp); + break; + case aiTextureMapMode_Decal: + ptex->set_wrap_u(SamplerState::WM_border_color); + ptex->set_border_color(LColor(0, 0, 0, 0)); + break; + case aiTextureMapMode_Mirror: + ptex->set_wrap_u(SamplerState::WM_mirror); + break; + default: + break; + } + switch (mapmode[1]) { + case aiTextureMapMode_Wrap: + ptex->set_wrap_v(SamplerState::WM_repeat); + break; + case aiTextureMapMode_Clamp: + ptex->set_wrap_v(SamplerState::WM_clamp); + break; + case aiTextureMapMode_Decal: + ptex->set_wrap_v(SamplerState::WM_border_color); + ptex->set_border_color(LColor(0, 0, 0, 0)); + break; + case aiTextureMapMode_Mirror: + ptex->set_wrap_v(SamplerState::WM_mirror); + break; + default: + break; + } + switch (mapmode[2]) { + case aiTextureMapMode_Wrap: + ptex->set_wrap_w(SamplerState::WM_repeat); + break; + case aiTextureMapMode_Clamp: + ptex->set_wrap_w(SamplerState::WM_clamp); + break; + case aiTextureMapMode_Decal: + ptex->set_wrap_w(SamplerState::WM_border_color); + ptex->set_border_color(LColor(0, 0, 0, 0)); + break; + case aiTextureMapMode_Mirror: + ptex->set_wrap_w(SamplerState::WM_mirror); + break; + default: + break; + } + tattr = DCAST(TextureAttrib, tattr->add_on_stage(stage, ptex)); } } From 356e974029a90ad8f6d2afcae6ea501a96c85a02 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Nov 2022 22:42:28 +0100 Subject: [PATCH 03/14] ptloader: Fix wrong initialization order issue --- pandatool/src/ptloader/config_ptloader.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandatool/src/ptloader/config_ptloader.cxx b/pandatool/src/ptloader/config_ptloader.cxx index e3fb3f9dd9..a5b012a5dc 100644 --- a/pandatool/src/ptloader/config_ptloader.cxx +++ b/pandatool/src/ptloader/config_ptloader.cxx @@ -80,10 +80,11 @@ init_libptloader() { LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr(); init_liblwo(); + init_libflt(); + FltToEggConverter *flt = new FltToEggConverter; reg->register_type(new LoaderFileTypePandatool(flt)); - init_libflt(); LwoToEggConverter *lwo = new LwoToEggConverter; reg->register_type(new LoaderFileTypePandatool(lwo)); From a11f7c9c8dda8de78ebc4c5ecb81ac02247ffa48 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Nov 2022 22:45:29 +0100 Subject: [PATCH 04/14] gobj: Add `TextureStage::write()` overload with `indent_level` param --- panda/src/gobj/textureStage.cxx | 31 +++++++++++++++++++++++-------- panda/src/gobj/textureStage.h | 1 + 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/panda/src/gobj/textureStage.cxx b/panda/src/gobj/textureStage.cxx index 26a00fc6c7..cd29b04040 100644 --- a/panda/src/gobj/textureStage.cxx +++ b/panda/src/gobj/textureStage.cxx @@ -15,6 +15,7 @@ #include "internalName.h" #include "bamReader.h" #include "bamWriter.h" +#include "indent.h" using std::ostream; @@ -221,15 +222,28 @@ compare_to(const TextureStage &other) const { */ void TextureStage:: write(ostream &out) const { - out << "TextureStage " << get_name() << ", sort = " << get_sort() << ", priority = " << get_priority() << "\n" - << " texcoords = " << get_texcoord_name()->get_name() - << ", mode = " << get_mode() << ", color = " << get_color() - << ", scale = " << get_rgb_scale() << ", " << get_alpha_scale() - << ", saved_result = " << get_saved_result() - << ", tex_view_offset = " << get_tex_view_offset() << "\n"; + write(out, 0); +} + +/** + * Writes the details of this stage + */ +void TextureStage:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) + << "TextureStage " << get_name() << ", sort = " << get_sort() + << ", priority = " << get_priority() << "\n"; + + indent(out, indent_level) + << " texcoords = " << get_texcoord_name()->get_name() + << ", mode = " << get_mode() << ", color = " << get_color() + << ", scale = " << get_rgb_scale() << ", " << get_alpha_scale() + << ", saved_result = " << get_saved_result() + << ", tex_view_offset = " << get_tex_view_offset() << "\n"; if (get_mode() == M_combine) { - out << " RGB combine mode = " << get_combine_rgb_mode() << "\n"; + indent(out, indent_level) + << " RGB combine mode = " << get_combine_rgb_mode() << "\n"; if (get_num_combine_rgb_operands() >= 1) { out << " 0: " << get_combine_rgb_source0() << ", " << get_combine_rgb_operand0() << "\n"; @@ -242,7 +256,8 @@ write(ostream &out) const { out << " 2: " << get_combine_rgb_source2() << ", " << get_combine_rgb_operand2() << "\n"; } - out << " alpha combine mode = " << get_combine_alpha_mode() << "\n"; + indent(out, indent_level) + << " alpha combine mode = " << get_combine_alpha_mode() << "\n"; if (get_num_combine_alpha_operands() >= 1) { out << " 0: " << get_combine_alpha_source0() << ", " << get_combine_alpha_operand0() << "\n"; diff --git a/panda/src/gobj/textureStage.h b/panda/src/gobj/textureStage.h index a1a9ccb6dd..ebbd1ab1a2 100644 --- a/panda/src/gobj/textureStage.h +++ b/panda/src/gobj/textureStage.h @@ -182,6 +182,7 @@ PUBLISHED: int compare_to(const TextureStage &other) const; void write(std::ostream &out) const; + void write(std::ostream &out, int indent_level) const; void output(std::ostream &out) const; INLINE static TextureStage *get_default(); From 9248182a0b09730954e8ba06204e794dff002a7d Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Nov 2022 20:46:56 +0100 Subject: [PATCH 05/14] assimp: Support texture transforms --- pandatool/src/assimp/assimpLoader.cxx | 37 ++++++++++++++++++++++++--- pandatool/src/assimp/assimpLoader.h | 3 ++- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/pandatool/src/assimp/assimpLoader.cxx b/pandatool/src/assimp/assimpLoader.cxx index e2c0ff63d5..5725bf85a8 100644 --- a/pandatool/src/assimp/assimpLoader.cxx +++ b/pandatool/src/assimp/assimpLoader.cxx @@ -35,6 +35,8 @@ #include "animBundleNode.h" #include "animChannelMatrixXfmTable.h" #include "pvector.h" +#include "cmath.h" +#include "deg_2_rad.h" #include "pandaIOSystem.h" #include "pandaLogger.h" @@ -285,7 +287,8 @@ load_texture(size_t index) { * Converts an aiMaterial into a RenderState. */ void AssimpLoader:: -load_texture_stage(const aiMaterial &mat, const aiTextureType &ttype, CPT(TextureAttrib) &tattr) { +load_texture_stage(const aiMaterial &mat, const aiTextureType &ttype, + CPT(TextureAttrib) &tattr, CPT(TexMatrixAttrib) &tmattr) { aiString path; aiTextureMapping mapping; unsigned int uvindex; @@ -402,6 +405,30 @@ load_texture_stage(const aiMaterial &mat, const aiTextureType &ttype, CPT(Textur } tattr = DCAST(TextureAttrib, tattr->add_on_stage(stage, ptex)); + + // Is there a texture transform? + aiUVTransform transform; + if (AI_SUCCESS == mat.Get(AI_MATKEY_UVTRANSFORM(ttype, i), transform)) { + // Reconstruct the original origin from the glTF file. + PN_stdfloat rcos, rsin; + csincos(-transform.mRotation, &rsin, &rcos); + transform.mTranslation.x -= (0.5 * transform.mScaling.x) * (-rcos + rsin + 1); + transform.mTranslation.y -= ((0.5 * transform.mScaling.y) * (rsin + rcos - 1)) + 1 - transform.mScaling.y; + + LMatrix3 matrix = + LMatrix3::translate_mat(0, -1) * + LMatrix3::scale_mat(transform.mScaling.x, transform.mScaling.y) * + LMatrix3::rotate_mat(rad_2_deg(-transform.mRotation)) * + LMatrix3::translate_mat(transform.mTranslation.x, 1 + transform.mTranslation.y); + + CPT(TransformState) cstate = + TransformState::make_mat3(matrix); + + CPT(RenderAttrib) new_attr = (tmattr == nullptr) + ? TexMatrixAttrib::make(stage, std::move(cstate)) + : tmattr->add_stage(stage, std::move(cstate)); + tmattr = DCAST(TexMatrixAttrib, std::move(new_attr)); + } } } } @@ -477,11 +504,15 @@ load_material(size_t index) { // And let's not forget the textures! CPT(TextureAttrib) tattr = DCAST(TextureAttrib, TextureAttrib::make()); - load_texture_stage(mat, aiTextureType_DIFFUSE, tattr); - load_texture_stage(mat, aiTextureType_LIGHTMAP, tattr); + CPT(TexMatrixAttrib) tmattr; + load_texture_stage(mat, aiTextureType_DIFFUSE, tattr, tmattr); + load_texture_stage(mat, aiTextureType_LIGHTMAP, tattr, tmattr); if (tattr->get_num_on_stages() > 0) { state = state->add_attrib(tattr); } + if (tmattr != nullptr) { + state = state->add_attrib(tmattr); + } _mat_states[index] = state; } diff --git a/pandatool/src/assimp/assimpLoader.h b/pandatool/src/assimp/assimpLoader.h index 3133fee377..da60c36a3f 100644 --- a/pandatool/src/assimp/assimpLoader.h +++ b/pandatool/src/assimp/assimpLoader.h @@ -72,7 +72,8 @@ private: const aiNode *find_node(const aiNode &root, const aiString &name); void load_texture(size_t index); - void load_texture_stage(const aiMaterial &mat, const aiTextureType &ttype, CPT(TextureAttrib) &tattr); + void load_texture_stage(const aiMaterial &mat, const aiTextureType &ttype, + CPT(TextureAttrib) &tattr, CPT(TexMatrixAttrib) &tmattr); void load_material(size_t index); void create_joint(Character *character, CharacterJointBundle *bundle, PartGroup *parent, const aiNode &node); void create_anim_channel(const aiAnimation &anim, AnimBundle *bundle, AnimGroup *parent, const aiNode &node); From b254e5b7ba46cf89e0a77043e4f8ed83c593e5eb Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Nov 2022 20:49:43 +0100 Subject: [PATCH 06/14] assimp: Support importing alpha mode from glTF files --- pandatool/src/assimp/assimpLoader.cxx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pandatool/src/assimp/assimpLoader.cxx b/pandatool/src/assimp/assimpLoader.cxx index 5725bf85a8..0a5e84fa94 100644 --- a/pandatool/src/assimp/assimpLoader.cxx +++ b/pandatool/src/assimp/assimpLoader.cxx @@ -21,9 +21,11 @@ #include "geomTriangles.h" #include "pnmFileTypeRegistry.h" #include "pnmImage.h" +#include "alphaTestAttrib.h" #include "materialAttrib.h" #include "textureAttrib.h" #include "cullFaceAttrib.h" +#include "transparencyAttrib.h" #include "ambientLight.h" #include "directionalLight.h" #include "spotlight.h" @@ -43,6 +45,10 @@ #include +#ifndef AI_MATKEY_GLTF_ALPHAMODE +#define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0 +#endif + using std::ostringstream; using std::stringstream; using std::string; @@ -502,6 +508,19 @@ load_material(size_t index) { } } + // Alpha mode. + aiString alpha_mode; + if (AI_SUCCESS == mat.Get(AI_MATKEY_GLTF_ALPHAMODE, alpha_mode)) { + if (strcmp(alpha_mode.C_Str(), "MASK") == 0) { + PN_stdfloat cutoff = 0.5; + mat.Get(AI_MATKEY_GLTF_ALPHACUTOFF, cutoff); + state = state->add_attrib(AlphaTestAttrib::make(AlphaTestAttrib::M_greater_equal, cutoff)); + } + else if (strcmp(alpha_mode.C_Str(), "BLEND") == 0) { + state = state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha)); + } + } + // And let's not forget the textures! CPT(TextureAttrib) tattr = DCAST(TextureAttrib, TextureAttrib::make()); CPT(TexMatrixAttrib) tmattr; From 6e636f5ca0818eb519a328322d67c8107a45f31e Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Nov 2022 21:10:00 +0100 Subject: [PATCH 07/14] assimp: Fix issues reading external files --- pandatool/src/assimp/pandaIOStream.cxx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pandatool/src/assimp/pandaIOStream.cxx b/pandatool/src/assimp/pandaIOStream.cxx index ad610d608f..e8d4dc8657 100644 --- a/pandatool/src/assimp/pandaIOStream.cxx +++ b/pandatool/src/assimp/pandaIOStream.cxx @@ -27,10 +27,11 @@ PandaIOStream(std::istream &stream) : _istream(stream) { */ size_t PandaIOStream:: FileSize() const { + _istream.clear(); std::streampos cur = _istream.tellg(); _istream.seekg(0, ios::end); std::streampos end = _istream.tellg(); - _istream.seekg(cur, ios::beg); + _istream.seekg(cur); return end; } @@ -47,8 +48,14 @@ Flush() { */ size_t PandaIOStream:: Read(void *buffer, size_t size, size_t count) { - _istream.read((char*) buffer, size * count); - return _istream.gcount(); + _istream.read((char *)buffer, size * count); + + if (_istream.eof()) { + // Gracefully handle EOF. + _istream.clear(ios::eofbit); + } + + return _istream.gcount() / size; } /** From 5a483e389933d83ffdeef10d85cfc280225501ce Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Nov 2022 22:42:04 +0100 Subject: [PATCH 08/14] assimp: Fix a typo in a config var description --- pandatool/src/assimp/config_assimp.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandatool/src/assimp/config_assimp.cxx b/pandatool/src/assimp/config_assimp.cxx index 2b84d2c63b..78ea989e16 100644 --- a/pandatool/src/assimp/config_assimp.cxx +++ b/pandatool/src/assimp/config_assimp.cxx @@ -52,7 +52,7 @@ ConfigVariableBool assimp_fix_infacing_normals ConfigVariableBool assimp_optimize_meshes ("assimp-optimize-meshes", true, - PRC_DESC("Removes the number of draw calls by unifying geometry with the same " + PRC_DESC("Reduces the number of draw calls by unifying geometry with the same " "materials. Especially effective in conjunction with " "assimp-optimize-graph and assimp-remove-redundant-materials.")); From 162a7b2c3440d0cd0d6c4efb6419cb25ea9756b8 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Nov 2022 22:43:15 +0100 Subject: [PATCH 09/14] assimp: Support reading tangents and binormals --- pandatool/src/assimp/assimpLoader.cxx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pandatool/src/assimp/assimpLoader.cxx b/pandatool/src/assimp/assimpLoader.cxx index 0a5e84fa94..e6df2983c1 100644 --- a/pandatool/src/assimp/assimpLoader.cxx +++ b/pandatool/src/assimp/assimpLoader.cxx @@ -721,6 +721,10 @@ load_mesh(size_t index) { aformat->add_column(InternalName::get_texcoord_name(out.str()), 3, Geom::NT_stdfloat, Geom::C_texcoord); } } + if (mesh.HasTangentsAndBitangents()) { + aformat->add_column(InternalName::get_tangent(), 3, Geom::NT_stdfloat, Geom::C_vector); + aformat->add_column(InternalName::get_binormal(), 3, Geom::NT_stdfloat, Geom::C_vector); + } PT(GeomVertexArrayFormat) tb_aformat = new GeomVertexArrayFormat; tb_aformat->add_column(InternalName::make("transform_blend"), 1, Geom::NT_uint16, Geom::C_index); @@ -853,6 +857,18 @@ load_mesh(size_t index) { } } + // Now the tangents and bitangents, if any. + if (mesh.HasTangentsAndBitangents()) { + GeomVertexWriter tangent (vdata, InternalName::get_tangent()); + GeomVertexWriter binormal (vdata, InternalName::get_binormal()); + for (size_t i = 0; i < mesh.mNumVertices; ++i) { + const aiVector3D &tvec = mesh.mTangents[i]; + const aiVector3D &bvec = mesh.mBitangents[i]; + tangent.add_data3(tvec.x, tvec.y, tvec.z); + binormal.add_data3(bvec.x, bvec.y, bvec.z); + } + } + // Now the transform blend table if (character) { GeomVertexWriter transform_blend (vdata, InternalName::get_transform_blend()); From 286cf0d28654ee531396feed75ecbf059d7ac037 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Nov 2022 22:44:02 +0100 Subject: [PATCH 10/14] assimp: Improve performance of loading geometry --- pandatool/src/assimp/assimpLoader.cxx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pandatool/src/assimp/assimpLoader.cxx b/pandatool/src/assimp/assimpLoader.cxx index e6df2983c1..2a838ebf06 100644 --- a/pandatool/src/assimp/assimpLoader.cxx +++ b/pandatool/src/assimp/assimpLoader.cxx @@ -817,7 +817,7 @@ load_mesh(size_t index) { GeomVertexWriter vertex (vdata, InternalName::get_vertex()); for (size_t i = 0; i < mesh.mNumVertices; ++i) { const aiVector3D &vec = mesh.mVertices[i]; - vertex.add_data3(vec.x, vec.y, vec.z); + vertex.set_data3(vec.x, vec.y, vec.z); } // Now the normals, if any. @@ -825,7 +825,7 @@ load_mesh(size_t index) { GeomVertexWriter normal (vdata, InternalName::get_normal()); for (size_t i = 0; i < mesh.mNumVertices; ++i) { const aiVector3D &vec = mesh.mNormals[i]; - normal.add_data3(vec.x, vec.y, vec.z); + normal.set_data3(vec.x, vec.y, vec.z); } } @@ -834,7 +834,7 @@ load_mesh(size_t index) { GeomVertexWriter color (vdata, InternalName::get_color()); for (size_t i = 0; i < mesh.mNumVertices; ++i) { const aiColor4D &col = mesh.mColors[0][i]; - color.add_data4(col.r, col.g, col.b, col.a); + color.set_data4(col.r, col.g, col.b, col.a); } } @@ -844,7 +844,7 @@ load_mesh(size_t index) { GeomVertexWriter texcoord0 (vdata, InternalName::get_texcoord()); for (size_t i = 0; i < mesh.mNumVertices; ++i) { const aiVector3D &vec = mesh.mTextureCoords[0][i]; - texcoord0.add_data3(vec.x, vec.y, vec.z); + texcoord0.set_data3(vec.x, vec.y, vec.z); } for (unsigned int u = 1; u < num_uvs; ++u) { ostringstream out; @@ -852,7 +852,7 @@ load_mesh(size_t index) { GeomVertexWriter texcoord (vdata, InternalName::get_texcoord_name(out.str())); for (size_t i = 0; i < mesh.mNumVertices; ++i) { const aiVector3D &vec = mesh.mTextureCoords[u][i]; - texcoord.add_data3(vec.x, vec.y, vec.z); + texcoord.set_data3(vec.x, vec.y, vec.z); } } } @@ -864,8 +864,8 @@ load_mesh(size_t index) { for (size_t i = 0; i < mesh.mNumVertices; ++i) { const aiVector3D &tvec = mesh.mTangents[i]; const aiVector3D &bvec = mesh.mBitangents[i]; - tangent.add_data3(tvec.x, tvec.y, tvec.z); - binormal.add_data3(bvec.x, bvec.y, bvec.z); + tangent.set_data3(tvec.x, tvec.y, tvec.z); + binormal.set_data3(bvec.x, bvec.y, bvec.z); } } @@ -879,7 +879,7 @@ load_mesh(size_t index) { for (size_t j = 0; j < bone_weights[i].size(); ++j) { tblend.add_transform(bone_weights[i][j].joint_vertex_xform, bone_weights[i][j].weight); } - transform_blend.add_data1i(tbtable->add_blend(tblend)); + transform_blend.set_data1i(tbtable->add_blend(tblend)); } tbtable->set_rows(SparseArray::lower_on(vdata->get_num_rows())); From ffe2137680f90eddff7842aeda32bcdb9e1c233c Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Nov 2022 22:44:21 +0100 Subject: [PATCH 11/14] assimp: Support reading additional texture maps, as well as PBR --- pandatool/src/assimp/assimpLoader.cxx | 103 +++++++++++++++++++++++--- pandatool/src/assimp/assimpLoader.h | 4 +- 2 files changed, 94 insertions(+), 13 deletions(-) diff --git a/pandatool/src/assimp/assimpLoader.cxx b/pandatool/src/assimp/assimpLoader.cxx index 2a838ebf06..2ffb6fd237 100644 --- a/pandatool/src/assimp/assimpLoader.cxx +++ b/pandatool/src/assimp/assimpLoader.cxx @@ -45,10 +45,39 @@ #include +#ifndef AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0 +#endif + +#ifndef AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 +#endif + +#ifndef AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 +#endif + #ifndef AI_MATKEY_GLTF_ALPHAMODE #define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0 #endif +#ifndef AI_MATKEY_GLTF_ALPHACUTOFF +#define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0 +#endif + +// Older versions of Assimp used these glTF-specific keys instead. +#ifndef AI_MATKEY_BASE_COLOR +#define AI_MATKEY_BASE_COLOR AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR +#endif + +#ifndef AI_MATKEY_METALLIC_FACTOR +#define AI_MATKEY_METALLIC_FACTOR AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR +#endif + +#ifndef AI_MATKEY_ROUGHNESS_FACTOR +#define AI_MATKEY_ROUGHNESS_FACTOR AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR +#endif + using std::ostringstream; using std::stringstream; using std::string; @@ -294,7 +323,8 @@ load_texture(size_t index) { */ void AssimpLoader:: load_texture_stage(const aiMaterial &mat, const aiTextureType &ttype, - CPT(TextureAttrib) &tattr, CPT(TexMatrixAttrib) &tmattr) { + TextureStage::Mode mode, CPT(TextureAttrib) &tattr, + CPT(TexMatrixAttrib) &tmattr) { aiString path; aiTextureMapping mapping; unsigned int uvindex; @@ -312,13 +342,23 @@ load_texture_stage(const aiMaterial &mat, const aiTextureType &ttype, uvindex = i; } - stringstream str; - str << uvindex; - PT(TextureStage) stage = new TextureStage(str.str()); - if (uvindex > 0) { - stage->set_texcoord_name(InternalName::get_texcoord_name(str.str())); + if (ttype == aiTextureType_DIFFUSE && i == 1) { + // The glTF 2 importer duplicates this slot in older versions of Assimp. + // Since glTF doesn't support multiple diffuse textures anyway, we check + // for this old glTF-specific key, and if present, ignore this texture. + aiColor4D col; + if (AI_SUCCESS == mat.Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR, col)) { + return; + } } - PT(Texture) ptex = nullptr; + + std::string uvindex_str = format_string(uvindex); + PT(TextureStage) stage = new TextureStage(uvindex_str); + stage->set_mode(mode); + if (uvindex > 0) { + stage->set_texcoord_name(InternalName::get_texcoord_name(uvindex_str)); + } + PT(Texture) ptex; // I'm not sure if this is the right way to handle it, as I couldn't find // much information on embedded textures. @@ -448,7 +488,7 @@ load_material(size_t index) { CPT(RenderState) state = RenderState::make_empty(); - aiColor3D col; + aiColor4D col; bool have; int ival; PN_stdfloat fval; @@ -458,7 +498,11 @@ load_material(size_t index) { // First do the material attribute. PT(Material) pmat = new Material; have = false; - if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_DIFFUSE, col)) { + if (AI_SUCCESS == mat.Get(AI_MATKEY_BASE_COLOR, col)) { + pmat->set_base_color(LColor(col.r, col.g, col.b, col.a)); + have = true; + } + else if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_DIFFUSE, col)) { pmat->set_diffuse(LColor(col.r, col.g, col.b, 1)); have = true; } @@ -470,6 +514,13 @@ load_material(size_t index) { } have = true; } + //else { + // if (AI_SUCCESS == mat.Get(AI_MATKEY_SHININESS_STRENGTH, fval)) { + // pmat->set_specular(LColor(fval, fval, fval, 1)); + // } else { + // pmat->set_specular(LColor(1, 1, 1, 1)); + // } + //} if (AI_SUCCESS == mat.Get(AI_MATKEY_COLOR_AMBIENT, col)) { pmat->set_specular(LColor(col.r, col.g, col.b, 1)); have = true; @@ -485,6 +536,22 @@ load_material(size_t index) { pmat->set_shininess(fval); have = true; } + if (AI_SUCCESS == mat.Get(AI_MATKEY_METALLIC_FACTOR, fval)) { + pmat->set_metallic(fval); + have = true; + } + if (AI_SUCCESS == mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, fval)) { + pmat->set_roughness(fval); + have = true; + } + if (AI_SUCCESS == mat.Get(AI_MATKEY_REFRACTI, fval)) { + pmat->set_refractive_index(fval); + have = true; + } + else if (pmat->has_metallic()) { + // Default refractive index to 1.5 for PBR models + pmat->set_refractive_index(1.5); + } if (have) { state = state->add_attrib(MaterialAttrib::make(pmat)); } @@ -524,8 +591,20 @@ load_material(size_t index) { // And let's not forget the textures! CPT(TextureAttrib) tattr = DCAST(TextureAttrib, TextureAttrib::make()); CPT(TexMatrixAttrib) tmattr; - load_texture_stage(mat, aiTextureType_DIFFUSE, tattr, tmattr); - load_texture_stage(mat, aiTextureType_LIGHTMAP, tattr, tmattr); + load_texture_stage(mat, aiTextureType_DIFFUSE, TextureStage::M_modulate, tattr, tmattr); + + // Check for an ORM map, from the glTF/OBJ importer. glTF also puts it in the + // LIGHTMAP slot, despite only having the lightmap in the red channel, so we + // have to ignore it. + if (mat.GetTextureCount(aiTextureType_UNKNOWN) > 0) { + load_texture_stage(mat, aiTextureType_UNKNOWN, TextureStage::M_selector, tattr, tmattr); + } else { + load_texture_stage(mat, aiTextureType_LIGHTMAP, TextureStage::M_modulate, tattr, tmattr); + } + + load_texture_stage(mat, aiTextureType_NORMALS, TextureStage::M_normal, tattr, tmattr); + load_texture_stage(mat, aiTextureType_EMISSIVE, TextureStage::M_emission, tattr, tmattr); + load_texture_stage(mat, aiTextureType_HEIGHT, TextureStage::M_height, tattr, tmattr); if (tattr->get_num_on_stages() > 0) { state = state->add_attrib(tattr); } @@ -533,7 +612,7 @@ load_material(size_t index) { state = state->add_attrib(tmattr); } - _mat_states[index] = state; + _mat_states[index] = std::move(state); } /** diff --git a/pandatool/src/assimp/assimpLoader.h b/pandatool/src/assimp/assimpLoader.h index da60c36a3f..3b4854451d 100644 --- a/pandatool/src/assimp/assimpLoader.h +++ b/pandatool/src/assimp/assimpLoader.h @@ -18,6 +18,7 @@ #include "filename.h" #include "modelRoot.h" #include "texture.h" +#include "textureStage.h" #include "pmap.h" #include @@ -73,7 +74,8 @@ private: void load_texture(size_t index); void load_texture_stage(const aiMaterial &mat, const aiTextureType &ttype, - CPT(TextureAttrib) &tattr, CPT(TexMatrixAttrib) &tmattr); + TextureStage::Mode mode, CPT(TextureAttrib) &tattr, + CPT(TexMatrixAttrib) &tmattr); void load_material(size_t index); void create_joint(Character *character, CharacterJointBundle *bundle, PartGroup *parent, const aiNode &node); void create_anim_channel(const aiAnimation &anim, AnimBundle *bundle, AnimGroup *parent, const aiNode &node); From 2044861597e2780eed428e097440ccfd47634fc5 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Nov 2022 20:50:45 +0100 Subject: [PATCH 12/14] assimp: Support custom object properties as tags --- pandatool/src/assimp/assimpLoader.cxx | 39 +++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/pandatool/src/assimp/assimpLoader.cxx b/pandatool/src/assimp/assimpLoader.cxx index 2ffb6fd237..82383f0d32 100644 --- a/pandatool/src/assimp/assimpLoader.cxx +++ b/pandatool/src/assimp/assimpLoader.cxx @@ -39,6 +39,7 @@ #include "pvector.h" #include "cmath.h" #include "deg_2_rad.h" +#include "string_utils.h" #include "pandaIOSystem.h" #include "pandaLogger.h" @@ -1040,6 +1041,44 @@ load_node(const aiNode &node, PandaNode *parent) { parent->add_child(pnode); } + if (node.mMetaData != nullptr) { + for (unsigned i = 0; i < node.mMetaData->mNumProperties; ++i) { + const aiMetadataEntry &entry = node.mMetaData->mValues[i]; + std::string value; + switch (entry.mType) { + //case AI_BOOL: + // value = (*static_cast(entry.mData)) ? "1" : ""; + // break; + case (aiMetadataType)1: // AI_INT32 + value = format_string(*static_cast(entry.mData)); + break; + case AI_UINT64: + value = format_string(*static_cast(entry.mData)); + break; + case AI_FLOAT: + value = format_string(*static_cast(entry.mData)); + break; + case AI_AISTRING: + { + const aiString *str = static_cast(entry.mData); + value = std::string(str->data, str->length); + } + break; + default: + // Special case because AI_DOUBLE was added in Assimp 4.0 with the same + // value as AI_AISTRING. Defined as if so that we don't get a duplicate + // case error with moder ncompilers. + if (entry.mType == (aiMetadataType)4) { + value = format_string(*static_cast(entry.mData)); + break; + } + continue; + } + const aiString &key = node.mMetaData->mKeys[i]; + pnode->set_tag(std::string(key.data, key.length), std::move(value)); + } + } + // Load in the transformation matrix. const aiMatrix4x4 &t = node.mTransformation; if (!t.IsIdentity()) { From 1fd2e124cf7afddbab87ff2f771d18753cce3700 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 3 Nov 2022 13:14:07 +0100 Subject: [PATCH 13/14] assimp: Fix unprotected debug statements --- pandatool/src/assimp/assimpLoader.cxx | 77 ++++++++++++++++++--------- 1 file changed, 53 insertions(+), 24 deletions(-) diff --git a/pandatool/src/assimp/assimpLoader.cxx b/pandatool/src/assimp/assimpLoader.cxx index 82383f0d32..99407ccabe 100644 --- a/pandatool/src/assimp/assimpLoader.cxx +++ b/pandatool/src/assimp/assimpLoader.cxx @@ -269,8 +269,11 @@ load_texture(size_t index) { if (tex.mHeight == 0) { // Compressed texture. - assimp_cat.debug() - << "Reading embedded compressed texture with format " << tex.achFormatHint << " and size " << tex.mWidth << "\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "Reading embedded compressed texture with format " + << tex.achFormatHint << " and size " << tex.mWidth << "\n"; + } stringstream str; str.write((char*) tex.pcData, tex.mWidth); @@ -296,8 +299,11 @@ load_texture(size_t index) { } } } else { - assimp_cat.debug() - << "Reading embedded raw texture with size " << tex.mWidth << "x" << tex.mHeight << "\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "Reading embedded raw texture with size " + << tex.mWidth << "x" << tex.mHeight << "\n"; + } ptex->setup_2d_texture(tex.mWidth, tex.mHeight, Texture::T_unsigned_byte, Texture::F_rgba); PTA_uchar data = ptex->modify_ram_image(); @@ -628,8 +634,10 @@ create_joint(Character *character, CharacterJointBundle *bundle, PartGroup *pare t.a4, t.b4, t.c4, t.d4); PT(CharacterJoint) joint = new CharacterJoint(character, bundle, parent, node.mName.C_Str(), mat); - assimp_cat.debug() - << "Creating joint for: " << node.mName.C_Str() << "\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "Creating joint for: " << node.mName.C_Str() << "\n"; + } for (size_t i = 0; i < node.mNumChildren; ++i) { if (_bonemap.find(node.mChildren[i]->mName.C_Str()) != _bonemap.end()) { @@ -654,8 +662,10 @@ create_anim_channel(const aiAnimation &anim, AnimBundle *bundle, AnimGroup *pare } if (node_anim) { - assimp_cat.debug() - << "Found channel for node: " << node.mName.C_Str() << "\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "Found channel for node: " << node.mName.C_Str() << "\n"; + } // assimp_cat.debug() << "Num Position Keys " << // node_anim->mNumPositionKeys << "\n"; assimp_cat.debug() << "Num // Rotation Keys " << node_anim->mNumRotationKeys << "\n"; @@ -703,7 +713,7 @@ create_anim_channel(const aiAnimation &anim, AnimBundle *bundle, AnimGroup *pare group->set_table('j', tablej); group->set_table('k', tablek); } - else { + else if (assimp_cat.is_debug()) { assimp_cat.debug() << "No channel found for node: " << node.mName.C_Str() << "\n"; } @@ -726,8 +736,10 @@ load_mesh(size_t index) { // Check if we need to make a Character PT(Character) character = nullptr; if (mesh.HasBones()) { - assimp_cat.debug() - << "Creating character for " << mesh.mName.C_Str() << "\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "Creating character for " << mesh.mName.C_Str() << "\n"; + } // Find and add all bone nodes to the bone map for (size_t i = 0; i < mesh.mNumBones; ++i) { @@ -767,8 +779,10 @@ load_mesh(size_t index) { const aiBone &bone = *mesh.mBones[i]; CharacterJoint *joint = character->find_joint(bone.mName.C_Str()); if (joint == nullptr) { - assimp_cat.debug() - << "Could not find joint for bone: " << bone.mName.C_Str() << "\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "Could not find joint for bone: " << bone.mName.C_Str() << "\n"; + } continue; } @@ -814,11 +828,17 @@ load_mesh(size_t index) { aiAnimation &ai_anim = *_scene->mAnimations[i]; bool convert_anim = false; - assimp_cat.debug() - << "Checking to see if anim (" << ai_anim.mName.C_Str() << ") matches character (" << mesh.mName.C_Str() << ")\n"; - for (size_t j = 0; j < ai_anim.mNumChannels; ++j) { + if (assimp_cat.is_debug()) { assimp_cat.debug() - << "Searching for " << ai_anim.mChannels[j]->mNodeName.C_Str() << " in bone map" << "\n"; + << "Checking to see if anim (" << ai_anim.mName.C_Str() + << ") matches character (" << mesh.mName.C_Str() << ")\n"; + } + for (size_t j = 0; j < ai_anim.mNumChannels; ++j) { + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "Searching for " << ai_anim.mChannels[j]->mNodeName.C_Str() + << " in bone map" << "\n"; + } if (_bonemap.find(ai_anim.mChannels[j]->mNodeName.C_Str()) != _bonemap.end()) { convert_anim = true; break; @@ -826,8 +846,11 @@ load_mesh(size_t index) { } if (convert_anim) { - assimp_cat.debug() - << "Found animation (" << ai_anim.mName.C_Str() << ") for character (" << mesh.mName.C_Str() << ")\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "Found animation (" << ai_anim.mName.C_Str() << ") for character (" + << mesh.mName.C_Str() << ")\n"; + } // Now create the animation unsigned int frames = 0; @@ -843,10 +866,12 @@ load_mesh(size_t index) { } } PN_stdfloat fps = frames / (ai_anim.mTicksPerSecond * ai_anim.mDuration); - assimp_cat.debug() - << "FPS " << fps << "\n"; - assimp_cat.debug() - << "Frames " << frames << "\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() + << "FPS " << fps << "\n"; + assimp_cat.debug() + << "Frames " << frames << "\n"; + } PT(AnimBundle) bundle = new AnimBundle(mesh.mName.C_Str(), fps, frames); PT(AnimGroup) skeleton = new AnimGroup(bundle, ""); @@ -1112,7 +1137,9 @@ load_node(const aiNode &node, PandaNode *parent) { } if (character) { + if (assimp_cat.is_debug()) { assimp_cat.debug() << "Adding char to geom\n"; + } character->add_child(gnode); } } @@ -1124,7 +1151,9 @@ load_node(const aiNode &node, PandaNode *parent) { void AssimpLoader:: load_light(const aiLight &light) { string name (light.mName.data, light.mName.length); - assimp_cat.debug() << "Found light '" << name << "'\n"; + if (assimp_cat.is_debug()) { + assimp_cat.debug() << "Found light '" << name << "'\n"; + } aiColor3D col; aiVector3D vec; From db6ea00967bae67c16574dcef23997c55e701e6a Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 3 Nov 2022 13:14:35 +0100 Subject: [PATCH 14/14] assimp: Add assimp-collapse-dummy-root-node option This is false for now, but will be true in the future. See #366 --- pandatool/src/assimp/assimpLoader.cxx | 16 ++++++++++++++-- pandatool/src/assimp/config_assimp.cxx | 7 +++++++ pandatool/src/assimp/config_assimp.h | 1 + 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/pandatool/src/assimp/assimpLoader.cxx b/pandatool/src/assimp/assimpLoader.cxx index 99407ccabe..377a59b1c5 100644 --- a/pandatool/src/assimp/assimpLoader.cxx +++ b/pandatool/src/assimp/assimpLoader.cxx @@ -1056,13 +1056,25 @@ load_node(const aiNode &node, PandaNode *parent) { if (node.mNumMeshes > 0) { pnode = new GeomNode(name); } else { - pnode = new PandaNode(name); + // Many importers create a dummy root node, but they all call it + // differently, and some (glTF) create it only conditionally. + // It usually has some funny name like or $dummy_root, + // except the .obj loader, which assigns it the model base name like we do. + if (parent == _root && assimp_collapse_dummy_root_node && + _charmap.find(node.mName.C_Str()) == _charmap.end() && + (name.empty() || name[0] == '$' || name == "RootNode" || name == "ROOT" || name == "Root" || (name.size() > 2 && name[0] == '<' && name[name.size() - 1] == '>') || name == _root->get_name())) { + // Collapse root node. + pnode = _root; + } else { + pnode = new PandaNode(name); + } } if (_charmap.find(node.mName.C_Str()) != _charmap.end()) { character = _charmap[node.mName.C_Str()]; parent->add_child(character); - } else { + } + else if (parent != pnode) { parent->add_child(pnode); } diff --git a/pandatool/src/assimp/config_assimp.cxx b/pandatool/src/assimp/config_assimp.cxx index 78ea989e16..060b480d3a 100644 --- a/pandatool/src/assimp/config_assimp.cxx +++ b/pandatool/src/assimp/config_assimp.cxx @@ -86,6 +86,13 @@ ConfigVariableDouble assimp_smooth_normal_angle "normals. Note that you may need to clear the model-cache after " "changing this.")); +ConfigVariableBool assimp_collapse_dummy_root_node +("assimp-collapse-dummy-root-node", false, + PRC_DESC("If set to true, collapses the root node that Assimp creates, if it " + "appears to be a synthetic dummy root node and contains no meshes. " + "This variable is new as of Panda3D 1.10.13 and will become true by " + "default as of Panda3D 1.11.0.")); + /** * Initializes the library. This must be called at least once before any of * the functions or classes in this library can be used. Normally it will be diff --git a/pandatool/src/assimp/config_assimp.h b/pandatool/src/assimp/config_assimp.h index e17e2caae6..f999a60030 100644 --- a/pandatool/src/assimp/config_assimp.h +++ b/pandatool/src/assimp/config_assimp.h @@ -33,6 +33,7 @@ extern ConfigVariableBool assimp_optimize_graph; extern ConfigVariableBool assimp_flip_winding_order; extern ConfigVariableBool assimp_gen_normals; extern ConfigVariableDouble assimp_smooth_normal_angle; +extern ConfigVariableBool assimp_collapse_dummy_root_node; extern EXPCL_ASSIMP void init_libassimp();