123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743 |
- /*
- ---------------------------------------------------------------------------
- Open Asset Import Library (assimp)
- ---------------------------------------------------------------------------
- Copyright (c) 2006-2025, assimp team
- All rights reserved.
- Redistribution and use of this software in source and binary forms,
- with or without modification, are permitted provided that the following
- conditions are met:
- * Redistributions of source code must retain the above
- copyright notice, this list of conditions and the
- following disclaimer.
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the
- following disclaimer in the documentation and/or other
- materials provided with the distribution.
- * Neither the name of the assimp team, nor the names of its
- contributors may be used to endorse or promote products
- derived from this software without specific prior
- written permission of the assimp team.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- ---------------------------------------------------------------------------
- */
- /** @file B3DImporter.cpp
- * @brief Implementation of the b3d importer class
- */
- #ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
- // internal headers
- #include "AssetLib/B3D/B3DImporter.h"
- #include "PostProcessing/ConvertToLHProcess.h"
- #include "PostProcessing/TextureTransform.h"
- #include <assimp/StringUtils.h>
- #include <assimp/anim.h>
- #include <assimp/importerdesc.h>
- #include <assimp/scene.h>
- #include <assimp/DefaultLogger.hpp>
- #include <assimp/IOSystem.hpp>
- #include <memory>
- namespace Assimp {
- using namespace std;
- static constexpr aiImporterDesc desc = {
- "BlitzBasic 3D Importer",
- "",
- "",
- "http://www.blitzbasic.com/",
- aiImporterFlags_SupportBinaryFlavour,
- 0,
- 0,
- 0,
- 0,
- "b3d"
- };
- #ifdef _MSC_VER
- #pragma warning(disable : 4018)
- #endif
- // #define DEBUG_B3D
- template <typename T>
- void DeleteAllBarePointers(std::vector<T> &x) {
- for (auto p : x) {
- delete p;
- }
- }
- B3DImporter::~B3DImporter() = default;
- // ------------------------------------------------------------------------------------------------
- bool B3DImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const {
- size_t pos = pFile.find_last_of('.');
- if (pos == string::npos) {
- return false;
- }
- string ext = pFile.substr(pos + 1);
- if (ext.size() != 3) {
- return false;
- }
- return (ext[0] == 'b' || ext[0] == 'B') && (ext[1] == '3') && (ext[2] == 'd' || ext[2] == 'D');
- }
- // ------------------------------------------------------------------------------------------------
- // Loader meta information
- const aiImporterDesc *B3DImporter::GetInfo() const {
- return &desc;
- }
- // ------------------------------------------------------------------------------------------------
- void B3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
- std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
- // Check whether we can read from the file
- if (file == nullptr) {
- throw DeadlyImportError("Failed to open B3D file ", pFile, ".");
- }
- // check whether the .b3d file is large enough to contain
- // at least one chunk.
- size_t fileSize = file->FileSize();
- if (fileSize < 8) {
- throw DeadlyImportError("B3D File is too small.");
- }
- _pos = 0;
- _buf.resize(fileSize);
- file->Read(&_buf[0], 1, fileSize);
- _stack.clear();
- ReadBB3D(pScene);
- }
- // ------------------------------------------------------------------------------------------------
- AI_WONT_RETURN void B3DImporter::Oops() {
- throw DeadlyImportError("B3D Importer - INTERNAL ERROR");
- }
- // ------------------------------------------------------------------------------------------------
- AI_WONT_RETURN void B3DImporter::Fail(const string &str) {
- #ifdef DEBUG_B3D
- ASSIMP_LOG_ERROR("Error in B3D file data: ", str);
- #endif
- throw DeadlyImportError("B3D Importer - error in B3D file data: ", str);
- }
- // ------------------------------------------------------------------------------------------------
- int B3DImporter::ReadByte() {
- if (_pos >= _buf.size()) {
- Fail("EOF");
- }
- return _buf[_pos++];
- }
- // ------------------------------------------------------------------------------------------------
- int B3DImporter::ReadInt() {
- if (_pos + 4 > _buf.size()) {
- Fail("EOF");
- }
- int n;
- memcpy(&n, &_buf[_pos], 4);
- _pos += 4;
- return n;
- }
- // ------------------------------------------------------------------------------------------------
- float B3DImporter::ReadFloat() {
- if (_pos + 4 > _buf.size()) {
- Fail("EOF");
- }
- float n;
- memcpy(&n, &_buf[_pos], 4);
- _pos += 4;
- return n;
- }
- // ------------------------------------------------------------------------------------------------
- aiVector2D B3DImporter::ReadVec2() {
- float x = ReadFloat();
- float y = ReadFloat();
- return aiVector2D(x, y);
- }
- // ------------------------------------------------------------------------------------------------
- aiVector3D B3DImporter::ReadVec3() {
- float x = ReadFloat();
- float y = ReadFloat();
- float z = ReadFloat();
- return aiVector3D(x, y, z);
- }
- // ------------------------------------------------------------------------------------------------
- aiQuaternion B3DImporter::ReadQuat() {
- // (aramis_acg) Fix to adapt the loader to changed quat orientation
- float w = -ReadFloat();
- float x = ReadFloat();
- float y = ReadFloat();
- float z = ReadFloat();
- return aiQuaternion(w, x, y, z);
- }
- // ------------------------------------------------------------------------------------------------
- string B3DImporter::ReadString() {
- if (_pos > _buf.size()) {
- Fail("EOF");
- }
- string str;
- while (_pos < _buf.size()) {
- char c = (char)ReadByte();
- if (!c) {
- return str;
- }
- str += c;
- }
- return string();
- }
- // ------------------------------------------------------------------------------------------------
- string B3DImporter::ReadChunk() {
- string tag;
- for (int i = 0; i < 4; ++i) {
- tag += char(ReadByte());
- }
- #ifdef DEBUG_B3D
- ASSIMP_LOG_DEBUG("ReadChunk: ", tag);
- #endif
- unsigned sz = (unsigned)ReadInt();
- _stack.push_back(_pos + sz);
- return tag;
- }
- // ------------------------------------------------------------------------------------------------
- void B3DImporter::ExitChunk() {
- _pos = _stack.back();
- _stack.pop_back();
- }
- // ------------------------------------------------------------------------------------------------
- size_t B3DImporter::ChunkSize() {
- return _stack.back() - _pos;
- }
- // ------------------------------------------------------------------------------------------------
- template <class T>
- T *B3DImporter::to_array(const vector<T> &v) {
- if (v.empty()) {
- return nullptr;
- }
- T *p = new T[v.size()];
- for (size_t i = 0; i < v.size(); ++i) {
- p[i] = v[i];
- }
- return p;
- }
- // ------------------------------------------------------------------------------------------------
- template <class T>
- T **unique_to_array(vector<std::unique_ptr<T>> &v) {
- if (v.empty()) {
- return nullptr;
- }
- T **p = new T *[v.size()];
- for (size_t i = 0; i < v.size(); ++i) {
- p[i] = v[i].release();
- }
- return p;
- }
- // ------------------------------------------------------------------------------------------------
- void B3DImporter::ReadTEXS() {
- while (ChunkSize()) {
- string name = ReadString();
- /*int flags=*/ReadInt();
- /*int blend=*/ReadInt();
- /*aiVector2D pos=*/ReadVec2();
- /*aiVector2D scale=*/ReadVec2();
- /*float rot=*/ReadFloat();
- _textures.push_back(name);
- }
- }
- // ------------------------------------------------------------------------------------------------
- void B3DImporter::ReadBRUS() {
- int n_texs = ReadInt();
- if (n_texs < 0 || n_texs > 8) {
- Fail("Bad texture count");
- }
- while (ChunkSize()) {
- string name = ReadString();
- aiVector3D color = ReadVec3();
- float alpha = ReadFloat();
- float shiny = ReadFloat();
- /*int blend=**/ ReadInt();
- int fx = ReadInt();
- std::unique_ptr<aiMaterial> mat(new aiMaterial);
- // Name
- aiString ainame(name);
- mat->AddProperty(&ainame, AI_MATKEY_NAME);
- // Diffuse color
- mat->AddProperty(&color, 1, AI_MATKEY_COLOR_DIFFUSE);
- // Opacity
- mat->AddProperty(&alpha, 1, AI_MATKEY_OPACITY);
- // Specular color
- aiColor3D speccolor(shiny, shiny, shiny);
- mat->AddProperty(&speccolor, 1, AI_MATKEY_COLOR_SPECULAR);
- // Specular power
- float specpow = shiny * 128;
- mat->AddProperty(&specpow, 1, AI_MATKEY_SHININESS);
- // Double sided
- if (fx & 0x10) {
- int i = 1;
- mat->AddProperty(&i, 1, AI_MATKEY_TWOSIDED);
- }
- // Textures
- for (int i = 0; i < n_texs; ++i) {
- int texid = ReadInt();
- if (texid < -1 || (texid >= 0 && texid >= static_cast<int>(_textures.size()))) {
- Fail("Bad texture id");
- }
- if (i == 0 && texid >= 0) {
- aiString texname(_textures[texid]);
- mat->AddProperty(&texname, AI_MATKEY_TEXTURE_DIFFUSE(0));
- }
- }
- _materials.emplace_back(std::move(mat));
- }
- }
- // ------------------------------------------------------------------------------------------------
- void B3DImporter::ReadVRTS() {
- _vflags = ReadInt();
- _tcsets = ReadInt();
- _tcsize = ReadInt();
- if (_tcsets < 0 || _tcsets > 4 || _tcsize < 0 || _tcsize > 4) {
- Fail("Bad texcoord data");
- }
- int sz = 12 + (_vflags & 1 ? 12 : 0) + (_vflags & 2 ? 16 : 0) + (_tcsets * _tcsize * 4);
- size_t n_verts = ChunkSize() / sz;
- int v0 = static_cast<int>(_vertices.size());
- _vertices.resize(v0 + n_verts);
- for (unsigned int i = 0; i < n_verts; ++i) {
- Vertex &v = _vertices[v0 + i];
- memset(v.bones, 0, sizeof(v.bones));
- memset(v.weights, 0, sizeof(v.weights));
- v.vertex = ReadVec3();
- if (_vflags & 1) {
- v.normal = ReadVec3();
- }
- if (_vflags & 2) {
- ReadQuat(); // skip v 4bytes...
- }
- for (int j = 0; j < _tcsets; ++j) {
- float t[4] = { 0, 0, 0, 0 };
- for (int k = 0; k < _tcsize; ++k) {
- t[k] = ReadFloat();
- }
- t[1] = 1 - t[1];
- if (!j) {
- v.texcoords = aiVector3D(t[0], t[1], t[2]);
- }
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- void B3DImporter::ReadTRIS(int v0) {
- int matid = ReadInt();
- if (matid == -1) {
- matid = 0;
- } else if (matid < 0 || matid >= (int)_materials.size()) {
- #ifdef DEBUG_B3D
- ASSIMP_LOG_ERROR("material id=", matid);
- #endif
- Fail("Bad material id");
- }
- std::unique_ptr<aiMesh> mesh(new aiMesh);
- mesh->mMaterialIndex = matid;
- mesh->mNumFaces = 0;
- mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
- size_t n_tris = ChunkSize() / 12;
- aiFace *face = mesh->mFaces = new aiFace[n_tris];
- for (unsigned int i = 0; i < n_tris; ++i) {
- int i0 = ReadInt() + v0;
- int i1 = ReadInt() + v0;
- int i2 = ReadInt() + v0;
- if (i0 < 0 || i0 >= (int)_vertices.size() || i1 < 0 || i1 >= (int)_vertices.size() || i2 < 0 || i2 >= (int)_vertices.size()) {
- #ifdef DEBUG_B3D
- ASSIMP_LOG_ERROR("Bad triangle index: i0=", i0, ", i1=", i1, ", i2=", i2);
- #endif
- Fail("Bad triangle index");
- }
- face->mNumIndices = 3;
- face->mIndices = new unsigned[3];
- face->mIndices[0] = i0;
- face->mIndices[1] = i1;
- face->mIndices[2] = i2;
- ++mesh->mNumFaces;
- ++face;
- }
- _meshes.emplace_back(std::move(mesh));
- }
- // ------------------------------------------------------------------------------------------------
- void B3DImporter::ReadMESH() {
- /*int matid=*/ReadInt();
- int v0 = static_cast<int>(_vertices.size());
- while (ChunkSize()) {
- string t = ReadChunk();
- if (t == "VRTS") {
- ReadVRTS();
- } else if (t == "TRIS") {
- ReadTRIS(v0);
- }
- ExitChunk();
- }
- }
- // ------------------------------------------------------------------------------------------------
- void B3DImporter::ReadBONE(int id) {
- while (ChunkSize()) {
- int vertex = ReadInt();
- float weight = ReadFloat();
- if (vertex < 0 || vertex >= (int)_vertices.size()) {
- Fail("Bad vertex index");
- }
- Vertex &v = _vertices[vertex];
- for (int i = 0; i < 4; ++i) {
- if (!v.weights[i]) {
- v.bones[i] = static_cast<unsigned char>(id);
- v.weights[i] = weight;
- break;
- }
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- void B3DImporter::ReadKEYS(aiNodeAnim *nodeAnim) {
- vector<aiVectorKey> trans, scale;
- vector<aiQuatKey> rot;
- int flags = ReadInt();
- while (ChunkSize()) {
- int frame = ReadInt();
- if (flags & 1) {
- trans.emplace_back(frame, ReadVec3());
- }
- if (flags & 2) {
- scale.emplace_back(frame, ReadVec3());
- }
- if (flags & 4) {
- rot.emplace_back(frame, ReadQuat());
- }
- }
- if (flags & 1) {
- nodeAnim->mNumPositionKeys = static_cast<unsigned int>(trans.size());
- nodeAnim->mPositionKeys = to_array(trans);
- }
- if (flags & 2) {
- nodeAnim->mNumScalingKeys = static_cast<unsigned int>(scale.size());
- nodeAnim->mScalingKeys = to_array(scale);
- }
- if (flags & 4) {
- nodeAnim->mNumRotationKeys = static_cast<unsigned int>(rot.size());
- nodeAnim->mRotationKeys = to_array(rot);
- }
- }
- // ------------------------------------------------------------------------------------------------
- void B3DImporter::ReadANIM() {
- /*int flags=*/ReadInt();
- int frames = ReadInt();
- float fps = ReadFloat();
- std::unique_ptr<aiAnimation> anim(new aiAnimation);
- anim->mDuration = frames;
- anim->mTicksPerSecond = fps;
- _animations.emplace_back(std::move(anim));
- }
- // ------------------------------------------------------------------------------------------------
- aiNode *B3DImporter::ReadNODE(aiNode *parent) {
- string name = ReadString();
- aiVector3D t = ReadVec3();
- aiVector3D s = ReadVec3();
- aiQuaternion r = ReadQuat();
- aiMatrix4x4 trans, scale, rot;
- aiMatrix4x4::Translation(t, trans);
- aiMatrix4x4::Scaling(s, scale);
- rot = aiMatrix4x4(r.GetMatrix());
- aiMatrix4x4 tform = trans * rot * scale;
- int nodeid = static_cast<int>(_nodes.size());
- aiNode *node = new aiNode(name);
- _nodes.push_back(node);
- node->mParent = parent;
- node->mTransformation = tform;
- std::unique_ptr<aiNodeAnim> nodeAnim;
- vector<unsigned> meshes;
- vector<aiNode *> children;
- while (ChunkSize()) {
- const string chunk = ReadChunk();
- if (chunk == "MESH") {
- unsigned int n = static_cast<unsigned int>(_meshes.size());
- ReadMESH();
- for (unsigned int i = n; i < static_cast<unsigned int>(_meshes.size()); ++i) {
- meshes.push_back(i);
- }
- } else if (chunk == "BONE") {
- ReadBONE(nodeid);
- } else if (chunk == "ANIM") {
- ReadANIM();
- } else if (chunk == "KEYS") {
- if (!nodeAnim) {
- nodeAnim.reset(new aiNodeAnim);
- nodeAnim->mNodeName = node->mName;
- }
- ReadKEYS(nodeAnim.get());
- } else if (chunk == "NODE") {
- aiNode *child = ReadNODE(node);
- children.push_back(child);
- }
- ExitChunk();
- }
- if (nodeAnim) {
- _nodeAnims.emplace_back(std::move(nodeAnim));
- }
- node->mNumMeshes = static_cast<unsigned int>(meshes.size());
- node->mMeshes = to_array(meshes);
- node->mNumChildren = static_cast<unsigned int>(children.size());
- node->mChildren = to_array(children);
- return node;
- }
- // ------------------------------------------------------------------------------------------------
- void B3DImporter::ReadBB3D(aiScene *scene) {
- _textures.clear();
- _materials.clear();
- _vertices.clear();
- _meshes.clear();
- DeleteAllBarePointers(_nodes);
- _nodes.clear();
- _nodeAnims.clear();
- _animations.clear();
- string t = ReadChunk();
- if (t == "BB3D") {
- int version = ReadInt();
- if (!DefaultLogger::isNullLogger()) {
- char dmp[128];
- ai_snprintf(dmp, 128, "B3D file format version: %i", version);
- ASSIMP_LOG_INFO(dmp);
- }
- while (ChunkSize()) {
- const string chunk = ReadChunk();
- if (chunk == "TEXS") {
- ReadTEXS();
- } else if (chunk == "BRUS") {
- ReadBRUS();
- } else if (chunk == "NODE") {
- ReadNODE(nullptr);
- }
- ExitChunk();
- }
- }
- ExitChunk();
- if (!_nodes.size()) {
- Fail("No nodes");
- }
- if (!_meshes.size()) {
- Fail("No meshes");
- }
- // Fix nodes/meshes/bones
- for (size_t i = 0; i < _nodes.size(); ++i) {
- aiNode *node = _nodes[i];
- for (size_t j = 0; j < node->mNumMeshes; ++j) {
- aiMesh *mesh = _meshes[node->mMeshes[j]].get();
- int n_tris = mesh->mNumFaces;
- int n_verts = mesh->mNumVertices = n_tris * 3;
- aiVector3D *mv = mesh->mVertices = new aiVector3D[n_verts], *mn = nullptr, *mc = nullptr;
- if (_vflags & 1) {
- mn = mesh->mNormals = new aiVector3D[n_verts];
- }
- if (_tcsets) {
- mc = mesh->mTextureCoords[0] = new aiVector3D[n_verts];
- }
- aiFace *face = mesh->mFaces;
- vector<vector<aiVertexWeight>> vweights(_nodes.size());
- for (int vertIdx = 0; vertIdx < n_verts; vertIdx += 3) {
- for (int faceIndex = 0; faceIndex < 3; ++faceIndex) {
- Vertex &v = _vertices[face->mIndices[faceIndex]];
- *mv++ = v.vertex;
- if (mn) *mn++ = v.normal;
- if (mc) *mc++ = v.texcoords;
- face->mIndices[faceIndex] = vertIdx + faceIndex;
- for (int k = 0; k < 4; ++k) {
- if (!v.weights[k])
- break;
- int bone = v.bones[k];
- float weight = v.weights[k];
- vweights[bone].emplace_back(vertIdx + faceIndex, weight);
- }
- }
- ++face;
- }
- vector<aiBone *> bones;
- for (size_t weightIndx = 0; weightIndx < vweights.size(); ++weightIndx) {
- vector<aiVertexWeight> &weights = vweights[weightIndx];
- if (!weights.size()) {
- continue;
- }
- aiBone *bone = new aiBone;
- bones.push_back(bone);
- aiNode *bnode = _nodes[weightIndx];
- bone->mName = bnode->mName;
- bone->mNumWeights = static_cast<unsigned int>(weights.size());
- bone->mWeights = to_array(weights);
- aiMatrix4x4 mat = bnode->mTransformation;
- while (bnode->mParent) {
- bnode = bnode->mParent;
- mat = bnode->mTransformation * mat;
- }
- bone->mOffsetMatrix = mat.Inverse();
- }
- mesh->mNumBones = static_cast<unsigned int>(bones.size());
- mesh->mBones = to_array(bones);
- }
- }
- // nodes
- scene->mRootNode = _nodes[0];
- _nodes.clear(); // node ownership now belongs to scene
- // material
- if (!_materials.size()) {
- _materials.emplace_back(std::unique_ptr<aiMaterial>(new aiMaterial));
- }
- scene->mNumMaterials = static_cast<unsigned int>(_materials.size());
- scene->mMaterials = unique_to_array(_materials);
- // meshes
- scene->mNumMeshes = static_cast<unsigned int>(_meshes.size());
- scene->mMeshes = unique_to_array(_meshes);
- // animations
- if (_animations.size() == 1 && _nodeAnims.size()) {
- aiAnimation *anim = _animations.back().get();
- anim->mNumChannels = static_cast<unsigned int>(_nodeAnims.size());
- anim->mChannels = unique_to_array(_nodeAnims);
- scene->mNumAnimations = static_cast<unsigned int>(_animations.size());
- scene->mAnimations = unique_to_array(_animations);
- }
- // convert to RH
- MakeLeftHandedProcess makeleft;
- makeleft.Execute(scene);
- FlipWindingOrderProcess flip;
- flip.Execute(scene);
- }
- } // namespace Assimp
- #endif // !! ASSIMP_BUILD_NO_B3D_IMPORTER
|