From db81b925d776103326128bf629cbdda576a223e7 Mon Sep 17 00:00:00 2001 From: sanine Date: Sat, 16 Apr 2022 11:55:09 -0500 Subject: move 3rd-party librarys into libs/ and add built-in honeysuckle --- libs/assimp/code/AssetLib/AC/ACLoader.cpp | 865 ++++++++++++++++++++++++++++++ libs/assimp/code/AssetLib/AC/ACLoader.h | 270 ++++++++++ 2 files changed, 1135 insertions(+) create mode 100644 libs/assimp/code/AssetLib/AC/ACLoader.cpp create mode 100644 libs/assimp/code/AssetLib/AC/ACLoader.h (limited to 'libs/assimp/code/AssetLib/AC') diff --git a/libs/assimp/code/AssetLib/AC/ACLoader.cpp b/libs/assimp/code/AssetLib/AC/ACLoader.cpp new file mode 100644 index 0000000..e93624b --- /dev/null +++ b/libs/assimp/code/AssetLib/AC/ACLoader.cpp @@ -0,0 +1,865 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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 Implementation of the AC3D importer class */ + +#ifndef ASSIMP_BUILD_NO_AC_IMPORTER + +// internal headers +#include "ACLoader.h" +#include "Common/Importer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Assimp; + +static const aiImporterDesc desc = { + "AC3D Importer", + "", + "", + "", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "ac acc ac3d" +}; + +// ------------------------------------------------------------------------------------------------ +// skip to the next token +inline const char *AcSkipToNextToken(const char *buffer) { + if (!SkipSpaces(&buffer)) { + ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL"); + } + return buffer; +} + +// ------------------------------------------------------------------------------------------------ +// read a string (may be enclosed in double quotation marks). buffer must point to " +inline const char *AcGetString(const char *buffer, std::string &out) { + if (*buffer == '\0') { + throw DeadlyImportError("AC3D: Unexpected EOF in string"); + } + ++buffer; + const char *sz = buffer; + while ('\"' != *buffer) { + if (IsLineEnd(*buffer)) { + ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL in string"); + out = "ERROR"; + break; + } + ++buffer; + } + if (IsLineEnd(*buffer)) { + return buffer; + } + out = std::string(sz, (unsigned int)(buffer - sz)); + ++buffer; + + return buffer; +} + +// ------------------------------------------------------------------------------------------------ +// read 1 to n floats prefixed with an optional predefined identifier +template +inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *name, size_t name_length, size_t num, T *out) { + buffer = AcSkipToNextToken(buffer); + if (0 != name_length) { + if (0 != strncmp(buffer, name, name_length) || !IsSpace(buffer[name_length])) { + ASSIMP_LOG_ERROR("AC3D: Unexpected token. ", name, " was expected."); + return buffer; + } + buffer += name_length + 1; + } + for (unsigned int _i = 0; _i < num; ++_i) { + buffer = AcSkipToNextToken(buffer); + buffer = fast_atoreal_move(buffer, ((float *)out)[_i]); + } + + return buffer; +} + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +AC3DImporter::AC3DImporter() : + buffer(), + configSplitBFCull(), + configEvalSubdivision(), + mNumMeshes(), + mLights(), + mLightsCounter(0), + mGroupsCounter(0), + mPolysCounter(0), + mWorldsCounter(0) { + // nothing to be done here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +AC3DImporter::~AC3DImporter() { + // nothing to be done here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool AC3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const uint32_t tokens[] = { AI_MAKE_MAGIC("AC3D") }; + return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); +} + +// ------------------------------------------------------------------------------------------------ +// Loader meta information +const aiImporterDesc *AC3DImporter::GetInfo() const { + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Get a pointer to the next line from the file +bool AC3DImporter::GetNextLine() { + SkipLine(&buffer); + return SkipSpaces(&buffer); +} + +// ------------------------------------------------------------------------------------------------ +// Parse an object section in an AC file +void AC3DImporter::LoadObjectSection(std::vector &objects) { + if (!TokenMatch(buffer, "OBJECT", 6)) + return; + + SkipSpaces(&buffer); + + ++mNumMeshes; + + objects.push_back(Object()); + Object &obj = objects.back(); + + aiLight *light = nullptr; + if (!ASSIMP_strincmp(buffer, "light", 5)) { + // This is a light source. Add it to the list + mLights->push_back(light = new aiLight()); + + // Return a point light with no attenuation + light->mType = aiLightSource_POINT; + light->mColorDiffuse = light->mColorSpecular = aiColor3D(1.f, 1.f, 1.f); + light->mAttenuationConstant = 1.f; + + // Generate a default name for both the light source and the node + // FIXME - what's the right way to print a size_t? Is 'zu' universally available? stick with the safe version. + light->mName.length = ::ai_snprintf(light->mName.data, MAXLEN, "ACLight_%i", static_cast(mLights->size()) - 1); + obj.name = std::string(light->mName.data); + + ASSIMP_LOG_VERBOSE_DEBUG("AC3D: Light source encountered"); + obj.type = Object::Light; + } else if (!ASSIMP_strincmp(buffer, "group", 5)) { + obj.type = Object::Group; + } else if (!ASSIMP_strincmp(buffer, "world", 5)) { + obj.type = Object::World; + } else + obj.type = Object::Poly; + while (GetNextLine()) { + if (TokenMatch(buffer, "kids", 4)) { + SkipSpaces(&buffer); + unsigned int num = strtoul10(buffer, &buffer); + GetNextLine(); + if (num) { + // load the children of this object recursively + obj.children.reserve(num); + for (unsigned int i = 0; i < num; ++i) + LoadObjectSection(obj.children); + } + return; + } else if (TokenMatch(buffer, "name", 4)) { + SkipSpaces(&buffer); + buffer = AcGetString(buffer, obj.name); + + // If this is a light source, we'll also need to store + // the name of the node in it. + if (light) { + light->mName.Set(obj.name); + } + } else if (TokenMatch(buffer, "texture", 7)) { + SkipSpaces(&buffer); + buffer = AcGetString(buffer, obj.texture); + } else if (TokenMatch(buffer, "texrep", 6)) { + SkipSpaces(&buffer); + buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 2, &obj.texRepeat); + if (!obj.texRepeat.x || !obj.texRepeat.y) + obj.texRepeat = aiVector2D(1.f, 1.f); + } else if (TokenMatch(buffer, "texoff", 6)) { + SkipSpaces(&buffer); + buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 2, &obj.texOffset); + } else if (TokenMatch(buffer, "rot", 3)) { + SkipSpaces(&buffer); + buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 9, &obj.rotation); + } else if (TokenMatch(buffer, "loc", 3)) { + SkipSpaces(&buffer); + buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 3, &obj.translation); + } else if (TokenMatch(buffer, "subdiv", 6)) { + SkipSpaces(&buffer); + obj.subDiv = strtoul10(buffer, &buffer); + } else if (TokenMatch(buffer, "crease", 6)) { + SkipSpaces(&buffer); + obj.crease = fast_atof(buffer); + } else if (TokenMatch(buffer, "numvert", 7)) { + SkipSpaces(&buffer); + + unsigned int t = strtoul10(buffer, &buffer); + if (t >= AI_MAX_ALLOC(aiVector3D)) { + throw DeadlyImportError("AC3D: Too many vertices, would run out of memory"); + } + obj.vertices.reserve(t); + for (unsigned int i = 0; i < t; ++i) { + if (!GetNextLine()) { + ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: not all vertices have been parsed yet"); + break; + } else if (!IsNumeric(*buffer)) { + ASSIMP_LOG_ERROR("AC3D: Unexpected token: not all vertices have been parsed yet"); + --buffer; // make sure the line is processed a second time + break; + } + obj.vertices.push_back(aiVector3D()); + aiVector3D &v = obj.vertices.back(); + buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 3, &v.x); + } + } else if (TokenMatch(buffer, "numsurf", 7)) { + SkipSpaces(&buffer); + + bool Q3DWorkAround = false; + + const unsigned int t = strtoul10(buffer, &buffer); + obj.surfaces.reserve(t); + for (unsigned int i = 0; i < t; ++i) { + GetNextLine(); + if (!TokenMatch(buffer, "SURF", 4)) { + // FIX: this can occur for some files - Quick 3D for + // example writes no surf chunks + if (!Q3DWorkAround) { + ASSIMP_LOG_WARN("AC3D: SURF token was expected"); + ASSIMP_LOG_VERBOSE_DEBUG("Continuing with Quick3D Workaround enabled"); + } + --buffer; // make sure the line is processed a second time + // break; --- see fix notes above + + Q3DWorkAround = true; + } + SkipSpaces(&buffer); + obj.surfaces.push_back(Surface()); + Surface &surf = obj.surfaces.back(); + surf.flags = strtoul_cppstyle(buffer); + + while (1) { + if (!GetNextLine()) { + throw DeadlyImportError("AC3D: Unexpected EOF: surface is incomplete"); + } + if (TokenMatch(buffer, "mat", 3)) { + SkipSpaces(&buffer); + surf.mat = strtoul10(buffer); + } else if (TokenMatch(buffer, "refs", 4)) { + // --- see fix notes above + if (Q3DWorkAround) { + if (!surf.entries.empty()) { + buffer -= 6; + break; + } + } + + SkipSpaces(&buffer); + const unsigned int m = strtoul10(buffer); + surf.entries.reserve(m); + + obj.numRefs += m; + + for (unsigned int k = 0; k < m; ++k) { + if (!GetNextLine()) { + ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: surface references are incomplete"); + break; + } + surf.entries.push_back(Surface::SurfaceEntry()); + Surface::SurfaceEntry &entry = surf.entries.back(); + + entry.first = strtoul10(buffer, &buffer); + SkipSpaces(&buffer); + buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 2, &entry.second); + } + } else { + --buffer; // make sure the line is processed a second time + break; + } + } + } + } + } + ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: \'kids\' line was expected"); +} + +// ------------------------------------------------------------------------------------------------ +// Convert a material from AC3DImporter::Material to aiMaterial +void AC3DImporter::ConvertMaterial(const Object &object, + const Material &matSrc, + aiMaterial &matDest) { + aiString s; + + if (matSrc.name.length()) { + s.Set(matSrc.name); + matDest.AddProperty(&s, AI_MATKEY_NAME); + } + if (object.texture.length()) { + s.Set(object.texture); + matDest.AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0)); + + // UV transformation + if (1.f != object.texRepeat.x || 1.f != object.texRepeat.y || + object.texOffset.x || object.texOffset.y) { + aiUVTransform transform; + transform.mScaling = object.texRepeat; + transform.mTranslation = object.texOffset; + matDest.AddProperty(&transform, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0)); + } + } + + matDest.AddProperty(&matSrc.rgb, 1, AI_MATKEY_COLOR_DIFFUSE); + matDest.AddProperty(&matSrc.amb, 1, AI_MATKEY_COLOR_AMBIENT); + matDest.AddProperty(&matSrc.emis, 1, AI_MATKEY_COLOR_EMISSIVE); + matDest.AddProperty(&matSrc.spec, 1, AI_MATKEY_COLOR_SPECULAR); + + int n = -1; + if (matSrc.shin) { + n = aiShadingMode_Phong; + matDest.AddProperty(&matSrc.shin, 1, AI_MATKEY_SHININESS); + } else { + n = aiShadingMode_Gouraud; + } + matDest.AddProperty(&n, 1, AI_MATKEY_SHADING_MODEL); + + float f = 1.f - matSrc.trans; + matDest.AddProperty(&f, 1, AI_MATKEY_OPACITY); +} + +// ------------------------------------------------------------------------------------------------ +// Converts the loaded data to the internal verbose representation +aiNode *AC3DImporter::ConvertObjectSection(Object &object, + std::vector &meshes, + std::vector &outMaterials, + const std::vector &materials, + aiNode *parent) { + aiNode *node = new aiNode(); + node->mParent = parent; + if (object.vertices.size()) { + if (!object.surfaces.size() || !object.numRefs) { + /* " An object with 7 vertices (no surfaces, no materials defined). + This is a good way of getting point data into AC3D. + The Vertex->create convex-surface/object can be used on these + vertices to 'wrap' a 3d shape around them " + (http://www.opencity.info/html/ac3dfileformat.html) + + therefore: if no surfaces are defined return point data only + */ + + ASSIMP_LOG_INFO("AC3D: No surfaces defined in object definition, " + "a point list is returned"); + + meshes.push_back(new aiMesh()); + aiMesh *mesh = meshes.back(); + + mesh->mNumFaces = mesh->mNumVertices = (unsigned int)object.vertices.size(); + aiFace *faces = mesh->mFaces = new aiFace[mesh->mNumFaces]; + aiVector3D *verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + + for (unsigned int i = 0; i < mesh->mNumVertices; ++i, ++faces, ++verts) { + *verts = object.vertices[i]; + faces->mNumIndices = 1; + faces->mIndices = new unsigned int[1]; + faces->mIndices[0] = i; + } + + // use the primary material in this case. this should be the + // default material if all objects of the file contain points + // and no faces. + mesh->mMaterialIndex = 0; + outMaterials.push_back(new aiMaterial()); + ConvertMaterial(object, materials[0], *outMaterials.back()); + } else { + // need to generate one or more meshes for this object. + // find out how many different materials we have + typedef std::pair IntPair; + typedef std::vector MatTable; + MatTable needMat(materials.size(), IntPair(0, 0)); + + std::vector::iterator it, end = object.surfaces.end(); + std::vector::iterator it2, end2; + + for (it = object.surfaces.begin(); it != end; ++it) { + unsigned int idx = (*it).mat; + if (idx >= needMat.size()) { + ASSIMP_LOG_ERROR("AC3D: material index is out of range"); + idx = 0; + } + if ((*it).entries.empty()) { + ASSIMP_LOG_WARN("AC3D: surface her zero vertex references"); + } + + // validate all vertex indices to make sure we won't crash here + for (it2 = (*it).entries.begin(), + end2 = (*it).entries.end(); + it2 != end2; ++it2) { + if ((*it2).first >= object.vertices.size()) { + ASSIMP_LOG_WARN("AC3D: Invalid vertex reference"); + (*it2).first = 0; + } + } + + if (!needMat[idx].first) { + ++node->mNumMeshes; + } + + switch ((*it).GetType()) { + // closed line + case Surface::ClosedLine: + needMat[idx].first += (unsigned int)(*it).entries.size(); + needMat[idx].second += (unsigned int)(*it).entries.size() << 1u; + break; + + // unclosed line + case Surface::OpenLine: + needMat[idx].first += (unsigned int)(*it).entries.size() - 1; + needMat[idx].second += ((unsigned int)(*it).entries.size() - 1) << 1u; + break; + + // triangle strip + case Surface::TriangleStrip: + needMat[idx].first += (unsigned int)(*it).entries.size() - 2; + needMat[idx].second += ((unsigned int)(*it).entries.size() - 2) * 3; + break; + + default: + // Coerce unknowns to a polygon and warn + ASSIMP_LOG_WARN("AC3D: The type flag of a surface is unknown: ", (*it).flags); + (*it).flags &= ~(Surface::Mask); + // fallthrough + + // polygon + case Surface::Polygon: + // the number of faces increments by one, the number + // of vertices by surface.numref. + needMat[idx].first++; + needMat[idx].second += (unsigned int)(*it).entries.size(); + }; + } + unsigned int *pip = node->mMeshes = new unsigned int[node->mNumMeshes]; + unsigned int mat = 0; + const size_t oldm = meshes.size(); + for (MatTable::const_iterator cit = needMat.begin(), cend = needMat.end(); + cit != cend; ++cit, ++mat) { + if (!(*cit).first) { + continue; + } + + // allocate a new aiMesh object + *pip++ = (unsigned int)meshes.size(); + aiMesh *mesh = new aiMesh(); + meshes.push_back(mesh); + + mesh->mMaterialIndex = static_cast(outMaterials.size()); + outMaterials.push_back(new aiMaterial()); + ConvertMaterial(object, materials[mat], *outMaterials.back()); + + // allocate storage for vertices and normals + mesh->mNumFaces = (*cit).first; + if (mesh->mNumFaces == 0) { + throw DeadlyImportError("AC3D: No faces"); + } else if (mesh->mNumFaces > AI_MAX_ALLOC(aiFace)) { + throw DeadlyImportError("AC3D: Too many faces, would run out of memory"); + } + aiFace *faces = mesh->mFaces = new aiFace[mesh->mNumFaces]; + + mesh->mNumVertices = (*cit).second; + if (mesh->mNumVertices == 0) { + throw DeadlyImportError("AC3D: No vertices"); + } else if (mesh->mNumVertices > AI_MAX_ALLOC(aiVector3D)) { + throw DeadlyImportError("AC3D: Too many vertices, would run out of memory"); + } + aiVector3D *vertices = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + unsigned int cur = 0; + + // allocate UV coordinates, but only if the texture name for the + // surface is not empty + aiVector3D *uv = nullptr; + if (object.texture.length()) { + uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; + mesh->mNumUVComponents[0] = 2; + } + + for (it = object.surfaces.begin(); it != end; ++it) { + if (mat == (*it).mat) { + const Surface &src = *it; + + // closed polygon + uint8_t type = (*it).GetType(); + if (type == Surface::Polygon) { + aiFace &face = *faces++; + face.mNumIndices = (unsigned int)src.entries.size(); + if (0 != face.mNumIndices) { + face.mIndices = new unsigned int[face.mNumIndices]; + for (unsigned int i = 0; i < face.mNumIndices; ++i, ++vertices) { + const Surface::SurfaceEntry &entry = src.entries[i]; + face.mIndices[i] = cur++; + + // copy vertex positions + if (static_cast(vertices - mesh->mVertices) >= mesh->mNumVertices) { + throw DeadlyImportError("AC3D: Invalid number of vertices"); + } + *vertices = object.vertices[entry.first] + object.translation; + + // copy texture coordinates + if (uv) { + uv->x = entry.second.x; + uv->y = entry.second.y; + ++uv; + } + } + } + } else if (type == Surface::TriangleStrip) { + for (unsigned int i = 0; i < (unsigned int)src.entries.size() - 2; ++i) { + const Surface::SurfaceEntry &entry1 = src.entries[i]; + const Surface::SurfaceEntry &entry2 = src.entries[i + 1]; + const Surface::SurfaceEntry &entry3 = src.entries[i + 2]; + + // skip degenerate triangles + if (object.vertices[entry1.first] == object.vertices[entry2.first] || + object.vertices[entry1.first] == object.vertices[entry3.first] || + object.vertices[entry2.first] == object.vertices[entry3.first]) { + mesh->mNumFaces--; + mesh->mNumVertices -= 3; + continue; + } + + aiFace &face = *faces++; + face.mNumIndices = 3; + face.mIndices = new unsigned int[face.mNumIndices]; + face.mIndices[0] = cur++; + face.mIndices[1] = cur++; + face.mIndices[2] = cur++; + if (!(i & 1)) { + *vertices++ = object.vertices[entry1.first] + object.translation; + if (uv) { + uv->x = entry1.second.x; + uv->y = entry1.second.y; + ++uv; + } + *vertices++ = object.vertices[entry2.first] + object.translation; + if (uv) { + uv->x = entry2.second.x; + uv->y = entry2.second.y; + ++uv; + } + } else { + *vertices++ = object.vertices[entry2.first] + object.translation; + if (uv) { + uv->x = entry2.second.x; + uv->y = entry2.second.y; + ++uv; + } + *vertices++ = object.vertices[entry1.first] + object.translation; + if (uv) { + uv->x = entry1.second.x; + uv->y = entry1.second.y; + ++uv; + } + } + if (static_cast(vertices - mesh->mVertices) >= mesh->mNumVertices) { + throw DeadlyImportError("AC3D: Invalid number of vertices"); + } + *vertices++ = object.vertices[entry3.first] + object.translation; + if (uv) { + uv->x = entry3.second.x; + uv->y = entry3.second.y; + ++uv; + } + } + } else { + + it2 = (*it).entries.begin(); + + // either a closed or an unclosed line + unsigned int tmp = (unsigned int)(*it).entries.size(); + if (Surface::OpenLine == type) --tmp; + for (unsigned int m = 0; m < tmp; ++m) { + aiFace &face = *faces++; + + face.mNumIndices = 2; + face.mIndices = new unsigned int[2]; + face.mIndices[0] = cur++; + face.mIndices[1] = cur++; + + // copy vertex positions + if (it2 == (*it).entries.end()) { + throw DeadlyImportError("AC3D: Bad line"); + } + ai_assert((*it2).first < object.vertices.size()); + *vertices++ = object.vertices[(*it2).first]; + + // copy texture coordinates + if (uv) { + uv->x = (*it2).second.x; + uv->y = (*it2).second.y; + ++uv; + } + + if (Surface::ClosedLine == type && tmp - 1 == m) { + // if this is a closed line repeat its beginning now + it2 = (*it).entries.begin(); + } else + ++it2; + + // second point + *vertices++ = object.vertices[(*it2).first]; + + if (uv) { + uv->x = (*it2).second.x; + uv->y = (*it2).second.y; + ++uv; + } + } + } + } + } + } + + // Now apply catmull clark subdivision if necessary. We split meshes into + // materials which is not done by AC3D during smoothing, so we need to + // collect all meshes using the same material group. + if (object.subDiv) { + if (configEvalSubdivision) { + std::unique_ptr div(Subdivider::Create(Subdivider::CATMULL_CLARKE)); + ASSIMP_LOG_INFO("AC3D: Evaluating subdivision surface: ", object.name); + + std::vector cpy(meshes.size() - oldm, nullptr); + div->Subdivide(&meshes[oldm], cpy.size(), &cpy.front(), object.subDiv, true); + std::copy(cpy.begin(), cpy.end(), meshes.begin() + oldm); + + // previous meshes are deleted vy Subdivide(). + } else { + ASSIMP_LOG_INFO("AC3D: Letting the subdivision surface untouched due to my configuration: ", object.name); + } + } + } + } + + if (object.name.length()) + node->mName.Set(object.name); + else { + // generate a name depending on the type of the node + switch (object.type) { + case Object::Group: + node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACGroup_%i", mGroupsCounter++); + break; + case Object::Poly: + node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACPoly_%i", mPolysCounter++); + break; + case Object::Light: + node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACLight_%i", mLightsCounter++); + break; + + // there shouldn't be more than one world, but we don't care + case Object::World: + node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACWorld_%i", mWorldsCounter++); + break; + } + } + + // setup the local transformation matrix of the object + // compute the transformation offset to the parent node + node->mTransformation = aiMatrix4x4(object.rotation); + + if (object.type == Object::Group || !object.numRefs) { + node->mTransformation.a4 = object.translation.x; + node->mTransformation.b4 = object.translation.y; + node->mTransformation.c4 = object.translation.z; + } + + // add children to the object + if (object.children.size()) { + node->mNumChildren = (unsigned int)object.children.size(); + node->mChildren = new aiNode *[node->mNumChildren]; + for (unsigned int i = 0; i < node->mNumChildren; ++i) { + node->mChildren[i] = ConvertObjectSection(object.children[i], meshes, outMaterials, materials, node); + } + } + + return node; +} + +// ------------------------------------------------------------------------------------------------ +void AC3DImporter::SetupProperties(const Importer *pImp) { + configSplitBFCull = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL, 1) ? true : false; + configEvalSubdivision = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION, 1) ? true : false; +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void AC3DImporter::InternReadFile(const std::string &pFile, + aiScene *pScene, IOSystem *pIOHandler) { + std::unique_ptr file(pIOHandler->Open(pFile, "rb")); + + // Check whether we can read from the file + if (file.get() == nullptr) { + throw DeadlyImportError("Failed to open AC3D file ", pFile, "."); + } + + // allocate storage and copy the contents of the file to a memory buffer + std::vector mBuffer2; + TextFileToBuffer(file.get(), mBuffer2); + + buffer = &mBuffer2[0]; + mNumMeshes = 0; + + mLightsCounter = mPolysCounter = mWorldsCounter = mGroupsCounter = 0; + + if (::strncmp(buffer, "AC3D", 4)) { + throw DeadlyImportError("AC3D: No valid AC3D file, magic sequence not found"); + } + + // print the file format version to the console + unsigned int version = HexDigitToDecimal(buffer[4]); + char msg[3]; + ASSIMP_itoa10(msg, 3, version); + ASSIMP_LOG_INFO("AC3D file format version: ", msg); + + std::vector materials; + materials.reserve(5); + + std::vector rootObjects; + rootObjects.reserve(5); + + std::vector lights; + mLights = &lights; + + while (GetNextLine()) { + if (TokenMatch(buffer, "MATERIAL", 8)) { + materials.push_back(Material()); + Material &mat = materials.back(); + + // manually parse the material ... sscanf would use the buldin atof ... + // Format: (name) rgb %f %f %f amb %f %f %f emis %f %f %f spec %f %f %f shi %d trans %f + + buffer = AcSkipToNextToken(buffer); + if ('\"' == *buffer) { + buffer = AcGetString(buffer, mat.name); + buffer = AcSkipToNextToken(buffer); + } + + buffer = TAcCheckedLoadFloatArray(buffer, "rgb", 3, 3, &mat.rgb); + buffer = TAcCheckedLoadFloatArray(buffer, "amb", 3, 3, &mat.amb); + buffer = TAcCheckedLoadFloatArray(buffer, "emis", 4, 3, &mat.emis); + buffer = TAcCheckedLoadFloatArray(buffer, "spec", 4, 3, &mat.spec); + buffer = TAcCheckedLoadFloatArray(buffer, "shi", 3, 1, &mat.shin); + buffer = TAcCheckedLoadFloatArray(buffer, "trans", 5, 1, &mat.trans); + } + LoadObjectSection(rootObjects); + } + + if (rootObjects.empty() || !mNumMeshes) { + throw DeadlyImportError("AC3D: No meshes have been loaded"); + } + if (materials.empty()) { + ASSIMP_LOG_WARN("AC3D: No material has been found"); + materials.push_back(Material()); + } + + mNumMeshes += (mNumMeshes >> 2u) + 1; + std::vector meshes; + meshes.reserve(mNumMeshes); + + std::vector omaterials; + materials.reserve(mNumMeshes); + + // generate a dummy root if there are multiple objects on the top layer + Object *root; + if (1 == rootObjects.size()) + root = &rootObjects[0]; + else { + root = new Object(); + } + + // now convert the imported stuff to our output data structure + pScene->mRootNode = ConvertObjectSection(*root, meshes, omaterials, materials); + if (1 != rootObjects.size()) { + delete root; + } + + if (!::strncmp(pScene->mRootNode->mName.data, "Node", 4)) { + pScene->mRootNode->mName.Set(""); + } + + // copy meshes + if (meshes.empty()) { + throw DeadlyImportError("An unknown error occurred during converting"); + } + pScene->mNumMeshes = (unsigned int)meshes.size(); + pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]; + ::memcpy(pScene->mMeshes, &meshes[0], pScene->mNumMeshes * sizeof(void *)); + + // copy materials + pScene->mNumMaterials = (unsigned int)omaterials.size(); + pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials]; + ::memcpy(pScene->mMaterials, &omaterials[0], pScene->mNumMaterials * sizeof(void *)); + + // copy lights + pScene->mNumLights = (unsigned int)lights.size(); + if (lights.size()) { + pScene->mLights = new aiLight *[lights.size()]; + ::memcpy(pScene->mLights, &lights[0], lights.size() * sizeof(void *)); + } +} + +#endif //!defined ASSIMP_BUILD_NO_AC_IMPORTER diff --git a/libs/assimp/code/AssetLib/AC/ACLoader.h b/libs/assimp/code/AssetLib/AC/ACLoader.h new file mode 100644 index 0000000..aabc114 --- /dev/null +++ b/libs/assimp/code/AssetLib/AC/ACLoader.h @@ -0,0 +1,270 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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 ACLoader.h + * @brief Declaration of the .ac importer class. + */ +#ifndef AI_AC3DLOADER_H_INCLUDED +#define AI_AC3DLOADER_H_INCLUDED + +#include + +#include +#include + +struct aiNode; +struct aiMesh; +struct aiMaterial; +struct aiLight; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** AC3D (*.ac) importer class +*/ +class AC3DImporter : public BaseImporter { +public: + AC3DImporter(); + ~AC3DImporter() override; + + // Represents an AC3D material + struct Material { + Material() : + rgb(0.6f, 0.6f, 0.6f), + spec(1.f, 1.f, 1.f), + shin(0.f), + trans(0.f) {} + + // base color of the material + aiColor3D rgb; + + // ambient color of the material + aiColor3D amb; + + // emissive color of the material + aiColor3D emis; + + // specular color of the material + aiColor3D spec; + + // shininess exponent + float shin; + + // transparency. 0 == opaque + float trans; + + // name of the material. optional. + std::string name; + }; + + // Represents an AC3D surface + struct Surface { + Surface() : + mat(0), + flags(0) {} + + unsigned int mat, flags; + + typedef std::pair SurfaceEntry; + std::vector entries; + + // Type is low nibble of flags + enum Type : uint8_t { + Polygon = 0x0, + ClosedLine = 0x1, + OpenLine = 0x2, + TriangleStrip = 0x4, // ACC extension (TORCS and Speed Dreams) + + Mask = 0xf, + }; + + inline uint8_t GetType() const { return (flags & Mask); } + }; + + // Represents an AC3D object + struct Object { + Object() : + type(World), + name(), + children(), + texture(), + texRepeat(1.f, 1.f), + texOffset(0.0f, 0.0f), + rotation(), + translation(), + vertices(), + surfaces(), + numRefs(0), + subDiv(0), + crease() {} + + // Type description + enum Type { + World = 0x0, + Poly = 0x1, + Group = 0x2, + Light = 0x4 + } type; + + // name of the object + std::string name; + + // object children + std::vector children; + + // texture to be assigned to all surfaces of the object + std::string texture; + + // texture repat factors (scaling for all coordinates) + aiVector2D texRepeat, texOffset; + + // rotation matrix + aiMatrix3x3 rotation; + + // translation vector + aiVector3D translation; + + // vertices + std::vector vertices; + + // surfaces + std::vector surfaces; + + // number of indices (= num verts in verbose format) + unsigned int numRefs; + + // number of subdivisions to be performed on the + // imported data + unsigned int subDiv; + + // max angle limit for smoothing + float crease; + }; + +public: + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. + */ + bool CanRead(const std::string &pFile, IOSystem *pIOHandler, + bool checkSig) const override; + +protected: + // ------------------------------------------------------------------- + /** Return importer meta information. + * See #BaseImporter::GetInfo for the details */ + const aiImporterDesc *GetInfo() const override; + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details*/ + void InternReadFile(const std::string &pFile, aiScene *pScene, + IOSystem *pIOHandler) override; + + // ------------------------------------------------------------------- + /** Called prior to ReadFile(). + * The function is a request to the importer to update its configuration + * basing on the Importer's configuration property list.*/ + void SetupProperties(const Importer *pImp) override; + +private: + // ------------------------------------------------------------------- + /** Get the next line from the file. + * @return false if the end of the file was reached*/ + bool GetNextLine(); + + // ------------------------------------------------------------------- + /** Load the object section. This method is called recursively to + * load subobjects, the method returns after a 'kids 0' was + * encountered. + * @objects List of output objects*/ + void LoadObjectSection(std::vector &objects); + + // ------------------------------------------------------------------- + /** Convert all objects into meshes and nodes. + * @param object Current object to work on + * @param meshes Pointer to the list of output meshes + * @param outMaterials List of output materials + * @param materials Material list + * @param Scenegraph node for the object */ + aiNode *ConvertObjectSection(Object &object, + std::vector &meshes, + std::vector &outMaterials, + const std::vector &materials, + aiNode *parent = nullptr); + + // ------------------------------------------------------------------- + /** Convert a material + * @param object Current object + * @param matSrc Source material description + * @param matDest Destination material to be filled */ + void ConvertMaterial(const Object &object, + const Material &matSrc, + aiMaterial &matDest); + +private: + // points to the next data line + const char *buffer; + + // Configuration option: if enabled, up to two meshes + // are generated per material: those faces who have + // their bf cull flags set are separated. + bool configSplitBFCull; + + // Configuration switch: subdivision surfaces are only + // evaluated if the value is true. + bool configEvalSubdivision; + + // counts how many objects we have in the tree. + // basing on this information we can find a + // good estimate how many meshes we'll have in the final scene. + unsigned int mNumMeshes; + + // current list of light sources + std::vector *mLights; + + // name counters + unsigned int mLightsCounter, mGroupsCounter, mPolysCounter, mWorldsCounter; +}; + +} // end of namespace Assimp + +#endif // AI_AC3DIMPORTER_H_INC -- cgit v1.2.1