diff options
author | sanine <sanine.not@pm.me> | 2022-04-16 11:55:09 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2022-04-16 11:55:09 -0500 |
commit | db81b925d776103326128bf629cbdda576a223e7 (patch) | |
tree | 58bea8155c686733310009f6bed7363f91fbeb9d /libs/assimp/code/AssetLib/ASE/ASEParser.cpp | |
parent | 55860037b14fb3893ba21cf2654c83d349cc1082 (diff) |
move 3rd-party librarys into libs/ and add built-in honeysuckle
Diffstat (limited to 'libs/assimp/code/AssetLib/ASE/ASEParser.cpp')
-rw-r--r-- | libs/assimp/code/AssetLib/ASE/ASEParser.cpp | 1869 |
1 files changed, 1869 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/ASE/ASEParser.cpp b/libs/assimp/code/AssetLib/ASE/ASEParser.cpp new file mode 100644 index 0000000..14eb720 --- /dev/null +++ b/libs/assimp/code/AssetLib/ASE/ASEParser.cpp @@ -0,0 +1,1869 @@ +/* +--------------------------------------------------------------------------- +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 ASEParser.cpp + * @brief Implementation of the ASE parser class + */ + +#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER +#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER + +// internal headers +#include "ASELoader.h" +#include "PostProcessing/TextureTransform.h" + +#include <assimp/fast_atof.h> +#include <assimp/DefaultLogger.hpp> + +using namespace Assimp; +using namespace Assimp::ASE; + +// ------------------------------------------------------------------------------------------------ +// Begin an ASE parsing function + +#define AI_ASE_PARSER_INIT() \ + int iDepth = 0; + +// ------------------------------------------------------------------------------------------------ +// Handle a "top-level" section in the file. EOF is no error in this case. + +#define AI_ASE_HANDLE_TOP_LEVEL_SECTION() \ + else if ('{' == *filePtr) iDepth++; \ + else if ('}' == *filePtr) { \ + if (0 == --iDepth) { \ + ++filePtr; \ + SkipToNextToken(); \ + return; \ + } \ + } \ + else if ('\0' == *filePtr) { \ + return; \ + } \ + if (IsLineEnd(*filePtr) && !bLastWasEndLine) { \ + ++iLineNumber; \ + bLastWasEndLine = true; \ + } else \ + bLastWasEndLine = false; \ + ++filePtr; + +// ------------------------------------------------------------------------------------------------ +// Handle a nested section in the file. EOF is an error in this case +// @param level "Depth" of the section +// @param msg Full name of the section (including the asterisk) + +#define AI_ASE_HANDLE_SECTION(level, msg) \ + if ('{' == *filePtr) \ + iDepth++; \ + else if ('}' == *filePtr) { \ + if (0 == --iDepth) { \ + ++filePtr; \ + SkipToNextToken(); \ + return; \ + } \ + } else if ('\0' == *filePtr) { \ + LogError("Encountered unexpected EOL while parsing a " msg \ + " chunk (Level " level ")"); \ + } \ + if (IsLineEnd(*filePtr) && !bLastWasEndLine) { \ + ++iLineNumber; \ + bLastWasEndLine = true; \ + } else \ + bLastWasEndLine = false; \ + ++filePtr; + +// ------------------------------------------------------------------------------------------------ +Parser::Parser(const char *szFile, unsigned int fileFormatDefault) { + ai_assert(nullptr != szFile); + + filePtr = szFile; + iFileFormat = fileFormatDefault; + + // make sure that the color values are invalid + m_clrBackground.r = get_qnan(); + m_clrAmbient.r = get_qnan(); + + // setup some default values + iLineNumber = 0; + iFirstFrame = 0; + iLastFrame = 0; + iFrameSpeed = 30; // use 30 as default value for this property + iTicksPerFrame = 1; // use 1 as default value for this property + bLastWasEndLine = false; // need to handle \r\n seqs due to binary file mapping +} + +// ------------------------------------------------------------------------------------------------ +void Parser::LogWarning(const char *szWarn) { + ai_assert(nullptr != szWarn); + + char szTemp[2048]; +#if _MSC_VER >= 1400 + sprintf_s(szTemp, "Line %u: %s", iLineNumber, szWarn); +#else + ai_snprintf(szTemp, sizeof(szTemp), "Line %u: %s", iLineNumber, szWarn); +#endif + + // output the warning to the logger ... + ASSIMP_LOG_WARN(szTemp); +} + +// ------------------------------------------------------------------------------------------------ +void Parser::LogInfo(const char *szWarn) { + ai_assert(nullptr != szWarn); + + char szTemp[1024]; +#if _MSC_VER >= 1400 + sprintf_s(szTemp, "Line %u: %s", iLineNumber, szWarn); +#else + ai_snprintf(szTemp, 1024, "Line %u: %s", iLineNumber, szWarn); +#endif + + // output the information to the logger ... + ASSIMP_LOG_INFO(szTemp); +} + +// ------------------------------------------------------------------------------------------------ +AI_WONT_RETURN void Parser::LogError(const char *szWarn) { + ai_assert(nullptr != szWarn); + + char szTemp[1024]; +#if _MSC_VER >= 1400 + sprintf_s(szTemp, "Line %u: %s", iLineNumber, szWarn); +#else + ai_snprintf(szTemp, 1024, "Line %u: %s", iLineNumber, szWarn); +#endif + + // throw an exception + throw DeadlyImportError(szTemp); +} + +// ------------------------------------------------------------------------------------------------ +bool Parser::SkipToNextToken() { + while (true) { + char me = *filePtr; + + // increase the line number counter if necessary + if (IsLineEnd(me) && !bLastWasEndLine) { + ++iLineNumber; + bLastWasEndLine = true; + } else + bLastWasEndLine = false; + if ('*' == me || '}' == me || '{' == me) return true; + if ('\0' == me) return false; + + ++filePtr; + } +} + +// ------------------------------------------------------------------------------------------------ +bool Parser::SkipSection() { + // must handle subsections ... + int iCnt = 0; + while (true) { + if ('}' == *filePtr) { + --iCnt; + if (0 == iCnt) { + // go to the next valid token ... + ++filePtr; + SkipToNextToken(); + return true; + } + } else if ('{' == *filePtr) { + ++iCnt; + } else if ('\0' == *filePtr) { + LogWarning("Unable to parse block: Unexpected EOF, closing bracket \'}\' was expected [#1]"); + return false; + } else if (IsLineEnd(*filePtr)) + ++iLineNumber; + ++filePtr; + } +} + +// ------------------------------------------------------------------------------------------------ +void Parser::Parse() { + AI_ASE_PARSER_INIT(); + while (true) { + if ('*' == *filePtr) { + ++filePtr; + + // Version should be 200. Validate this ... + if (TokenMatch(filePtr, "3DSMAX_ASCIIEXPORT", 18)) { + unsigned int fmt; + ParseLV4MeshLong(fmt); + + if (fmt > 200) { + LogWarning("Unknown file format version: *3DSMAX_ASCIIEXPORT should \ + be <= 200"); + } + // ************************************************************* + // - fmt will be 0 if we're unable to read the version number + // there are some faulty files without a version number ... + // in this case we'll guess the exact file format by looking + // at the file extension (ASE, ASK, ASC) + // ************************************************************* + + if (fmt) { + iFileFormat = fmt; + } + continue; + } + // main scene information + if (TokenMatch(filePtr, "SCENE", 5)) { + ParseLV1SceneBlock(); + continue; + } + // "group" - no implementation yet, in facte + // we're just ignoring them for the moment + if (TokenMatch(filePtr, "GROUP", 5)) { + Parse(); + continue; + } + // material list + if (TokenMatch(filePtr, "MATERIAL_LIST", 13)) { + ParseLV1MaterialListBlock(); + continue; + } + // geometric object (mesh) + if (TokenMatch(filePtr, "GEOMOBJECT", 10)) + + { + m_vMeshes.push_back(Mesh("UNNAMED")); + ParseLV1ObjectBlock(m_vMeshes.back()); + continue; + } + // helper object = dummy in the hierarchy + if (TokenMatch(filePtr, "HELPEROBJECT", 12)) + + { + m_vDummies.push_back(Dummy()); + ParseLV1ObjectBlock(m_vDummies.back()); + continue; + } + // light object + if (TokenMatch(filePtr, "LIGHTOBJECT", 11)) + + { + m_vLights.push_back(Light("UNNAMED")); + ParseLV1ObjectBlock(m_vLights.back()); + continue; + } + // camera object + if (TokenMatch(filePtr, "CAMERAOBJECT", 12)) { + m_vCameras.push_back(Camera("UNNAMED")); + ParseLV1ObjectBlock(m_vCameras.back()); + continue; + } + // comment - print it on the console + if (TokenMatch(filePtr, "COMMENT", 7)) { + std::string out = "<unknown>"; + ParseString(out, "*COMMENT"); + LogInfo(("Comment: " + out).c_str()); + continue; + } + // ASC bone weights + if (AI_ASE_IS_OLD_FILE_FORMAT() && TokenMatch(filePtr, "MESH_SOFTSKINVERTS", 18)) { + ParseLV1SoftSkinBlock(); + } + } + AI_ASE_HANDLE_TOP_LEVEL_SECTION(); + } + return; +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV1SoftSkinBlock() { + // TODO: fix line counting here + + // ************************************************************** + // The soft skin block is formatted differently. There are no + // nested sections supported and the single elements aren't + // marked by keywords starting with an asterisk. + + /** + FORMAT BEGIN + + *MESH_SOFTSKINVERTS { + <nodename> + <number of vertices> + + [for <number of vertices> times:] + <number of weights> [for <number of weights> times:] <bone name> <weight> + } + + FORMAT END + */ + // ************************************************************** + while (true) { + if (*filePtr == '}') { + ++filePtr; + return; + } else if (*filePtr == '\0') + return; + else if (*filePtr == '{') + ++filePtr; + + else // if (!IsSpace(*filePtr) && !IsLineEnd(*filePtr)) + { + ASE::Mesh *curMesh = nullptr; + unsigned int numVerts = 0; + + const char *sz = filePtr; + while (!IsSpaceOrNewLine(*filePtr)) + ++filePtr; + + const unsigned int diff = (unsigned int)(filePtr - sz); + if (diff) { + std::string name = std::string(sz, diff); + for (std::vector<ASE::Mesh>::iterator it = m_vMeshes.begin(); + it != m_vMeshes.end(); ++it) { + if ((*it).mName == name) { + curMesh = &(*it); + break; + } + } + if (!curMesh) { + LogWarning("Encountered unknown mesh in *MESH_SOFTSKINVERTS section"); + + // Skip the mesh data - until we find a new mesh + // or the end of the *MESH_SOFTSKINVERTS section + while (true) { + SkipSpacesAndLineEnd(&filePtr); + if (*filePtr == '}') { + ++filePtr; + return; + } else if (!IsNumeric(*filePtr)) + break; + + SkipLine(&filePtr); + } + } else { + SkipSpacesAndLineEnd(&filePtr); + ParseLV4MeshLong(numVerts); + + // Reserve enough storage + curMesh->mBoneVertices.reserve(numVerts); + + for (unsigned int i = 0; i < numVerts; ++i) { + SkipSpacesAndLineEnd(&filePtr); + unsigned int numWeights; + ParseLV4MeshLong(numWeights); + + curMesh->mBoneVertices.push_back(ASE::BoneVertex()); + ASE::BoneVertex &vert = curMesh->mBoneVertices.back(); + + // Reserve enough storage + vert.mBoneWeights.reserve(numWeights); + + std::string bone; + for (unsigned int w = 0; w < numWeights; ++w) { + bone.clear(); + ParseString(bone, "*MESH_SOFTSKINVERTS.Bone"); + + // Find the bone in the mesh's list + std::pair<int, ai_real> me; + me.first = -1; + + for (unsigned int n = 0; n < curMesh->mBones.size(); ++n) { + if (curMesh->mBones[n].mName == bone) { + me.first = n; + break; + } + } + if (-1 == me.first) { + // We don't have this bone yet, so add it to the list + me.first = static_cast<int>(curMesh->mBones.size()); + curMesh->mBones.push_back(ASE::Bone(bone)); + } + ParseLV4MeshFloat(me.second); + + // Add the new bone weight to list + vert.mBoneWeights.push_back(me); + } + } + } + } + } + ++filePtr; + SkipSpacesAndLineEnd(&filePtr); + } +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV1SceneBlock() { + AI_ASE_PARSER_INIT(); + while (true) { + if ('*' == *filePtr) { + ++filePtr; + if (TokenMatch(filePtr, "SCENE_BACKGROUND_STATIC", 23)) + + { + // parse a color triple and assume it is really the bg color + ParseLV4MeshFloatTriple(&m_clrBackground.r); + continue; + } + if (TokenMatch(filePtr, "SCENE_AMBIENT_STATIC", 20)) + + { + // parse a color triple and assume it is really the bg color + ParseLV4MeshFloatTriple(&m_clrAmbient.r); + continue; + } + if (TokenMatch(filePtr, "SCENE_FIRSTFRAME", 16)) { + ParseLV4MeshLong(iFirstFrame); + continue; + } + if (TokenMatch(filePtr, "SCENE_LASTFRAME", 15)) { + ParseLV4MeshLong(iLastFrame); + continue; + } + if (TokenMatch(filePtr, "SCENE_FRAMESPEED", 16)) { + ParseLV4MeshLong(iFrameSpeed); + continue; + } + if (TokenMatch(filePtr, "SCENE_TICKSPERFRAME", 19)) { + ParseLV4MeshLong(iTicksPerFrame); + continue; + } + } + AI_ASE_HANDLE_TOP_LEVEL_SECTION(); + } +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV1MaterialListBlock() { + AI_ASE_PARSER_INIT(); + + unsigned int iMaterialCount = 0; + unsigned int iOldMaterialCount = (unsigned int)m_vMaterials.size(); + while (true) { + if ('*' == *filePtr) { + ++filePtr; + if (TokenMatch(filePtr, "MATERIAL_COUNT", 14)) { + ParseLV4MeshLong(iMaterialCount); + + // now allocate enough storage to hold all materials + m_vMaterials.resize(iOldMaterialCount + iMaterialCount, Material("INVALID")); + continue; + } + if (TokenMatch(filePtr, "MATERIAL", 8)) { + unsigned int iIndex = 0; + ParseLV4MeshLong(iIndex); + + if (iIndex >= iMaterialCount) { + LogWarning("Out of range: material index is too large"); + iIndex = iMaterialCount - 1; + return; + } + + // get a reference to the material + Material &sMat = m_vMaterials[iIndex + iOldMaterialCount]; + // parse the material block + ParseLV2MaterialBlock(sMat); + continue; + } + if( iDepth == 1 ){ + // CRUDE HACK: support missing brace after "Ascii Scene Exporter v2.51" + LogWarning("Missing closing brace in material list"); + --filePtr; + return; + } + } + AI_ASE_HANDLE_TOP_LEVEL_SECTION(); + } +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2MaterialBlock(ASE::Material &mat) { + AI_ASE_PARSER_INIT(); + + unsigned int iNumSubMaterials = 0; + while (true) { + if ('*' == *filePtr) { + ++filePtr; + if (TokenMatch(filePtr, "MATERIAL_NAME", 13)) { + if (!ParseString(mat.mName, "*MATERIAL_NAME")) + SkipToNextToken(); + continue; + } + // ambient material color + if (TokenMatch(filePtr, "MATERIAL_AMBIENT", 16)) { + ParseLV4MeshFloatTriple(&mat.mAmbient.r); + continue; + } + // diffuse material color + if (TokenMatch(filePtr, "MATERIAL_DIFFUSE", 16)) { + ParseLV4MeshFloatTriple(&mat.mDiffuse.r); + continue; + } + // specular material color + if (TokenMatch(filePtr, "MATERIAL_SPECULAR", 17)) { + ParseLV4MeshFloatTriple(&mat.mSpecular.r); + continue; + } + // material shading type + if (TokenMatch(filePtr, "MATERIAL_SHADING", 16)) { + if (TokenMatch(filePtr, "Blinn", 5)) { + mat.mShading = Discreet3DS::Blinn; + } else if (TokenMatch(filePtr, "Phong", 5)) { + mat.mShading = Discreet3DS::Phong; + } else if (TokenMatch(filePtr, "Flat", 4)) { + mat.mShading = Discreet3DS::Flat; + } else if (TokenMatch(filePtr, "Wire", 4)) { + mat.mShading = Discreet3DS::Wire; + } else { + // assume gouraud shading + mat.mShading = Discreet3DS::Gouraud; + SkipToNextToken(); + } + continue; + } + // material transparency + if (TokenMatch(filePtr, "MATERIAL_TRANSPARENCY", 21)) { + ParseLV4MeshFloat(mat.mTransparency); + mat.mTransparency = ai_real(1.0) - mat.mTransparency; + continue; + } + // material self illumination + if (TokenMatch(filePtr, "MATERIAL_SELFILLUM", 18)) { + ai_real f = 0.0; + ParseLV4MeshFloat(f); + + mat.mEmissive.r = f; + mat.mEmissive.g = f; + mat.mEmissive.b = f; + continue; + } + // material shininess + if (TokenMatch(filePtr, "MATERIAL_SHINE", 14)) { + ParseLV4MeshFloat(mat.mSpecularExponent); + mat.mSpecularExponent *= 15; + continue; + } + // two-sided material + if (TokenMatch(filePtr, "MATERIAL_TWOSIDED", 17)) { + mat.mTwoSided = true; + continue; + } + // material shininess strength + if (TokenMatch(filePtr, "MATERIAL_SHINESTRENGTH", 22)) { + ParseLV4MeshFloat(mat.mShininessStrength); + continue; + } + // diffuse color map + if (TokenMatch(filePtr, "MAP_DIFFUSE", 11)) { + // parse the texture block + ParseLV3MapBlock(mat.sTexDiffuse); + continue; + } + // ambient color map + if (TokenMatch(filePtr, "MAP_AMBIENT", 11)) { + // parse the texture block + ParseLV3MapBlock(mat.sTexAmbient); + continue; + } + // specular color map + if (TokenMatch(filePtr, "MAP_SPECULAR", 12)) { + // parse the texture block + ParseLV3MapBlock(mat.sTexSpecular); + continue; + } + // opacity map + if (TokenMatch(filePtr, "MAP_OPACITY", 11)) { + // parse the texture block + ParseLV3MapBlock(mat.sTexOpacity); + continue; + } + // emissive map + if (TokenMatch(filePtr, "MAP_SELFILLUM", 13)) { + // parse the texture block + ParseLV3MapBlock(mat.sTexEmissive); + continue; + } + // bump map + if (TokenMatch(filePtr, "MAP_BUMP", 8)) { + // parse the texture block + ParseLV3MapBlock(mat.sTexBump); + } + // specular/shininess map + if (TokenMatch(filePtr, "MAP_SHINESTRENGTH", 17)) { + // parse the texture block + ParseLV3MapBlock(mat.sTexShininess); + continue; + } + // number of submaterials + if (TokenMatch(filePtr, "NUMSUBMTLS", 10)) { + ParseLV4MeshLong(iNumSubMaterials); + + // allocate enough storage + mat.avSubMaterials.resize(iNumSubMaterials, Material("INVALID SUBMATERIAL")); + } + // submaterial chunks + if (TokenMatch(filePtr, "SUBMATERIAL", 11)) { + + unsigned int iIndex = 0; + ParseLV4MeshLong(iIndex); + + if (iIndex >= iNumSubMaterials) { + LogWarning("Out of range: submaterial index is too large"); + iIndex = iNumSubMaterials - 1; + } + + // get a reference to the material + Material &sMat = mat.avSubMaterials[iIndex]; + + // parse the material block + ParseLV2MaterialBlock(sMat); + continue; + } + } + AI_ASE_HANDLE_SECTION("2", "*MATERIAL"); + } +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MapBlock(Texture &map) { + AI_ASE_PARSER_INIT(); + + // *********************************************************** + // *BITMAP should not be there if *MAP_CLASS is not BITMAP, + // but we need to expect that case ... if the path is + // empty the texture won't be used later. + // *********************************************************** + bool parsePath = true; + std::string temp; + while (true) { + if ('*' == *filePtr) { + ++filePtr; + // type of map + if (TokenMatch(filePtr, "MAP_CLASS", 9)) { + temp.clear(); + if (!ParseString(temp, "*MAP_CLASS")) + SkipToNextToken(); + if (temp != "Bitmap" && temp != "Normal Bump") { + ASSIMP_LOG_WARN("ASE: Skipping unknown map type: ", temp); + parsePath = false; + } + continue; + } + // path to the texture + if (parsePath && TokenMatch(filePtr, "BITMAP", 6)) { + if (!ParseString(map.mMapName, "*BITMAP")) + SkipToNextToken(); + + if (map.mMapName == "None") { + // Files with 'None' as map name are produced by + // an Maja to ASE exporter which name I forgot .. + ASSIMP_LOG_WARN("ASE: Skipping invalid map entry"); + map.mMapName = std::string(); + } + + continue; + } + // offset on the u axis + if (TokenMatch(filePtr, "UVW_U_OFFSET", 12)) { + ParseLV4MeshFloat(map.mOffsetU); + continue; + } + // offset on the v axis + if (TokenMatch(filePtr, "UVW_V_OFFSET", 12)) { + ParseLV4MeshFloat(map.mOffsetV); + continue; + } + // tiling on the u axis + if (TokenMatch(filePtr, "UVW_U_TILING", 12)) { + ParseLV4MeshFloat(map.mScaleU); + continue; + } + // tiling on the v axis + if (TokenMatch(filePtr, "UVW_V_TILING", 12)) { + ParseLV4MeshFloat(map.mScaleV); + continue; + } + // rotation around the z-axis + if (TokenMatch(filePtr, "UVW_ANGLE", 9)) { + ParseLV4MeshFloat(map.mRotation); + continue; + } + // map blending factor + if (TokenMatch(filePtr, "MAP_AMOUNT", 10)) { + ParseLV4MeshFloat(map.mTextureBlend); + continue; + } + } + AI_ASE_HANDLE_SECTION("3", "*MAP_XXXXXX"); + } + return; +} + +// ------------------------------------------------------------------------------------------------ +bool Parser::ParseString(std::string &out, const char *szName) { + char szBuffer[1024]; + if (!SkipSpaces(&filePtr)) { + + ai_snprintf(szBuffer, 1024, "Unable to parse %s block: Unexpected EOL", szName); + LogWarning(szBuffer); + return false; + } + // there must be '"' + if ('\"' != *filePtr) { + + ai_snprintf(szBuffer, 1024, "Unable to parse %s block: Strings are expected " + "to be enclosed in double quotation marks", + szName); + LogWarning(szBuffer); + return false; + } + ++filePtr; + const char *sz = filePtr; + while (true) { + if ('\"' == *sz) + break; + else if ('\0' == *sz) { + ai_snprintf(szBuffer, 1024, "Unable to parse %s block: Strings are expected to " + "be enclosed in double quotation marks but EOF was reached before " + "a closing quotation mark was encountered", + szName); + LogWarning(szBuffer); + return false; + } + sz++; + } + out = std::string(filePtr, (uintptr_t)sz - (uintptr_t)filePtr); + filePtr = sz + 1; + return true; +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV1ObjectBlock(ASE::BaseNode &node) { + AI_ASE_PARSER_INIT(); + while (true) { + if ('*' == *filePtr) { + ++filePtr; + + // first process common tokens such as node name and transform + // name of the mesh/node + if (TokenMatch(filePtr, "NODE_NAME", 9)) { + if (!ParseString(node.mName, "*NODE_NAME")) + SkipToNextToken(); + continue; + } + // name of the parent of the node + if (TokenMatch(filePtr, "NODE_PARENT", 11)) { + if (!ParseString(node.mParent, "*NODE_PARENT")) + SkipToNextToken(); + continue; + } + // transformation matrix of the node + if (TokenMatch(filePtr, "NODE_TM", 7)) { + ParseLV2NodeTransformBlock(node); + continue; + } + // animation data of the node + if (TokenMatch(filePtr, "TM_ANIMATION", 12)) { + ParseLV2AnimationBlock(node); + continue; + } + + if (node.mType == BaseNode::Light) { + // light settings + if (TokenMatch(filePtr, "LIGHT_SETTINGS", 14)) { + ParseLV2LightSettingsBlock((ASE::Light &)node); + continue; + } + // type of the light source + if (TokenMatch(filePtr, "LIGHT_TYPE", 10)) { + if (!ASSIMP_strincmp("omni", filePtr, 4)) { + ((ASE::Light &)node).mLightType = ASE::Light::OMNI; + } else if (!ASSIMP_strincmp("target", filePtr, 6)) { + ((ASE::Light &)node).mLightType = ASE::Light::TARGET; + } else if (!ASSIMP_strincmp("free", filePtr, 4)) { + ((ASE::Light &)node).mLightType = ASE::Light::FREE; + } else if (!ASSIMP_strincmp("directional", filePtr, 11)) { + ((ASE::Light &)node).mLightType = ASE::Light::DIRECTIONAL; + } else { + LogWarning("Unknown kind of light source"); + } + continue; + } + } else if (node.mType == BaseNode::Camera) { + // Camera settings + if (TokenMatch(filePtr, "CAMERA_SETTINGS", 15)) { + ParseLV2CameraSettingsBlock((ASE::Camera &)node); + continue; + } else if (TokenMatch(filePtr, "CAMERA_TYPE", 11)) { + if (!ASSIMP_strincmp("target", filePtr, 6)) { + ((ASE::Camera &)node).mCameraType = ASE::Camera::TARGET; + } else if (!ASSIMP_strincmp("free", filePtr, 4)) { + ((ASE::Camera &)node).mCameraType = ASE::Camera::FREE; + } else { + LogWarning("Unknown kind of camera"); + } + continue; + } + } else if (node.mType == BaseNode::Mesh) { + // mesh data + // FIX: Older files use MESH_SOFTSKIN + if (TokenMatch(filePtr, "MESH", 4) || + TokenMatch(filePtr, "MESH_SOFTSKIN", 13)) { + ParseLV2MeshBlock((ASE::Mesh &)node); + continue; + } + // mesh material index + if (TokenMatch(filePtr, "MATERIAL_REF", 12)) { + ParseLV4MeshLong(((ASE::Mesh &)node).iMaterialIndex); + continue; + } + } + } + AI_ASE_HANDLE_TOP_LEVEL_SECTION(); + } + return; +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2CameraSettingsBlock(ASE::Camera &camera) { + AI_ASE_PARSER_INIT(); + while (true) { + if ('*' == *filePtr) { + ++filePtr; + if (TokenMatch(filePtr, "CAMERA_NEAR", 11)) { + ParseLV4MeshFloat(camera.mNear); + continue; + } + if (TokenMatch(filePtr, "CAMERA_FAR", 10)) { + ParseLV4MeshFloat(camera.mFar); + continue; + } + if (TokenMatch(filePtr, "CAMERA_FOV", 10)) { + ParseLV4MeshFloat(camera.mFOV); + continue; + } + } + AI_ASE_HANDLE_SECTION("2", "CAMERA_SETTINGS"); + } + return; +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2LightSettingsBlock(ASE::Light &light) { + AI_ASE_PARSER_INIT(); + while (true) { + if ('*' == *filePtr) { + ++filePtr; + if (TokenMatch(filePtr, "LIGHT_COLOR", 11)) { + ParseLV4MeshFloatTriple(&light.mColor.r); + continue; + } + if (TokenMatch(filePtr, "LIGHT_INTENS", 12)) { + ParseLV4MeshFloat(light.mIntensity); + continue; + } + if (TokenMatch(filePtr, "LIGHT_HOTSPOT", 13)) { + ParseLV4MeshFloat(light.mAngle); + continue; + } + if (TokenMatch(filePtr, "LIGHT_FALLOFF", 13)) { + ParseLV4MeshFloat(light.mFalloff); + continue; + } + } + AI_ASE_HANDLE_SECTION("2", "LIGHT_SETTINGS"); + } +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2AnimationBlock(ASE::BaseNode &mesh) { + AI_ASE_PARSER_INIT(); + + ASE::Animation *anim = &mesh.mAnim; + while (true) { + if ('*' == *filePtr) { + ++filePtr; + if (TokenMatch(filePtr, "NODE_NAME", 9)) { + std::string temp; + if (!ParseString(temp, "*NODE_NAME")) + SkipToNextToken(); + + // If the name of the node contains .target it + // represents an animated camera or spot light + // target. + if (std::string::npos != temp.find(".Target")) { + if ((mesh.mType != BaseNode::Camera || ((ASE::Camera &)mesh).mCameraType != ASE::Camera::TARGET) && + (mesh.mType != BaseNode::Light || ((ASE::Light &)mesh).mLightType != ASE::Light::TARGET)) { + + ASSIMP_LOG_ERROR("ASE: Found target animation channel " + "but the node is neither a camera nor a spot light"); + anim = nullptr; + } else + anim = &mesh.mTargetAnim; + } + continue; + } + + // position keyframes + if (TokenMatch(filePtr, "CONTROL_POS_TRACK", 17) || + TokenMatch(filePtr, "CONTROL_POS_BEZIER", 18) || + TokenMatch(filePtr, "CONTROL_POS_TCB", 15)) { + if (!anim) + SkipSection(); + else + ParseLV3PosAnimationBlock(*anim); + continue; + } + // scaling keyframes + if (TokenMatch(filePtr, "CONTROL_SCALE_TRACK", 19) || + TokenMatch(filePtr, "CONTROL_SCALE_BEZIER", 20) || + TokenMatch(filePtr, "CONTROL_SCALE_TCB", 17)) { + if (!anim || anim == &mesh.mTargetAnim) { + // Target animation channels may have no rotation channels + ASSIMP_LOG_ERROR("ASE: Ignoring scaling channel in target animation"); + SkipSection(); + } else + ParseLV3ScaleAnimationBlock(*anim); + continue; + } + // rotation keyframes + if (TokenMatch(filePtr, "CONTROL_ROT_TRACK", 17) || + TokenMatch(filePtr, "CONTROL_ROT_BEZIER", 18) || + TokenMatch(filePtr, "CONTROL_ROT_TCB", 15)) { + if (!anim || anim == &mesh.mTargetAnim) { + // Target animation channels may have no rotation channels + ASSIMP_LOG_ERROR("ASE: Ignoring rotation channel in target animation"); + SkipSection(); + } else + ParseLV3RotAnimationBlock(*anim); + continue; + } + } + AI_ASE_HANDLE_SECTION("2", "TM_ANIMATION"); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3ScaleAnimationBlock(ASE::Animation &anim) { + AI_ASE_PARSER_INIT(); + unsigned int iIndex; + + while (true) { + if ('*' == *filePtr) { + ++filePtr; + + bool b = false; + + // For the moment we're just reading the three floats - + // we ignore the additional information for bezier's and TCBs + + // simple scaling keyframe + if (TokenMatch(filePtr, "CONTROL_SCALE_SAMPLE", 20)) { + b = true; + anim.mScalingType = ASE::Animation::TRACK; + } + + // Bezier scaling keyframe + if (TokenMatch(filePtr, "CONTROL_BEZIER_SCALE_KEY", 24)) { + b = true; + anim.mScalingType = ASE::Animation::BEZIER; + } + // TCB scaling keyframe + if (TokenMatch(filePtr, "CONTROL_TCB_SCALE_KEY", 21)) { + b = true; + anim.mScalingType = ASE::Animation::TCB; + } + if (b) { + anim.akeyScaling.push_back(aiVectorKey()); + aiVectorKey &key = anim.akeyScaling.back(); + ParseLV4MeshFloatTriple(&key.mValue.x, iIndex); + key.mTime = (double)iIndex; + } + } + AI_ASE_HANDLE_SECTION("3", "*CONTROL_POS_TRACK"); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3PosAnimationBlock(ASE::Animation &anim) { + AI_ASE_PARSER_INIT(); + unsigned int iIndex; + while (true) { + if ('*' == *filePtr) { + ++filePtr; + + bool b = false; + + // For the moment we're just reading the three floats - + // we ignore the additional information for bezier's and TCBs + + // simple scaling keyframe + if (TokenMatch(filePtr, "CONTROL_POS_SAMPLE", 18)) { + b = true; + anim.mPositionType = ASE::Animation::TRACK; + } + + // Bezier scaling keyframe + if (TokenMatch(filePtr, "CONTROL_BEZIER_POS_KEY", 22)) { + b = true; + anim.mPositionType = ASE::Animation::BEZIER; + } + // TCB scaling keyframe + if (TokenMatch(filePtr, "CONTROL_TCB_POS_KEY", 19)) { + b = true; + anim.mPositionType = ASE::Animation::TCB; + } + if (b) { + anim.akeyPositions.push_back(aiVectorKey()); + aiVectorKey &key = anim.akeyPositions.back(); + ParseLV4MeshFloatTriple(&key.mValue.x, iIndex); + key.mTime = (double)iIndex; + } + } + AI_ASE_HANDLE_SECTION("3", "*CONTROL_POS_TRACK"); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3RotAnimationBlock(ASE::Animation &anim) { + AI_ASE_PARSER_INIT(); + unsigned int iIndex; + while (true) { + if ('*' == *filePtr) { + ++filePtr; + + bool b = false; + + // For the moment we're just reading the floats - + // we ignore the additional information for bezier's and TCBs + + // simple scaling keyframe + if (TokenMatch(filePtr, "CONTROL_ROT_SAMPLE", 18)) { + b = true; + anim.mRotationType = ASE::Animation::TRACK; + } + + // Bezier scaling keyframe + if (TokenMatch(filePtr, "CONTROL_BEZIER_ROT_KEY", 22)) { + b = true; + anim.mRotationType = ASE::Animation::BEZIER; + } + // TCB scaling keyframe + if (TokenMatch(filePtr, "CONTROL_TCB_ROT_KEY", 19)) { + b = true; + anim.mRotationType = ASE::Animation::TCB; + } + if (b) { + anim.akeyRotations.push_back(aiQuatKey()); + aiQuatKey &key = anim.akeyRotations.back(); + aiVector3D v; + ai_real f; + ParseLV4MeshFloatTriple(&v.x, iIndex); + ParseLV4MeshFloat(f); + key.mTime = (double)iIndex; + key.mValue = aiQuaternion(v, f); + } + } + AI_ASE_HANDLE_SECTION("3", "*CONTROL_ROT_TRACK"); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2NodeTransformBlock(ASE::BaseNode &mesh) { + AI_ASE_PARSER_INIT(); + int mode = 0; + while (true) { + if ('*' == *filePtr) { + ++filePtr; + // name of the node + if (TokenMatch(filePtr, "NODE_NAME", 9)) { + std::string temp; + if (!ParseString(temp, "*NODE_NAME")) + SkipToNextToken(); + + std::string::size_type s; + if (temp == mesh.mName) { + mode = 1; + } else if (std::string::npos != (s = temp.find(".Target")) && + mesh.mName == temp.substr(0, s)) { + // This should be either a target light or a target camera + if ((mesh.mType == BaseNode::Light && ((ASE::Light &)mesh).mLightType == ASE::Light::TARGET) || + (mesh.mType == BaseNode::Camera && ((ASE::Camera &)mesh).mCameraType == ASE::Camera::TARGET)) { + mode = 2; + } else { + ASSIMP_LOG_ERROR("ASE: Ignoring target transform, " + "this is no spot light or target camera"); + } + } else { + ASSIMP_LOG_ERROR("ASE: Unknown node transformation: ", temp); + // mode = 0 + } + continue; + } + if (mode) { + // fourth row of the transformation matrix - and also the + // only information here that is interesting for targets + if (TokenMatch(filePtr, "TM_ROW3", 7)) { + ParseLV4MeshFloatTriple((mode == 1 ? mesh.mTransform[3] : &mesh.mTargetPosition.x)); + continue; + } + if (mode == 1) { + // first row of the transformation matrix + if (TokenMatch(filePtr, "TM_ROW0", 7)) { + ParseLV4MeshFloatTriple(mesh.mTransform[0]); + continue; + } + // second row of the transformation matrix + if (TokenMatch(filePtr, "TM_ROW1", 7)) { + ParseLV4MeshFloatTriple(mesh.mTransform[1]); + continue; + } + // third row of the transformation matrix + if (TokenMatch(filePtr, "TM_ROW2", 7)) { + ParseLV4MeshFloatTriple(mesh.mTransform[2]); + continue; + } + // inherited position axes + if (TokenMatch(filePtr, "INHERIT_POS", 11)) { + unsigned int aiVal[3]; + ParseLV4MeshLongTriple(aiVal); + + for (unsigned int i = 0; i < 3; ++i) + mesh.inherit.abInheritPosition[i] = aiVal[i] != 0; + continue; + } + // inherited rotation axes + if (TokenMatch(filePtr, "INHERIT_ROT", 11)) { + unsigned int aiVal[3]; + ParseLV4MeshLongTriple(aiVal); + + for (unsigned int i = 0; i < 3; ++i) + mesh.inherit.abInheritRotation[i] = aiVal[i] != 0; + continue; + } + // inherited scaling axes + if (TokenMatch(filePtr, "INHERIT_SCL", 11)) { + unsigned int aiVal[3]; + ParseLV4MeshLongTriple(aiVal); + + for (unsigned int i = 0; i < 3; ++i) + mesh.inherit.abInheritScaling[i] = aiVal[i] != 0; + continue; + } + } + } + } + AI_ASE_HANDLE_SECTION("2", "*NODE_TM"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2MeshBlock(ASE::Mesh &mesh) { + AI_ASE_PARSER_INIT(); + + unsigned int iNumVertices = 0; + unsigned int iNumFaces = 0; + unsigned int iNumTVertices = 0; + unsigned int iNumTFaces = 0; + unsigned int iNumCVertices = 0; + unsigned int iNumCFaces = 0; + while (true) { + if ('*' == *filePtr) { + ++filePtr; + // Number of vertices in the mesh + if (TokenMatch(filePtr, "MESH_NUMVERTEX", 14)) { + ParseLV4MeshLong(iNumVertices); + continue; + } + // Number of texture coordinates in the mesh + if (TokenMatch(filePtr, "MESH_NUMTVERTEX", 15)) { + ParseLV4MeshLong(iNumTVertices); + continue; + } + // Number of vertex colors in the mesh + if (TokenMatch(filePtr, "MESH_NUMCVERTEX", 15)) { + ParseLV4MeshLong(iNumCVertices); + continue; + } + // Number of regular faces in the mesh + if (TokenMatch(filePtr, "MESH_NUMFACES", 13)) { + ParseLV4MeshLong(iNumFaces); + continue; + } + // Number of UVWed faces in the mesh + if (TokenMatch(filePtr, "MESH_NUMTVFACES", 15)) { + ParseLV4MeshLong(iNumTFaces); + continue; + } + // Number of colored faces in the mesh + if (TokenMatch(filePtr, "MESH_NUMCVFACES", 15)) { + ParseLV4MeshLong(iNumCFaces); + continue; + } + // mesh vertex list block + if (TokenMatch(filePtr, "MESH_VERTEX_LIST", 16)) { + ParseLV3MeshVertexListBlock(iNumVertices, mesh); + continue; + } + // mesh face list block + if (TokenMatch(filePtr, "MESH_FACE_LIST", 14)) { + ParseLV3MeshFaceListBlock(iNumFaces, mesh); + continue; + } + // mesh texture vertex list block + if (TokenMatch(filePtr, "MESH_TVERTLIST", 14)) { + ParseLV3MeshTListBlock(iNumTVertices, mesh); + continue; + } + // mesh texture face block + if (TokenMatch(filePtr, "MESH_TFACELIST", 14)) { + ParseLV3MeshTFaceListBlock(iNumTFaces, mesh); + continue; + } + // mesh color vertex list block + if (TokenMatch(filePtr, "MESH_CVERTLIST", 14)) { + ParseLV3MeshCListBlock(iNumCVertices, mesh); + continue; + } + // mesh color face block + if (TokenMatch(filePtr, "MESH_CFACELIST", 14)) { + ParseLV3MeshCFaceListBlock(iNumCFaces, mesh); + continue; + } + // mesh normals + if (TokenMatch(filePtr, "MESH_NORMALS", 12)) { + ParseLV3MeshNormalListBlock(mesh); + continue; + } + // another mesh UV channel ... + if (TokenMatch(filePtr, "MESH_MAPPINGCHANNEL", 19)) { + unsigned int iIndex(0); + ParseLV4MeshLong(iIndex); + if (0 == iIndex) { + LogWarning("Mapping channel has an invalid index. Skipping UV channel"); + // skip it ... + SkipSection(); + } else { + if (iIndex < 2) { + LogWarning("Mapping channel has an invalid index. Skipping UV channel"); + // skip it ... + SkipSection(); + } + if (iIndex > AI_MAX_NUMBER_OF_TEXTURECOORDS) { + LogWarning("Too many UV channels specified. Skipping channel .."); + // skip it ... + SkipSection(); + } else { + // parse the mapping channel + ParseLV3MappingChannel(iIndex - 1, mesh); + } + continue; + } + } + // mesh animation keyframe. Not supported + if (TokenMatch(filePtr, "MESH_ANIMATION", 14)) { + + LogWarning("Found *MESH_ANIMATION element in ASE/ASK file. " + "Keyframe animation is not supported by Assimp, this element " + "will be ignored"); + //SkipSection(); + continue; + } + if (TokenMatch(filePtr, "MESH_WEIGHTS", 12)) { + ParseLV3MeshWeightsBlock(mesh); + continue; + } + } + AI_ASE_HANDLE_SECTION("2", "*MESH"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshWeightsBlock(ASE::Mesh &mesh) { + AI_ASE_PARSER_INIT(); + + unsigned int iNumVertices = 0, iNumBones = 0; + while (true) { + if ('*' == *filePtr) { + ++filePtr; + + // Number of bone vertices ... + if (TokenMatch(filePtr, "MESH_NUMVERTEX", 14)) { + ParseLV4MeshLong(iNumVertices); + continue; + } + // Number of bones + if (TokenMatch(filePtr, "MESH_NUMBONE", 12)) { + ParseLV4MeshLong(iNumBones); + continue; + } + // parse the list of bones + if (TokenMatch(filePtr, "MESH_BONE_LIST", 14)) { + ParseLV4MeshBones(iNumBones, mesh); + continue; + } + // parse the list of bones vertices + if (TokenMatch(filePtr, "MESH_BONE_VERTEX_LIST", 21)) { + ParseLV4MeshBonesVertices(iNumVertices, mesh); + continue; + } + } + AI_ASE_HANDLE_SECTION("3", "*MESH_WEIGHTS"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshBones(unsigned int iNumBones, ASE::Mesh &mesh) { + AI_ASE_PARSER_INIT(); + mesh.mBones.resize(iNumBones, Bone("UNNAMED")); + while (true) { + if ('*' == *filePtr) { + ++filePtr; + + // Mesh bone with name ... + if (TokenMatch(filePtr, "MESH_BONE_NAME", 14)) { + // parse an index ... + if (SkipSpaces(&filePtr)) { + unsigned int iIndex = strtoul10(filePtr, &filePtr); + if (iIndex >= iNumBones) { + LogWarning("Bone index is out of bounds"); + continue; + } + if (!ParseString(mesh.mBones[iIndex].mName, "*MESH_BONE_NAME")) + SkipToNextToken(); + continue; + } + } + } + AI_ASE_HANDLE_SECTION("3", "*MESH_BONE_LIST"); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshBonesVertices(unsigned int iNumVertices, ASE::Mesh &mesh) { + AI_ASE_PARSER_INIT(); + mesh.mBoneVertices.resize(iNumVertices); + while (true) { + if ('*' == *filePtr) { + ++filePtr; + + // Mesh bone vertex + if (TokenMatch(filePtr, "MESH_BONE_VERTEX", 16)) { + // read the vertex index + unsigned int iIndex = strtoul10(filePtr, &filePtr); + if (iIndex >= mesh.mPositions.size()) { + iIndex = (unsigned int)mesh.mPositions.size() - 1; + LogWarning("Bone vertex index is out of bounds. Using the largest valid " + "bone vertex index instead"); + } + + // --- ignored + ai_real afVert[3]; + ParseLV4MeshFloatTriple(afVert); + + std::pair<int, float> pairOut; + while (true) { + // first parse the bone index ... + if (!SkipSpaces(&filePtr)) break; + pairOut.first = strtoul10(filePtr, &filePtr); + + // then parse the vertex weight + if (!SkipSpaces(&filePtr)) break; + filePtr = fast_atoreal_move<float>(filePtr, pairOut.second); + + // -1 marks unused entries + if (-1 != pairOut.first) { + mesh.mBoneVertices[iIndex].mBoneWeights.push_back(pairOut); + } + } + continue; + } + } + AI_ASE_HANDLE_SECTION("4", "*MESH_BONE_VERTEX"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshVertexListBlock( + unsigned int iNumVertices, ASE::Mesh &mesh) { + AI_ASE_PARSER_INIT(); + + // allocate enough storage in the array + mesh.mPositions.resize(iNumVertices); + while (true) { + if ('*' == *filePtr) { + ++filePtr; + + // Vertex entry + if (TokenMatch(filePtr, "MESH_VERTEX", 11)) { + + aiVector3D vTemp; + unsigned int iIndex; + ParseLV4MeshFloatTriple(&vTemp.x, iIndex); + + if (iIndex >= iNumVertices) { + LogWarning("Invalid vertex index. It will be ignored"); + } else + mesh.mPositions[iIndex] = vTemp; + continue; + } + } + AI_ASE_HANDLE_SECTION("3", "*MESH_VERTEX_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshFaceListBlock(unsigned int iNumFaces, ASE::Mesh &mesh) { + AI_ASE_PARSER_INIT(); + + // allocate enough storage in the face array + mesh.mFaces.resize(iNumFaces); + while (true) { + if ('*' == *filePtr) { + ++filePtr; + + // Face entry + if (TokenMatch(filePtr, "MESH_FACE", 9)) { + + ASE::Face mFace; + ParseLV4MeshFace(mFace); + + if (mFace.iFace >= iNumFaces) { + LogWarning("Face has an invalid index. It will be ignored"); + } else + mesh.mFaces[mFace.iFace] = mFace; + continue; + } + } + AI_ASE_HANDLE_SECTION("3", "*MESH_FACE_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshTListBlock(unsigned int iNumVertices, + ASE::Mesh &mesh, unsigned int iChannel) { + AI_ASE_PARSER_INIT(); + + // allocate enough storage in the array + mesh.amTexCoords[iChannel].resize(iNumVertices); + while (true) { + if ('*' == *filePtr) { + ++filePtr; + + // Vertex entry + if (TokenMatch(filePtr, "MESH_TVERT", 10)) { + aiVector3D vTemp; + unsigned int iIndex; + ParseLV4MeshFloatTriple(&vTemp.x, iIndex); + + if (iIndex >= iNumVertices) { + LogWarning("Tvertex has an invalid index. It will be ignored"); + } else + mesh.amTexCoords[iChannel][iIndex] = vTemp; + + if (0.0f != vTemp.z) { + // we need 3 coordinate channels + mesh.mNumUVComponents[iChannel] = 3; + } + continue; + } + } + AI_ASE_HANDLE_SECTION("3", "*MESH_TVERT_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshTFaceListBlock(unsigned int iNumFaces, + ASE::Mesh &mesh, unsigned int iChannel) { + AI_ASE_PARSER_INIT(); + while (true) { + if ('*' == *filePtr) { + ++filePtr; + + // Face entry + if (TokenMatch(filePtr, "MESH_TFACE", 10)) { + unsigned int aiValues[3]; + unsigned int iIndex = 0; + + ParseLV4MeshLongTriple(aiValues, iIndex); + if (iIndex >= iNumFaces || iIndex >= mesh.mFaces.size()) { + LogWarning("UV-Face has an invalid index. It will be ignored"); + } else { + // copy UV indices + mesh.mFaces[iIndex].amUVIndices[iChannel][0] = aiValues[0]; + mesh.mFaces[iIndex].amUVIndices[iChannel][1] = aiValues[1]; + mesh.mFaces[iIndex].amUVIndices[iChannel][2] = aiValues[2]; + } + continue; + } + } + AI_ASE_HANDLE_SECTION("3", "*MESH_TFACE_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MappingChannel(unsigned int iChannel, ASE::Mesh &mesh) { + AI_ASE_PARSER_INIT(); + + unsigned int iNumTVertices = 0; + unsigned int iNumTFaces = 0; + while (true) { + if ('*' == *filePtr) { + ++filePtr; + + // Number of texture coordinates in the mesh + if (TokenMatch(filePtr, "MESH_NUMTVERTEX", 15)) { + ParseLV4MeshLong(iNumTVertices); + continue; + } + // Number of UVWed faces in the mesh + if (TokenMatch(filePtr, "MESH_NUMTVFACES", 15)) { + ParseLV4MeshLong(iNumTFaces); + continue; + } + // mesh texture vertex list block + if (TokenMatch(filePtr, "MESH_TVERTLIST", 14)) { + ParseLV3MeshTListBlock(iNumTVertices, mesh, iChannel); + continue; + } + // mesh texture face block + if (TokenMatch(filePtr, "MESH_TFACELIST", 14)) { + ParseLV3MeshTFaceListBlock(iNumTFaces, mesh, iChannel); + continue; + } + } + AI_ASE_HANDLE_SECTION("3", "*MESH_MAPPING_CHANNEL"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshCListBlock(unsigned int iNumVertices, ASE::Mesh &mesh) { + AI_ASE_PARSER_INIT(); + + // allocate enough storage in the array + mesh.mVertexColors.resize(iNumVertices); + while (true) { + if ('*' == *filePtr) { + ++filePtr; + + // Vertex entry + if (TokenMatch(filePtr, "MESH_VERTCOL", 12)) { + aiColor4D vTemp; + vTemp.a = 1.0f; + unsigned int iIndex; + ParseLV4MeshFloatTriple(&vTemp.r, iIndex); + + if (iIndex >= iNumVertices) { + LogWarning("Vertex color has an invalid index. It will be ignored"); + } else + mesh.mVertexColors[iIndex] = vTemp; + continue; + } + } + AI_ASE_HANDLE_SECTION("3", "*MESH_CVERTEX_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshCFaceListBlock(unsigned int iNumFaces, ASE::Mesh &mesh) { + AI_ASE_PARSER_INIT(); + while (true) { + if ('*' == *filePtr) { + ++filePtr; + + // Face entry + if (TokenMatch(filePtr, "MESH_CFACE", 10)) { + unsigned int aiValues[3]; + unsigned int iIndex = 0; + + ParseLV4MeshLongTriple(aiValues, iIndex); + if (iIndex >= iNumFaces || iIndex >= mesh.mFaces.size()) { + LogWarning("UV-Face has an invalid index. It will be ignored"); + } else { + // copy color indices + mesh.mFaces[iIndex].mColorIndices[0] = aiValues[0]; + mesh.mFaces[iIndex].mColorIndices[1] = aiValues[1]; + mesh.mFaces[iIndex].mColorIndices[2] = aiValues[2]; + } + continue; + } + } + AI_ASE_HANDLE_SECTION("3", "*MESH_CFACE_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh &sMesh) { + AI_ASE_PARSER_INIT(); + + // Allocate enough storage for the normals + sMesh.mNormals.resize(sMesh.mFaces.size() * 3, aiVector3D(0.f, 0.f, 0.f)); + unsigned int index, faceIdx = UINT_MAX; + + // FIXME: rewrite this and find out how to interpret the normals + // correctly. This is crap. + + // Smooth the vertex and face normals together. The result + // will be edgy then, but otherwise everything would be soft ... + while (true) { + if ('*' == *filePtr) { + ++filePtr; + if (faceIdx != UINT_MAX && TokenMatch(filePtr, "MESH_VERTEXNORMAL", 17)) { + aiVector3D vNormal; + ParseLV4MeshFloatTriple(&vNormal.x, index); + if (faceIdx >= sMesh.mFaces.size()) + continue; + + // Make sure we assign it to the correct face + const ASE::Face &face = sMesh.mFaces[faceIdx]; + if (index == face.mIndices[0]) + index = 0; + else if (index == face.mIndices[1]) + index = 1; + else if (index == face.mIndices[2]) + index = 2; + else { + ASSIMP_LOG_ERROR("ASE: Invalid vertex index in MESH_VERTEXNORMAL section"); + continue; + } + // We'll renormalize later + sMesh.mNormals[faceIdx * 3 + index] += vNormal; + continue; + } + if (TokenMatch(filePtr, "MESH_FACENORMAL", 15)) { + aiVector3D vNormal; + ParseLV4MeshFloatTriple(&vNormal.x, faceIdx); + + if (faceIdx >= sMesh.mFaces.size()) { + ASSIMP_LOG_ERROR("ASE: Invalid vertex index in MESH_FACENORMAL section"); + continue; + } + + // We'll renormalize later + sMesh.mNormals[faceIdx * 3] += vNormal; + sMesh.mNormals[faceIdx * 3 + 1] += vNormal; + sMesh.mNormals[faceIdx * 3 + 2] += vNormal; + continue; + } + } + AI_ASE_HANDLE_SECTION("3", "*MESH_NORMALS"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshFace(ASE::Face &out) { + // skip spaces and tabs + if (!SkipSpaces(&filePtr)) { + LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL [#1]"); + SkipToNextToken(); + return; + } + + // parse the face index + out.iFace = strtoul10(filePtr, &filePtr); + + // next character should be ':' + if (!SkipSpaces(&filePtr)) { + // FIX: there are some ASE files which haven't got : here .... + LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. \':\' expected [#2]"); + SkipToNextToken(); + return; + } + // FIX: There are some ASE files which haven't got ':' here + if (':' == *filePtr) ++filePtr; + + // Parse all mesh indices + for (unsigned int i = 0; i < 3; ++i) { + unsigned int iIndex = 0; + if (!SkipSpaces(&filePtr)) { + LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL"); + SkipToNextToken(); + return; + } + switch (*filePtr) { + case 'A': + case 'a': + break; + case 'B': + case 'b': + iIndex = 1; + break; + case 'C': + case 'c': + iIndex = 2; + break; + default: + LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. " + "A,B or C expected [#3]"); + SkipToNextToken(); + return; + }; + ++filePtr; + + // next character should be ':' + if (!SkipSpaces(&filePtr) || ':' != *filePtr) { + LogWarning("Unable to parse *MESH_FACE Element: " + "Unexpected EOL. \':\' expected [#2]"); + SkipToNextToken(); + return; + } + + ++filePtr; + if (!SkipSpaces(&filePtr)) { + LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. " + "Vertex index ecpected [#4]"); + SkipToNextToken(); + return; + } + out.mIndices[iIndex] = strtoul10(filePtr, &filePtr); + } + + // now we need to skip the AB, BC, CA blocks. + while (true) { + if ('*' == *filePtr) break; + if (IsLineEnd(*filePtr)) { + //iLineNumber++; + return; + } + filePtr++; + } + + // parse the smoothing group of the face + if (TokenMatch(filePtr, "*MESH_SMOOTHING", 15)) { + if (!SkipSpaces(&filePtr)) { + LogWarning("Unable to parse *MESH_SMOOTHING Element: " + "Unexpected EOL. Smoothing group(s) expected [#5]"); + SkipToNextToken(); + return; + } + + // Parse smoothing groups until we don't anymore see commas + // FIX: There needn't always be a value, sad but true + while (true) { + if (*filePtr < '9' && *filePtr >= '0') { + out.iSmoothGroup |= (1 << strtoul10(filePtr, &filePtr)); + } + SkipSpaces(&filePtr); + if (',' != *filePtr) { + break; + } + ++filePtr; + SkipSpaces(&filePtr); + } + } + + // *MESH_MTLID is optional, too + while (true) { + if ('*' == *filePtr) { + break; + } + if (IsLineEnd(*filePtr)) { + return; + } + filePtr++; + } + + if (TokenMatch(filePtr, "*MESH_MTLID", 11)) { + if (!SkipSpaces(&filePtr)) { + LogWarning("Unable to parse *MESH_MTLID Element: Unexpected EOL. " + "Material index expected [#6]"); + SkipToNextToken(); + return; + } + out.iMaterial = strtoul10(filePtr, &filePtr); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshLongTriple(unsigned int *apOut) { + ai_assert(nullptr != apOut); + + for (unsigned int i = 0; i < 3; ++i) + ParseLV4MeshLong(apOut[i]); +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshLongTriple(unsigned int *apOut, unsigned int &rIndexOut) { + ai_assert(nullptr != apOut); + + // parse the index + ParseLV4MeshLong(rIndexOut); + + // parse the three others + ParseLV4MeshLongTriple(apOut); +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshFloatTriple(ai_real *apOut, unsigned int &rIndexOut) { + ai_assert(nullptr != apOut); + + // parse the index + ParseLV4MeshLong(rIndexOut); + + // parse the three others + ParseLV4MeshFloatTriple(apOut); +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshFloatTriple(ai_real *apOut) { + ai_assert(nullptr != apOut); + + for (unsigned int i = 0; i < 3; ++i) { + ParseLV4MeshFloat(apOut[i]); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshFloat(ai_real &fOut) { + // skip spaces and tabs + if (!SkipSpaces(&filePtr)) { + // LOG + LogWarning("Unable to parse float: unexpected EOL [#1]"); + fOut = 0.0; + ++iLineNumber; + return; + } + // parse the first float + filePtr = fast_atoreal_move<ai_real>(filePtr, fOut); +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshLong(unsigned int &iOut) { + // Skip spaces and tabs + if (!SkipSpaces(&filePtr)) { + // LOG + LogWarning("Unable to parse long: unexpected EOL [#1]"); + iOut = 0; + ++iLineNumber; + return; + } + // parse the value + iOut = strtoul10(filePtr, &filePtr); +} + +#endif // ASSIMP_BUILD_NO_3DS_IMPORTER + +#endif // !! ASSIMP_BUILD_NO_BASE_IMPORTER |