diff options
Diffstat (limited to 'libs/assimp/tools/assimp_cmd')
-rw-r--r-- | libs/assimp/tools/assimp_cmd/CMakeLists.txt | 73 | ||||
-rw-r--r-- | libs/assimp/tools/assimp_cmd/CompareDump.cpp | 956 | ||||
-rw-r--r-- | libs/assimp/tools/assimp_cmd/Export.cpp | 166 | ||||
-rw-r--r-- | libs/assimp/tools/assimp_cmd/ImageExtractor.cpp | 361 | ||||
-rw-r--r-- | libs/assimp/tools/assimp_cmd/Info.cpp | 483 | ||||
-rw-r--r-- | libs/assimp/tools/assimp_cmd/Main.cpp | 564 | ||||
-rw-r--r-- | libs/assimp/tools/assimp_cmd/Main.h | 273 | ||||
-rw-r--r-- | libs/assimp/tools/assimp_cmd/WriteDump.cpp | 172 | ||||
-rw-r--r-- | libs/assimp/tools/assimp_cmd/assimp_cmd.rc | 51 | ||||
-rw-r--r-- | libs/assimp/tools/assimp_cmd/generic_inserter.hpp | 113 | ||||
-rw-r--r-- | libs/assimp/tools/assimp_cmd/resource.h | 21 |
11 files changed, 3233 insertions, 0 deletions
diff --git a/libs/assimp/tools/assimp_cmd/CMakeLists.txt b/libs/assimp/tools/assimp_cmd/CMakeLists.txt new file mode 100644 index 0000000..6f4b613 --- /dev/null +++ b/libs/assimp/tools/assimp_cmd/CMakeLists.txt @@ -0,0 +1,73 @@ +# 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. +# +#---------------------------------------------------------------------- +cmake_minimum_required( VERSION 3.10 ) + +INCLUDE_DIRECTORIES( + ${Assimp_SOURCE_DIR}/include + ${Assimp_SOURCE_DIR}/code + ${Assimp_BINARY_DIR}/tools/assimp_cmd +) + +LINK_DIRECTORIES( ${Assimp_BINARY_DIR} ${Assimp_BINARY_DIR}/lib ) + +ADD_EXECUTABLE( assimp_cmd + assimp_cmd.rc + CompareDump.cpp + ImageExtractor.cpp + Main.cpp + Main.h + resource.h + WriteDump.cpp + Info.cpp + Export.cpp +) + +TARGET_USE_COMMON_OUTPUT_DIRECTORY(assimp_cmd) + +SET_PROPERTY(TARGET assimp_cmd PROPERTY DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) + +TARGET_LINK_LIBRARIES( assimp_cmd assimp ${ZLIB_LIBRARIES} ) +SET_TARGET_PROPERTIES( assimp_cmd PROPERTIES + OUTPUT_NAME assimp +) + +INSTALL( TARGETS assimp_cmd + DESTINATION "${ASSIMP_BIN_INSTALL_DIR}" COMPONENT assimp-bin +) diff --git a/libs/assimp/tools/assimp_cmd/CompareDump.cpp b/libs/assimp/tools/assimp_cmd/CompareDump.cpp new file mode 100644 index 0000000..49f680c --- /dev/null +++ b/libs/assimp/tools/assimp_cmd/CompareDump.cpp @@ -0,0 +1,956 @@ +/* +--------------------------------------------------------------------------- +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 CompareDump.cpp + * @brief Implementation of the 'assimp cmpdmp', which compares + * two model dumps for equality. It plays an important role + * in the regression test suite. + */ + +#include "Main.h" +const char* AICMD_MSG_CMPDUMP_HELP = +"assimp cmpdump <actual> <expected>\n" +"\tCompare two short dumps produced with \'assimp dump <..> -s\' for equality.\n" +; + +#include "Common/assbin_chunks.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +#include "generic_inserter.hpp" +#include <map> +#include <deque> +#include <stack> +#include <sstream> +#include <iostream> +#include <assimp/ai_assert.h> + +// get << for aiString +template <typename char_t, typename traits_t> +void mysprint(std::basic_ostream<char_t, traits_t>& os, const aiString& vec) { + os << "[length: \'" << std::dec << vec.length << "\' content: \'" << vec.data << "\']"; +} + +template <typename char_t, typename traits_t> +std::basic_ostream<char_t, traits_t>& operator<< (std::basic_ostream<char_t, traits_t>& os, const aiString& vec) { + return generic_inserter(mysprint<char_t,traits_t>, os, vec); +} + +class sliced_chunk_iterator; +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// @class compare_fails_exception +/// +/// @brief Sentinel exception to return quickly from deeply nested control paths +//////////////////////////////////////////////////////////////////////////////////////////////////// +class compare_fails_exception : public virtual std::exception { +public: + + enum {MAX_ERR_LEN = 4096}; + + /* public c'tors */ + compare_fails_exception(const char* msg) { + strncpy(mywhat,msg,MAX_ERR_LEN-1); + strcat(mywhat,"\n"); + } + + /* public member functions */ + const char* what() const throw() { + return mywhat; + } + +private: + + char mywhat[MAX_ERR_LEN+1]; +}; + + +#define MY_FLT_EPSILON 1e-1f +#define MY_DBL_EPSILON 1e-1 +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// @class comparer_context +/// +/// @brief Record our way through the files to be compared and dump useful information if we fail. +//////////////////////////////////////////////////////////////////////////////////////////////////// +class comparer_context { + friend class sliced_chunk_iterator; + +public: + + /* construct given two file handles to compare */ + comparer_context(FILE* actual,FILE* expect) + : actual(actual) + , expect(expect) + , cnt_chunks(0) + { + ai_assert(actual); + ai_assert(expect); + + fseek(actual,0,SEEK_END); + lengths.push(std::make_pair(static_cast<uint32_t>(ftell(actual)),0)); + fseek(actual,0,SEEK_SET); + + history.push_back(HistoryEntry("---",PerChunkCounter())); + } + +public: + + + /* set new scope */ + void push_elem(const char* msg) { + const std::string s = msg; + + PerChunkCounter::const_iterator it = history.back().second.find(s); + if(it != history.back().second.end()) { + ++history.back().second[s]; + } + else history.back().second[s] = 0; + + history.push_back(HistoryEntry(s,PerChunkCounter())); + debug_trace.push_back("PUSH " + s); + } + + /* leave current scope */ + void pop_elem() { + ai_assert(history.size()); + debug_trace.push_back("POP "+ history.back().first); + history.pop_back(); + } + + + /* push current chunk length and start offset on top of stack */ + void push_length(uint32_t nl, uint32_t start) { + lengths.push(std::make_pair(nl,start)); + ++cnt_chunks; + } + + /* pop the chunk length stack */ + void pop_length() { + ai_assert(lengths.size()); + lengths.pop(); + } + + /* access the current chunk length */ + uint32_t get_latest_chunk_length() { + ai_assert(lengths.size()); + return lengths.top().first; + } + + /* access the current chunk start offset */ + uint32_t get_latest_chunk_start() { + ai_assert(lengths.size()); + return lengths.top().second; + } + + /* total number of chunk headers passed so far*/ + uint32_t get_num_chunks() { + return cnt_chunks; + } + + + /* get ACTUAL file desc. != NULL */ + FILE* get_actual() const { + return actual; + } + + /* get EXPECT file desc. != NULL */ + FILE* get_expect() const { + return expect; + } + + + /* compare next T from both streams, name occurs in error messages */ + template<typename T> T cmp(const std::string& name) { + T a,e; + read(a,e); + + if(a != e) { + std::stringstream ss; + failure((ss<< "Expected " << e << ", but actual is " << a, + ss.str()),name); + } + // std::cout << name << " " << std::hex << a << std::endl; + return a; + } + + /* compare next num T's from both streams, name occurs in error messages */ + template<typename T> void cmp(size_t num,const std::string& name) { + for(size_t n = 0; n < num; ++n) { + std::stringstream ss; + cmp<T>((ss<<name<<"["<<n<<"]",ss.str())); + // std::cout << name << " " << std::hex << a << std::endl; + } + } + + /* Bounds of an aiVector3D array (separate function + * because partial specializations of member functions are illegal--)*/ + template<typename T> void cmp_bounds(const std::string& name) { + cmp<T> (name+".<minimum-value>"); + cmp<T> (name+".<maximum-value>"); + } + +private: + + /* Report failure */ + AI_WONT_RETURN void failure(const std::string& err, const std::string& name) AI_WONT_RETURN_SUFFIX { + std::stringstream ss; + throw compare_fails_exception((ss + << "Files are different at " + << history.back().first + << "." + << name + << ".\nError is: " + << err + << ".\nCurrent position in scene hierarchy is " + << print_hierarchy(),ss.str().c_str() + )); + } + + /** print our 'stack' */ + std::string print_hierarchy() { + std::stringstream ss; + ss << "\n"; + + const char* last = history.back().first.c_str(); + std::string pad; + + for(ChunkHistory::reverse_iterator rev = history.rbegin(), + end = history.rend(); rev != end; ++rev, pad += " ") + { + ss << pad << (*rev).first << "(Index: " << (*rev).second[last] << ")" << "\n"; + last = (*rev).first.c_str(); + } + + ss << std::endl << "Debug trace: "<< "\n"; + for (std::vector<std::string>::const_iterator it = debug_trace.begin(); it != debug_trace.end(); ++it) { + ss << *it << "\n"; + } + ss << std::flush; + + return ss.str(); + } + + + /* read from both streams at the same time */ + template <typename T> void read(T& filla,T& fille) { + if(1 != fread(&filla,sizeof(T),1,actual)) { + EOFActual(); + } + if(1 != fread(&fille,sizeof(T),1,expect)) { + EOFExpect(); + } + } + +private: + + void EOFActual() { + std::stringstream ss; + throw compare_fails_exception((ss + << "Unexpected EOF reading ACTUAL.\nCurrent position in scene hierarchy is " + << print_hierarchy(),ss.str().c_str() + )); + } + + void EOFExpect() { + std::stringstream ss; + throw compare_fails_exception((ss + << "Unexpected EOF reading EXPECT.\nCurrent position in scene hierarchy is " + << print_hierarchy(),ss.str().c_str() + )); + } + + + FILE *const actual, *const expect; + + typedef std::map<std::string,unsigned int> PerChunkCounter; + typedef std::pair<std::string,PerChunkCounter> HistoryEntry; + + typedef std::deque<HistoryEntry> ChunkHistory; + ChunkHistory history; + + std::vector<std::string> debug_trace; + + typedef std::stack<std::pair<uint32_t,uint32_t> > LengthStack; + LengthStack lengths; + + uint32_t cnt_chunks; +}; + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/* specialization for aiString (it needs separate handling because its on-disk representation + * differs from its binary representation in memory and can't be treated as an array of n T's.*/ +template <> void comparer_context :: read<aiString>(aiString& filla,aiString& fille) { + uint32_t lena,lene; + read(lena,lene); + + if(lena && 1 != fread(&filla.data,lena,1,actual)) { + EOFActual(); + } + if(lene && 1 != fread(&fille.data,lene,1,expect)) { + EOFExpect(); + } + + fille.data[fille.length=static_cast<unsigned int>(lene)] = '\0'; + filla.data[filla.length=static_cast<unsigned int>(lena)] = '\0'; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/* Specialization for float, uses epsilon for comparisons*/ +template<> float comparer_context :: cmp<float>(const std::string& name) +{ + float a,e,t; + read(a,e); + + if((t=fabs(a-e)) > MY_FLT_EPSILON) { + std::stringstream ss; + failure((ss<< "Expected " << e << ", but actual is " + << a << " (delta is " << t << ")", ss.str()),name); + } + return a; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/* Specialization for double, uses epsilon for comparisons*/ +template<> double comparer_context :: cmp<double>(const std::string& name) +{ + double a,e,t; + read(a,e); + + if((t=fabs(a-e)) > MY_DBL_EPSILON) { + std::stringstream ss; + failure((ss<< "Expected " << e << ", but actual is " + << a << " (delta is " << t << ")", ss.str()),name); + } + return a; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/* Specialization for aiVector3D */ +template<> aiVector3D comparer_context :: cmp<aiVector3D >(const std::string& name) +{ + const float x = cmp<float>(name+".x"); + const float y = cmp<float>(name+".y"); + const float z = cmp<float>(name+".z"); + + return aiVector3D(x,y,z); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/* Specialization for aiColor4D */ +template<> aiColor4D comparer_context :: cmp<aiColor4D >(const std::string& name) +{ + const float r = cmp<float>(name+".r"); + const float g = cmp<float>(name+".g"); + const float b = cmp<float>(name+".b"); + const float a = cmp<float>(name+".a"); + + return aiColor4D(r,g,b,a); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/* Specialization for aiQuaternion */ +template<> aiQuaternion comparer_context :: cmp<aiQuaternion >(const std::string& name) +{ + const float w = cmp<float>(name+".w"); + const float x = cmp<float>(name+".x"); + const float y = cmp<float>(name+".y"); + const float z = cmp<float>(name+".z"); + + return aiQuaternion(w,x,y,z); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/* Specialization for aiQuatKey */ +template<> aiQuatKey comparer_context :: cmp<aiQuatKey >(const std::string& name) +{ + const double mTime = cmp<double>(name+".mTime"); + const aiQuaternion mValue = cmp<aiQuaternion>(name+".mValue"); + + return aiQuatKey(mTime,mValue); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/* Specialization for aiVectorKey */ +template<> aiVectorKey comparer_context :: cmp<aiVectorKey >(const std::string& name) +{ + const double mTime = cmp<double>(name+".mTime"); + const aiVector3D mValue = cmp<aiVector3D>(name+".mValue"); + + return aiVectorKey(mTime,mValue); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/* Specialization for aiMatrix4x4 */ +template<> aiMatrix4x4 comparer_context :: cmp<aiMatrix4x4 >(const std::string& name) +{ + aiMatrix4x4 res; + for(unsigned int i = 0; i < 4; ++i) { + for(unsigned int j = 0; j < 4; ++j) { + std::stringstream ss; + res[i][j] = cmp<float>(name+(ss<<".m"<<i<<j,ss.str())); + } + } + + return res; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/* Specialization for aiVertexWeight */ +template<> aiVertexWeight comparer_context :: cmp<aiVertexWeight >(const std::string& name) +{ + const unsigned int mVertexId = cmp<unsigned int>(name+".mVertexId"); + const float mWeight = cmp<float>(name+".mWeight"); + + return aiVertexWeight(mVertexId,mWeight); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// @class sliced_chunk_iterator +/// +/// @brief Helper to iterate easily through corresponding chunks of two dumps simultaneously. +/// +/// Not a *real* iterator, doesn't fully conform to the isocpp iterator spec +//////////////////////////////////////////////////////////////////////////////////////////////////// +class sliced_chunk_iterator { + + friend class sliced_chunk_reader; + sliced_chunk_iterator(comparer_context& ctx, long end) + : ctx(ctx) + , endit(false) + , next(std::numeric_limits<long>::max()) + , end(end) + { + load_next(); + } + +public: + + ~sliced_chunk_iterator() { + fseek(ctx.get_actual(),end,SEEK_SET); + fseek(ctx.get_expect(),end,SEEK_SET); + } + +public: + + /* get current chunk head */ + typedef std::pair<uint32_t,uint32_t> Chunk; + const Chunk& operator*() { + return current; + } + + /* get to next chunk head */ + const sliced_chunk_iterator& operator++() { + cleanup(); + load_next(); + return *this; + } + + /* */ + bool is_end() const { + return endit; + } + +private: + + /* get to the end of *this* chunk */ + void cleanup() { + if(next != std::numeric_limits<long>::max()) { + fseek(ctx.get_actual(),next,SEEK_SET); + fseek(ctx.get_expect(),next,SEEK_SET); + + ctx.pop_length(); + } + } + + /* advance to the next chunk */ + void load_next() { + + Chunk actual; + size_t res=0; + + const long cur = ftell(ctx.get_expect()); + if(end-cur<8) { + current = std::make_pair(0u,0u); + endit = true; + return; + } + + res|=fread(¤t.first,4,1,ctx.get_expect()); + res|=fread(¤t.second,4,1,ctx.get_expect()) <<1u; + res|=fread(&actual.first,4,1,ctx.get_actual()) <<2u; + res|=fread(&actual.second,4,1,ctx.get_actual()) <<3u; + + if(res!=0xf) { + ctx.failure("IO Error reading chunk head, dumps are malformed","<ChunkHead>"); + } + + if (current.first != actual.first) { + std::stringstream ss; + ctx.failure((ss + <<"Chunk headers do not match. EXPECT: " + << std::hex << current.first + <<" ACTUAL: " + << /*std::hex */actual.first, + ss.str()), + "<ChunkHead>"); + } + + if (current.first != actual.first) { + std::stringstream ss; + ctx.failure((ss + <<"Chunk lengths do not match. EXPECT: " + <<current.second + <<" ACTUAL: " + << actual.second, + ss.str()), + "<ChunkHead>"); + } + + next = cur+current.second+8; + ctx.push_length(current.second,cur+8); + } + + comparer_context& ctx; + Chunk current; + bool endit; + long next,end; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// @class sliced_chunk_reader +/// +/// @brief Helper to iterate easily through corresponding chunks of two dumps simultaneously. +//////////////////////////////////////////////////////////////////////////////////////////////////// +class sliced_chunk_reader { +public: + + // + sliced_chunk_reader(comparer_context& ctx) + : ctx(ctx) + {} + + // + ~sliced_chunk_reader() { + } + +public: + + sliced_chunk_iterator begin() const { + return sliced_chunk_iterator(ctx,ctx.get_latest_chunk_length()+ + ctx.get_latest_chunk_start()); + } + +private: + + comparer_context& ctx; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// @class scoped_chunk +/// +/// @brief Utility to simplify usage of comparer_context.push_elem/pop_elem +//////////////////////////////////////////////////////////////////////////////////////////////////// +class scoped_chunk { +public: + + // + scoped_chunk(comparer_context& ctx,const char* msg) + : ctx(ctx) + { + ctx.push_elem(msg); + } + + // + ~scoped_chunk() + { + ctx.pop_elem(); + } + +private: + + comparer_context& ctx; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +void CompareOnTheFlyMaterialProperty(comparer_context& comp) { + scoped_chunk chunk(comp,"aiMaterialProperty"); + + comp.cmp<aiString>("mKey"); + comp.cmp<uint32_t>("mSemantic"); + comp.cmp<uint32_t>("mIndex"); + const uint32_t length = comp.cmp<uint32_t>("mDataLength"); + const aiPropertyTypeInfo type = static_cast<aiPropertyTypeInfo>( + comp.cmp<uint32_t>("mType")); + + switch (type) + { + case aiPTI_Float: + comp.cmp<float>(length/4,"mData"); + break; + + case aiPTI_String: + comp.cmp<aiString>("mData"); + break; + + case aiPTI_Integer: + comp.cmp<uint32_t>(length/4,"mData"); + break; + + case aiPTI_Buffer: + comp.cmp<uint8_t>(length,"mData"); + break; + + default: + break; + }; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +void CompareOnTheFlyMaterial(comparer_context& comp) { + scoped_chunk chunk(comp,"aiMaterial"); + + comp.cmp<uint32_t>("aiMaterial::mNumProperties"); + sliced_chunk_reader reader(comp); + for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) { + if ((*it).first == ASSBIN_CHUNK_AIMATERIALPROPERTY) { + CompareOnTheFlyMaterialProperty(comp); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +void CompareOnTheFlyBone(comparer_context& comp) { + scoped_chunk chunk(comp,"aiBone"); + comp.cmp<aiString>("mName"); + comp.cmp<uint32_t>("mNumWeights"); + comp.cmp<aiMatrix4x4>("mOffsetMatrix"); + + comp.cmp_bounds<aiVertexWeight>("mWeights"); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +void CompareOnTheFlyNodeAnim(comparer_context& comp) { + scoped_chunk chunk(comp,"aiNodeAnim"); + + comp.cmp<aiString>("mNodeName"); + comp.cmp<uint32_t>("mNumPositionKeys"); + comp.cmp<uint32_t>("mNumRotationKeys"); + comp.cmp<uint32_t>("mNumScalingKeys"); + comp.cmp<uint32_t>("mPreState"); + comp.cmp<uint32_t>("mPostState"); + + comp.cmp_bounds<aiVectorKey>("mPositionKeys"); + comp.cmp_bounds<aiQuatKey>("mRotationKeys"); + comp.cmp_bounds<aiVectorKey>("mScalingKeys"); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +void CompareOnTheFlyMesh(comparer_context& comp) { + scoped_chunk chunk(comp,"aiMesh"); + + comp.cmp<uint32_t>("mPrimitiveTypes"); + comp.cmp<uint32_t>("mNumVertices"); + const uint32_t nf = comp.cmp<uint32_t>("mNumFaces"); + comp.cmp<uint32_t>("mNumBones"); + comp.cmp<uint32_t>("mMaterialIndex"); + + const uint32_t present = comp.cmp<uint32_t>("<vertex-components-present>"); + if(present & ASSBIN_MESH_HAS_POSITIONS) { + comp.cmp_bounds<aiVector3D>("mVertices"); + } + + if(present & ASSBIN_MESH_HAS_NORMALS) { + comp.cmp_bounds<aiVector3D>("mNormals"); + } + + if(present & ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS) { + comp.cmp_bounds<aiVector3D>("mTangents"); + comp.cmp_bounds<aiVector3D>("mBitangents"); + } + + for(unsigned int i = 0; present & ASSBIN_MESH_HAS_COLOR(i); ++i) { + std::stringstream ss; + comp.cmp_bounds<aiColor4D>((ss<<"mColors["<<i<<"]",ss.str())); + } + + for(unsigned int i = 0; present & ASSBIN_MESH_HAS_TEXCOORD(i); ++i) { + std::stringstream ss; + comp.cmp<uint32_t>((ss<<"mNumUVComponents["<<i<<"]",ss.str())); + comp.cmp_bounds<aiVector3D>((ss.clear(),ss<<"mTextureCoords["<<i<<"]",ss.str())); + } + + for(unsigned int i = 0; i< ((nf+511)/512); ++i) { + std::stringstream ss; + comp.cmp<uint32_t>((ss<<"mFaces["<<i*512<<"-"<<std::min(static_cast< + uint32_t>((i+1)*512),nf)<<"]",ss.str())); + } + + sliced_chunk_reader reader(comp); + for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) { + if ((*it).first == ASSBIN_CHUNK_AIBONE) { + CompareOnTheFlyBone(comp); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +void CompareOnTheFlyCamera(comparer_context& comp) { + scoped_chunk chunk(comp,"aiCamera"); + + comp.cmp<aiString>("mName"); + + comp.cmp<aiVector3D>("mPosition"); + comp.cmp<aiVector3D>("mLookAt"); + comp.cmp<aiVector3D>("mUp"); + + comp.cmp<float>("mHorizontalFOV"); + comp.cmp<float>("mClipPlaneNear"); + comp.cmp<float>("mClipPlaneFar"); + comp.cmp<float>("mAspect"); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +void CompareOnTheFlyLight(comparer_context& comp) { + scoped_chunk chunk(comp,"aiLight"); + + comp.cmp<aiString>("mName"); + const aiLightSourceType type = static_cast<aiLightSourceType>( + comp.cmp<uint32_t>("mType")); + + if(type!=aiLightSource_DIRECTIONAL) { + comp.cmp<float>("mAttenuationConstant"); + comp.cmp<float>("mAttenuationLinear"); + comp.cmp<float>("mAttenuationQuadratic"); + } + + comp.cmp<aiVector3D>("mColorDiffuse"); + comp.cmp<aiVector3D>("mColorSpecular"); + comp.cmp<aiVector3D>("mColorAmbient"); + + if(type==aiLightSource_SPOT) { + comp.cmp<float>("mAngleInnerCone"); + comp.cmp<float>("mAngleOuterCone"); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +void CompareOnTheFlyAnimation(comparer_context& comp) { + scoped_chunk chunk(comp,"aiAnimation"); + + comp.cmp<aiString>("mName"); + comp.cmp<double>("mDuration"); + comp.cmp<double>("mTicksPerSecond"); + comp.cmp<uint32_t>("mNumChannels"); + + sliced_chunk_reader reader(comp); + for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) { + if ((*it).first == ASSBIN_CHUNK_AINODEANIM) { + CompareOnTheFlyNodeAnim(comp); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +void CompareOnTheFlyTexture(comparer_context& comp) { + scoped_chunk chunk(comp,"aiTexture"); + + const uint32_t w = comp.cmp<uint32_t>("mWidth"); + const uint32_t h = comp.cmp<uint32_t>("mHeight"); + (void)w; (void)h; + comp.cmp<char>("achFormatHint[0]"); + comp.cmp<char>("achFormatHint[1]"); + comp.cmp<char>("achFormatHint[2]"); + comp.cmp<char>("achFormatHint[3]"); + comp.cmp<char>("achFormatHint[4]"); + comp.cmp<char>("achFormatHint[5]"); + comp.cmp<char>("achFormatHint[6]"); + comp.cmp<char>("achFormatHint[7]"); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +void CompareOnTheFlyNode(comparer_context& comp) { + scoped_chunk chunk(comp,"aiNode"); + comp.cmp<aiString>("mName"); + comp.cmp<aiMatrix4x4>("mTransformation"); + comp.cmp<uint32_t>("mNumChildren"); + comp.cmp<uint32_t>(comp.cmp<uint32_t>("mNumMeshes"),"mMeshes"); + + sliced_chunk_reader reader(comp); + for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) { + if ((*it).first == ASSBIN_CHUNK_AINODE) { + CompareOnTheFlyNode(comp); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +void CompareOnTheFlyScene(comparer_context& comp) { + scoped_chunk chunk(comp,"aiScene"); + + comp.cmp<uint32_t>("mFlags"); + comp.cmp<uint32_t>("mNumMeshes"); + comp.cmp<uint32_t>("mNumMaterials"); + comp.cmp<uint32_t>("mNumAnimations"); + comp.cmp<uint32_t>("mNumTextures"); + comp.cmp<uint32_t>("mNumLights"); + comp.cmp<uint32_t>("mNumCameras"); + + sliced_chunk_reader reader(comp); + for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) { + if ((*it).first == ASSBIN_CHUNK_AIMATERIAL) { + CompareOnTheFlyMaterial(comp); + } + else if ((*it).first == ASSBIN_CHUNK_AITEXTURE) { + CompareOnTheFlyTexture(comp); + } + else if ((*it).first == ASSBIN_CHUNK_AIMESH) { + CompareOnTheFlyMesh(comp); + } + else if ((*it).first == ASSBIN_CHUNK_AIANIMATION) { + CompareOnTheFlyAnimation(comp); + } + else if ((*it).first == ASSBIN_CHUNK_AICAMERA) { + CompareOnTheFlyCamera(comp); + } + else if ((*it).first == ASSBIN_CHUNK_AILIGHT) { + CompareOnTheFlyLight(comp); + } + else if ((*it).first == ASSBIN_CHUNK_AINODE) { + CompareOnTheFlyNode(comp); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +void CompareOnTheFly(comparer_context& comp) +{ + sliced_chunk_reader reader(comp); + for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) { + if ((*it).first == ASSBIN_CHUNK_AISCENE) { + CompareOnTheFlyScene(comp); + break; + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +void CheckHeader(comparer_context& comp) +{ + fseek(comp.get_actual(),ASSBIN_HEADER_LENGTH,SEEK_CUR); + fseek(comp.get_expect(),ASSBIN_HEADER_LENGTH,SEEK_CUR); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +int Assimp_CompareDump (const char* const* params, unsigned int num) +{ + // --help + if ((num == 1 && !strcmp( params[0], "-h")) || !strcmp( params[0], "--help") || !strcmp( params[0], "-?") ) { + printf("%s",AICMD_MSG_CMPDUMP_HELP); + return AssimpCmdError::Success; + } + + // assimp cmpdump actual expected + if (num < 2) { + std::cout << "assimp cmpdump: Invalid number of arguments. " + "See \'assimp cmpdump --help\'\r\n" << std::endl; + return AssimpCmdError::InvalidNumberOfArguments; + } + + if(!strcmp(params[0],params[1])) { + std::cout << "assimp cmpdump: same file, same content." << std::endl; + return AssimpCmdError::Success; + } + + class file_ptr + { + public: + file_ptr(FILE *p) + : m_file(p) + {} + ~file_ptr() + { + if (m_file) + { + fclose(m_file); + m_file = NULL; + } + } + + operator FILE *() { return m_file; } + + private: + FILE *m_file; + }; + file_ptr actual(fopen(params[0],"rb")); + if (!actual) { + std::cout << "assimp cmpdump: Failure reading ACTUAL data from " << + params[0] << std::endl; + return AssimpCmdError::FailedToLoadInputFile; + } + file_ptr expected(fopen(params[1],"rb")); + if (!expected) { + std::cout << "assimp cmpdump: Failure reading EXPECT data from " << + params[1] << std::endl; + return AssimpCmdCompareDumpError::FailedToLoadExpectedInputFile; + } + + comparer_context comp(actual,expected); + try { + CheckHeader(comp); + CompareOnTheFly(comp); + } + catch(const compare_fails_exception& ex) { + printf("%s",ex.what()); + return AssimpCmdCompareDumpError::FileComparaisonFailure; + } + catch(...) { + // we don't bother checking too rigourously here, so + // we might end up here ... + std::cout << "Unknown failure, are the input files well-defined?"; + return AssimpCmdCompareDumpError::UnknownFailure; + } + + std::cout << "Success (totally " << std::dec << comp.get_num_chunks() << + " chunks)" << std::endl; + + return AssimpCmdError::Success; +} diff --git a/libs/assimp/tools/assimp_cmd/Export.cpp b/libs/assimp/tools/assimp_cmd/Export.cpp new file mode 100644 index 0000000..fd9fc76 --- /dev/null +++ b/libs/assimp/tools/assimp_cmd/Export.cpp @@ -0,0 +1,166 @@ +/* +--------------------------------------------------------------------------- +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 Export.cpp + * @brief Implementation of the 'assimp export' utility + */ + +#include "Main.h" +#include <assimp/ParsingUtils.h> +#include <assimp/StringUtils.h> + +#ifndef ASSIMP_BUILD_NO_EXPORT + +const char *AICMD_MSG_EXPORT_HELP_E = + "assimp export <model> [<out>] [-f<h>] [common parameters]\n" + "\t -f<h> Specify the file format. If omitted, the output format is \n" + "\t\tderived from the file extension of the given output file \n" + "\t[See the assimp_cmd docs for a full list of all common parameters] \n"; + +// ----------------------------------------------------------------------------------- +size_t GetMatchingFormat(const std::string &outf, bool byext = false) { + for (size_t i = 0, end = globalExporter->GetExportFormatCount(); i < end; ++i) { + const aiExportFormatDesc *const e = globalExporter->GetExportFormatDescription(i); + if (outf == (byext ? e->fileExtension : e->id)) { + return i; + } + } + return SIZE_MAX; +} + +// ----------------------------------------------------------------------------------- +int Assimp_Export(const char *const *params, unsigned int num) { + const char *const invalid = "assimp export: Invalid number of arguments. See \'assimp export --help\'\n"; + if (num < 1) { + printf(invalid); + return AssimpCmdError::InvalidNumberOfArguments; + } + + // --help + if (!strcmp(params[0], "-h") || !strcmp(params[0], "--help") || !strcmp(params[0], "-?")) { + printf("%s", AICMD_MSG_EXPORT_HELP_E); + return AssimpCmdError::Success; + } + + std::string in = std::string(params[0]); + std::string out = (num > 1 ? std::string(params[1]) : "-"), outext; + + // + const std::string::size_type s = out.find_last_of('.'); + if (s != std::string::npos) { + outext = out.substr(s + 1); + out = out.substr(0, s); + } + + // get import flags + ImportData import; + ProcessStandardArguments(import, params + 1, num - 1); + + // process other flags + std::string outf = ""; + for (unsigned int i = (out[0] == '-' ? 1 : 2); i < num; ++i) { + if (!params[i]) { + continue; + } + if (!strncmp(params[i], "-f", 2)) { + if (strncmp(params[i], "-fi", 3)) + outf = std::string(params[i] + 2); + } else if (!strncmp(params[i], "--format=", 9)) { + outf = std::string(params[i] + 9); + } + } + + std::transform(outf.begin(), outf.end(), outf.begin(), ai_tolower<char>); + + // convert the output format to a format id + size_t outfi = GetMatchingFormat(outf); + if (outfi == SIZE_MAX) { + if (outf.length()) { + printf("assimp export: warning, format id \'%s\' is unknown\n", outf.c_str()); + } + + // retry to see if we know it as file extension + outfi = GetMatchingFormat(outf, true); + if (outfi == SIZE_MAX) { + // retry to see if we know the file extension of the output file + outfi = GetMatchingFormat(outext, true); + + if (outfi == SIZE_MAX) { + // still no match -> failure + printf("assimp export: no output format specified and I failed to guess it\n"); + return -23; + } + } else { + outext = outf; + } + } + + // if no output file is specified, take the file name from input file + if (out[0] == '-') { + std::string::size_type pos = in.find_last_of('.'); + if (pos == std::string::npos) { + pos = in.length(); + } + + out = in.substr(0, pos); + } + + const aiExportFormatDesc *const e = globalExporter->GetExportFormatDescription(outfi); + printf("assimp export: select file format: \'%s\' (%s)\n", e->id, e->description); + + // import the model + const aiScene *scene = ImportModel(import, in); + if (!scene) { + return AssimpCmdExportError::FailedToImportModel; + } + + // derive the final file name + out += "." + outext; + + // and call the export routine + if (!ExportModel(scene, import, out, e->id)) { + return AssimpCmdExportError::FailedToExportModel; + } + printf("assimp export: wrote output file: %s\n", out.c_str()); + return AssimpCmdError::Success; +} + +#endif // no export diff --git a/libs/assimp/tools/assimp_cmd/ImageExtractor.cpp b/libs/assimp/tools/assimp_cmd/ImageExtractor.cpp new file mode 100644 index 0000000..49edf97 --- /dev/null +++ b/libs/assimp/tools/assimp_cmd/ImageExtractor.cpp @@ -0,0 +1,361 @@ +/* +--------------------------------------------------------------------------- +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 ImageExtractor.cpp + * @brief Implementation of the 'assimp extract' utility + */ + +#include "Main.h" +#include <assimp/ParsingUtils.h> +#include <assimp/StringComparison.h> +#include <assimp/fast_atof.h> + +static const char *AICMD_MSG_DUMP_HELP_E = + "assimp extract <model> [<out>] [-t<n>] [-f<fmt>] [-ba] [-s] [common parameters]\n" + "\t -ba Writes BMP's with alpha channel\n" + "\t -t<n> Zero-based index of the texture to be extracted \n" + "\t -f<f> Specify the file format if <out> is omitted \n" + "\t[See the assimp_cmd docs for a full list of all common parameters] \n" + "\t -cfast Fast post processing preset, runs just a few important steps \n" + "\t -cdefault Default post processing: runs all recommended steps\n" + "\t -cfull Fires almost all post processing steps \n"; + +#define AI_EXTRACT_WRITE_BMP_ALPHA 0x1 +#include <assimp/Compiler/pushpack1.h> + +// ----------------------------------------------------------------------------------- +// Data structure for the first header of a BMP +struct BITMAPFILEHEADER { + uint16_t bfType; + uint32_t bfSize; + uint16_t bfReserved1; + uint16_t bfReserved2; + uint32_t bfOffBits; +} PACK_STRUCT; + +// ----------------------------------------------------------------------------------- +// Data structure for the second header of a BMP +struct BITMAPINFOHEADER { + int32_t biSize; + int32_t biWidth; + int32_t biHeight; + int16_t biPlanes; + int16_t biBitCount; + uint32_t biCompression; + int32_t biSizeImage; + int32_t biXPelsPerMeter; + int32_t biYPelsPerMeter; + int32_t biClrUsed; + int32_t biClrImportant; + + // pixel data follows header +} PACK_STRUCT; + +// ----------------------------------------------------------------------------------- +// Data structure for the header of a TGA +struct TGA_HEADER { + uint8_t identsize; // size of ID field that follows 18 byte header (0 usually) + uint8_t colourmaptype; // type of colour map 0=none, 1=has palette + uint8_t imagetype; // type of image 0=none,1=indexed,2=rgb,3=gray,+8=rle packed + + uint16_t colourmapstart; // first colour map entry in palette + uint16_t colourmaplength; // number of colors in palette + uint8_t colourmapbits; // number of bits per palette entry 15,16,24,32 + + uint16_t xstart; // image x origin + uint16_t ystart; // image y origin + uint16_t width; // image width in pixels + uint16_t height; // image height in pixels + uint8_t bits; // image bits per pixel 8,16,24,32 + uint8_t descriptor; // image descriptor bits (vh flip bits) + + // pixel data follows header +} PACK_STRUCT; + +#include <assimp/Compiler/poppack1.h> + +// ----------------------------------------------------------------------------------- +// Save a texture as bitmap +int SaveAsBMP(FILE *file, const aiTexel *data, unsigned int width, unsigned int height, bool SaveAlpha = false) { + if (!file || !data) { + return 1; + } + + const unsigned int numc = (SaveAlpha ? 4 : 3); + unsigned char *buffer = new unsigned char[width * height * numc]; + + for (unsigned int y = 0; y < height; ++y) { + for (unsigned int x = 0; x < width; ++x) { + + unsigned char *s = &buffer[(y * width + x) * numc]; + const aiTexel *t = &data[y * width + x]; + s[0] = t->b; + s[1] = t->g; + s[2] = t->r; + if (4 == numc) { + s[3] = t->a; + } + } + } + + BITMAPFILEHEADER header; + header.bfType = 'B' | (int('M') << 8u); + header.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); + header.bfSize = header.bfOffBits + width * height * numc; + header.bfReserved1 = header.bfReserved2 = 0; + + fwrite(&header, sizeof(BITMAPFILEHEADER), 1, file); + + BITMAPINFOHEADER info; + info.biSize = 40; + info.biWidth = width; + info.biHeight = height; + info.biPlanes = 1; + info.biBitCount = (int16_t)numc << 3; + info.biCompression = 0; + info.biSizeImage = width * height * numc; + info.biXPelsPerMeter = 1; // dummy + info.biYPelsPerMeter = 1; // dummy + info.biClrUsed = 0; + info.biClrImportant = 0; + + fwrite(&info, sizeof(BITMAPINFOHEADER), 1, file); + + unsigned char *temp = buffer + info.biSizeImage; + const unsigned int row = width * numc; + + for (int y = 0; temp -= row, y < info.biHeight; ++y) { + fwrite(temp, row, 1, file); + } + + // delete the buffer + delete[] buffer; + return 0; +} + +// ----------------------------------------------------------------------------------- +// Save a texture as tga +int SaveAsTGA(FILE *file, const aiTexel *data, unsigned int width, unsigned int height) { + if (!file || !data) { + return 1; + } + + TGA_HEADER head; + memset(&head, 0, sizeof(head)); + head.bits = 32; + head.height = (uint16_t)height; + head.width = (uint16_t)width; + head.descriptor |= (1u << 5); + + head.imagetype = 2; // actually it's RGBA + fwrite(&head, sizeof(TGA_HEADER), 1, file); + + for (unsigned int y = 0; y < height; ++y) { + for (unsigned int x = 0; x < width; ++x) { + fwrite(data + y * width + x, 4, 1, file); + } + } + + return 0; +} + +// ----------------------------------------------------------------------------------- +// Do the texture import for a given aiTexture +int DoExport(const aiTexture *tx, FILE *p, const std::string &extension, unsigned int flags) { + // export the image to the appropriate decoder + if (extension == "bmp") { + SaveAsBMP(p, tx->pcData, tx->mWidth, tx->mHeight, + (0 != (flags & AI_EXTRACT_WRITE_BMP_ALPHA))); + } else if (extension == "tga") { + SaveAsTGA(p, tx->pcData, tx->mWidth, tx->mHeight); + } else { + printf("assimp extract: No available texture encoder found for %s\n", extension.c_str()); + return AssimpCmdExtractError::NoAvailableTextureEncoderFound; + } + return AssimpCmdError::Success; +} + +// ----------------------------------------------------------------------------------- +// Implementation of the assimp extract utility +int Assimp_Extract(const char *const *params, unsigned int num) { + const char *const invalid = "assimp extract: Invalid number of arguments. See \'assimp extract --help\'\n"; + // assimp extract in out [options] + if (num < 1) { + printf(invalid); + return AssimpCmdError::InvalidNumberOfArguments; + } + + // --help + if (!strcmp(params[0], "-h") || !strcmp(params[0], "--help") || !strcmp(params[0], "-?")) { + printf("%s", AICMD_MSG_DUMP_HELP_E); + return AssimpCmdError::Success; + } + + std::string in = std::string(params[0]); + std::string out = (num > 1 ? std::string(params[1]) : "-"); + + // get import flags + ImportData import; + ProcessStandardArguments(import, params + 1, num - 1); + + bool nosuffix = false; + unsigned int texIdx = 0xffffffff, flags = 0; + + // process other flags + std::string extension = "bmp"; + for (unsigned int i = (out[0] == '-' ? 1 : 2); i < num; ++i) { + if (!params[i]) { + continue; + } + + if (!strncmp(params[i], "-f", 2)) { + extension = std::string(params[i] + 2); + } else if (!strncmp(params[i], "--format=", 9)) { + extension = std::string(params[i] + 9); + } else if (!strcmp(params[i], "--nosuffix") || !strcmp(params[i], "-s")) { + nosuffix = true; + } else if (!strncmp(params[i], "--texture=", 10)) { + texIdx = Assimp::strtoul10(params[i] + 10); + } else if (!strncmp(params[i], "-t", 2)) { + texIdx = Assimp::strtoul10(params[i] + 2); + } else if (!strcmp(params[i], "-ba") || !strcmp(params[i], "--bmp-with-alpha")) { + flags |= AI_EXTRACT_WRITE_BMP_ALPHA; + } +#if 0 + else { + printf("Unknown parameter: %s\n",params[i]); + return 10; + } +#endif + } + + std::transform(extension.begin(), extension.end(), extension.begin(), ai_tolower<char>); + + if (out[0] == '-') { + // take file name from input file + std::string::size_type s = in.find_last_of('.'); + if (s == std::string::npos) + s = in.length(); + + out = in.substr(0, s); + } + + // take file extension from file name, if given + std::string::size_type s = out.find_last_of('.'); + if (s != std::string::npos) { + extension = out.substr(s + 1, in.length() - (s + 1)); + out = out.substr(0, s); + } + + // import the main model + const aiScene *scene = ImportModel(import, in); + if (!scene) { + printf("assimp extract: Unable to load input file %s\n", in.c_str()); + return AssimpCmdError::FailedToLoadInputFile; + } + + // get the texture(s) to be exported + if (texIdx != 0xffffffff) { + + // check whether the requested texture is existing + if (texIdx >= scene->mNumTextures) { + ::printf("assimp extract: Texture %u requested, but there are just %i textures\n", + texIdx, scene->mNumTextures); + return AssimpCmdExtractError::TextureIndexIsOutOfRange; + } + } else { + ::printf("assimp extract: Exporting %i textures\n", scene->mNumTextures); + } + + // now write all output textures + for (unsigned int i = 0; i < scene->mNumTextures; ++i) { + if (texIdx != 0xffffffff && texIdx != i) { + continue; + } + + const aiTexture *tex = scene->mTextures[i]; + std::string out_cpy = out, out_ext = extension; + + // append suffix if necessary - always if all textures are exported + if (!nosuffix || (texIdx == 0xffffffff)) { + out_cpy.append("_img"); + char tmp[10]; + Assimp::ASSIMP_itoa10(tmp, i); + + out_cpy.append(std::string(tmp)); + } + + // if the texture is a compressed one, we'll export + // it to its native file format + if (!tex->mHeight) { + printf("assimp extract: Texture %u is compressed (%s). Writing native file format.\n", + i, tex->achFormatHint); + + // modify file extension + out_ext = std::string(tex->achFormatHint); + } + out_cpy.append("." + out_ext); + + // open output file + FILE *p = ::fopen(out_cpy.c_str(), "wb"); + if (!p) { + printf("assimp extract: Unable to open output file %s\n", out_cpy.c_str()); + return AssimpCmdError::FailedToOpenOutputFile; + } + int m; + + if (!tex->mHeight) { + m = (1 != fwrite(tex->pcData, tex->mWidth, 1, p)) ? + static_cast<int>(AssimpCmdError::Success) : + static_cast<int>(AssimpCmdExtractError::FailedToExportCompressedTexture); + } else { + m = DoExport(tex, p, extension, flags); + } + ::fclose(p); + + printf("assimp extract: Wrote texture %u to %s\n", i, out_cpy.c_str()); + if (texIdx != 0xffffffff) { + return m; + } + } + + return AssimpCmdError::Success; +} diff --git a/libs/assimp/tools/assimp_cmd/Info.cpp b/libs/assimp/tools/assimp_cmd/Info.cpp new file mode 100644 index 0000000..c43a783 --- /dev/null +++ b/libs/assimp/tools/assimp_cmd/Info.cpp @@ -0,0 +1,483 @@ +/* +--------------------------------------------------------------------------- +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 Info.cpp + * @brief Implementation of the 'assimp info' utility */ + +#include "Main.h" + +#include <cstdio> +#include <iostream> +#include <string> + +const char* AICMD_MSG_INFO_HELP_E = + "assimp info <file> [-r] [-v]\n" + "\tPrint basic structure of a 3D model\n" + "\t-r,--raw: No postprocessing, do a raw import\n" + "\t-v,--verbose: Print verbose info such as node transform data\n" + "\t-s, --silent: Print only minimal info\n"; + +const char *TREE_BRANCH_ASCII = "|-"; +const char *TREE_BRANCH_UTF8 = "\xe2\x94\x9c\xe2\x95\xb4"; +const char *TREE_STOP_ASCII = "'-"; +const char *TREE_STOP_UTF8 = "\xe2\x94\x94\xe2\x95\xb4"; +const char *TREE_CONTINUE_ASCII = "| "; +const char *TREE_CONTINUE_UTF8 = "\xe2\x94\x82 "; + +// note: by default this is using utf-8 text. +// this is well supported on pretty much any linux terminal. +// if this causes problems on some platform, +// put an #ifdef to use the ascii version for that platform. +const char *TREE_BRANCH = TREE_BRANCH_UTF8; +const char *TREE_STOP = TREE_STOP_UTF8; +const char *TREE_CONTINUE = TREE_CONTINUE_UTF8; + +// ----------------------------------------------------------------------------------- +unsigned int CountNodes(const aiNode* root) +{ + unsigned int i = 0; + for (unsigned int a = 0; a < root->mNumChildren; ++a ) { + i += CountNodes(root->mChildren[a]); + } + return 1+i; +} + +// ----------------------------------------------------------------------------------- +unsigned int GetMaxDepth(const aiNode* root) +{ + unsigned int cnt = 0; + for (unsigned int i = 0; i < root->mNumChildren; ++i ) { + cnt = std::max(cnt,GetMaxDepth(root->mChildren[i])); + } + return cnt+1; +} + +// ----------------------------------------------------------------------------------- +unsigned int CountVertices(const aiScene* scene) +{ + unsigned int cnt = 0; + for(unsigned int i = 0; i < scene->mNumMeshes; ++i) { + cnt += scene->mMeshes[i]->mNumVertices; + } + return cnt; +} + +// ----------------------------------------------------------------------------------- +unsigned int CountFaces(const aiScene* scene) +{ + unsigned int cnt = 0; + for(unsigned int i = 0; i < scene->mNumMeshes; ++i) { + cnt += scene->mMeshes[i]->mNumFaces; + } + return cnt; +} + +// ----------------------------------------------------------------------------------- +unsigned int CountBones(const aiScene* scene) +{ + unsigned int cnt = 0; + for(unsigned int i = 0; i < scene->mNumMeshes; ++i) { + cnt += scene->mMeshes[i]->mNumBones; + } + return cnt; +} + +// ----------------------------------------------------------------------------------- +unsigned int CountAnimChannels(const aiScene* scene) +{ + unsigned int cnt = 0; + for(unsigned int i = 0; i < scene->mNumAnimations; ++i) { + cnt += scene->mAnimations[i]->mNumChannels; + } + return cnt; +} + +// ----------------------------------------------------------------------------------- +unsigned int GetAvgFacePerMesh(const aiScene* scene) { + return (scene->mNumMeshes != 0) ? static_cast<unsigned int>(CountFaces(scene)/scene->mNumMeshes) : 0; +} + +// ----------------------------------------------------------------------------------- +unsigned int GetAvgVertsPerMesh(const aiScene* scene) { + return (scene->mNumMeshes != 0) ? static_cast<unsigned int>(CountVertices(scene)/scene->mNumMeshes) : 0; +} + +// ----------------------------------------------------------------------------------- +void FindSpecialPoints(const aiScene* scene,const aiNode* root,aiVector3D special_points[3],const aiMatrix4x4& mat=aiMatrix4x4()) +{ + // XXX that could be greatly simplified by using code from code/ProcessHelper.h + // XXX I just don't want to include it here. + const aiMatrix4x4 trafo = root->mTransformation*mat; + for(unsigned int i = 0; i < root->mNumMeshes; ++i) { + const aiMesh* mesh = scene->mMeshes[root->mMeshes[i]]; + + for(unsigned int a = 0; a < mesh->mNumVertices; ++a) { + aiVector3D v = trafo*mesh->mVertices[a]; + + special_points[0].x = std::min(special_points[0].x,v.x); + special_points[0].y = std::min(special_points[0].y,v.y); + special_points[0].z = std::min(special_points[0].z,v.z); + + special_points[1].x = std::max(special_points[1].x,v.x); + special_points[1].y = std::max(special_points[1].y,v.y); + special_points[1].z = std::max(special_points[1].z,v.z); + } + } + + for(unsigned int i = 0; i < root->mNumChildren; ++i) { + FindSpecialPoints(scene,root->mChildren[i],special_points,trafo); + } +} + +// ----------------------------------------------------------------------------------- +void FindSpecialPoints(const aiScene* scene,aiVector3D special_points[3]) +{ + special_points[0] = aiVector3D(1e10,1e10,1e10); + special_points[1] = aiVector3D(-1e10,-1e10,-1e10); + + FindSpecialPoints(scene,scene->mRootNode,special_points); + special_points[2] = (special_points[0]+special_points[1])*(ai_real)0.5; +} + +// ----------------------------------------------------------------------------------- +std::string FindPTypes(const aiScene* scene) +{ + bool haveit[4] = {0}; + for(unsigned int i = 0; i < scene->mNumMeshes; ++i) { + const unsigned int pt = scene->mMeshes[i]->mPrimitiveTypes; + if (pt & aiPrimitiveType_POINT) { + haveit[0]=true; + } + if (pt & aiPrimitiveType_LINE) { + haveit[1]=true; + } + if (pt & aiPrimitiveType_TRIANGLE) { + haveit[2]=true; + } + if (pt & aiPrimitiveType_POLYGON) { + haveit[3]=true; + } + } + return (haveit[0]?std::string("points"):"")+(haveit[1]?"lines":"")+ + (haveit[2]?"triangles":"")+(haveit[3]?"n-polygons":""); +} + +// ----------------------------------------------------------------------------------- +// Prettily print the node graph to stdout +void PrintHierarchy( + const aiNode* node, + const std::string &indent, + bool verbose, + bool last = false, + bool first = true +){ + // tree visualization + std::string branchchar; + if (first) { branchchar = ""; } + else if (last) { branchchar = TREE_STOP; } // "'-" + else { branchchar = TREE_BRANCH; } // "|-" + + // print the indent and the branch character and the name + std::cout << indent << branchchar << node->mName.C_Str(); + + // if there are meshes attached, indicate this + if (node->mNumMeshes) { + std::cout << " (mesh "; + bool sep = false; + for (size_t i=0; i < node->mNumMeshes; ++i) { + unsigned int mesh_index = node->mMeshes[i]; + if (sep) { std::cout << ", "; } + std::cout << mesh_index; + sep = true; + } + std::cout << ")"; + } + + // finish the line + std::cout << std::endl; + + // in verbose mode, print the transform data as well + if (verbose) { + // indent to use + std::string indentadd; + if (last) { indentadd += " "; } + else { indentadd += TREE_CONTINUE; } // "| ".. + if (node->mNumChildren == 0) { indentadd += " "; } + else { indentadd += TREE_CONTINUE; } // .."| " + aiVector3D s, r, t; + node->mTransformation.Decompose(s, r, t); + if (s.x != 1.0 || s.y != 1.0 || s.z != 1.0) { + std::cout << indent << indentadd; + printf(" S:[%f %f %f]\n", s.x, s.y, s.z); + } + if (r.x || r.y || r.z) { + std::cout << indent << indentadd; + printf(" R:[%f %f %f]\n", r.x, r.y, r.z); + } + if (t.x || t.y || t.z) { + std::cout << indent << indentadd; + printf(" T:[%f %f %f]\n", t.x, t.y, t.z); + } + } + + // and recurse + std::string nextIndent; + if (first) { nextIndent = indent; } + else if (last) { nextIndent = indent + " "; } + else { nextIndent = indent + TREE_CONTINUE; } // "| " + for (size_t i = 0; i < node->mNumChildren; ++i) { + bool lastone = (i == node->mNumChildren - 1); + PrintHierarchy( + node->mChildren[i], + nextIndent, + verbose, + lastone, + false + ); + } +} + +// ----------------------------------------------------------------------------------- +// Implementation of the assimp info utility to print basic file info +int Assimp_Info (const char* const* params, unsigned int num) { + // --help + if (!strcmp( params[0],"-h")||!strcmp( params[0],"--help")||!strcmp( params[0],"-?") ) { + printf("%s",AICMD_MSG_INFO_HELP_E); + return AssimpCmdError::Success; + } + + // asssimp info <file> [-r] + if (num < 1) { + printf("assimp info: Invalid number of arguments. " + "See \'assimp info --help\'\n"); + return AssimpCmdError::InvalidNumberOfArguments; + } + + const std::string in = std::string(params[0]); + + // get -r and -v arguments + bool raw = false; + bool verbose = false; + bool silent = false; + for(unsigned int i = 1; i < num; ++i) { + if (!strcmp(params[i],"--raw")||!strcmp(params[i],"-r")) { + raw = true; + } + if (!strcmp(params[i],"--verbose")||!strcmp(params[i],"-v")) { + verbose = true; + } + if (!strcmp(params[i], "--silent") || !strcmp(params[i], "-s")) { + silent = true; + } + } + + // Verbose and silent at the same time are not allowed + if ( verbose && silent ) { + printf("assimp info: Invalid arguments, verbose and silent at the same time are forbidden. "); + return AssimpCmdInfoError::InvalidCombinaisonOfArguments; + } + + // Parse post-processing flags unless -r was specified + ImportData import; + if (!raw) { + // get import flags + ProcessStandardArguments(import, params + 1, num - 1); + + //No custom post process flags defined, we set all the post process flags active + if(import.ppFlags == 0) + import.ppFlags |= aiProcessPreset_TargetRealtime_MaxQuality; + } + + // import the main model + const aiScene* scene = ImportModel(import,in); + if (!scene) { + printf("assimp info: Unable to load input file %s\n", + in.c_str()); + return AssimpCmdError::FailedToLoadInputFile; + } + + aiMemoryInfo mem; + globalImporter->GetMemoryRequirements(mem); + + + static const char* format_string = + "Memory consumption: %i B\n" + "Nodes: %i\n" + "Maximum depth %i\n" + "Meshes: %i\n" + "Animations: %i\n" + "Textures (embed.): %i\n" + "Materials: %i\n" + "Cameras: %i\n" + "Lights: %i\n" + "Vertices: %i\n" + "Faces: %i\n" + "Bones: %i\n" + "Animation Channels: %i\n" + "Primitive Types: %s\n" + "Average faces/mesh %i\n" + "Average verts/mesh %i\n" + "Minimum point (%f %f %f)\n" + "Maximum point (%f %f %f)\n" + "Center point (%f %f %f)\n" + + ; + + aiVector3D special_points[3]; + FindSpecialPoints(scene,special_points); + printf(format_string, + mem.total, + CountNodes(scene->mRootNode), + GetMaxDepth(scene->mRootNode), + scene->mNumMeshes, + scene->mNumAnimations, + scene->mNumTextures, + scene->mNumMaterials, + scene->mNumCameras, + scene->mNumLights, + CountVertices(scene), + CountFaces(scene), + CountBones(scene), + CountAnimChannels(scene), + FindPTypes(scene).c_str(), + GetAvgFacePerMesh(scene), + GetAvgVertsPerMesh(scene), + special_points[0][0],special_points[0][1],special_points[0][2], + special_points[1][0],special_points[1][1],special_points[1][2], + special_points[2][0],special_points[2][1],special_points[2][2] + ) + ; + + if (silent) + { + printf("\n"); + return AssimpCmdError::Success; + } + + // meshes + if (scene->mNumMeshes) { + printf("\nMeshes: (name) [vertices / bones / faces | primitive_types]\n"); + } + for (unsigned int i = 0; i < scene->mNumMeshes; ++i) { + const aiMesh* mesh = scene->mMeshes[i]; + printf(" %d (%s)", i, mesh->mName.C_Str()); + printf( + ": [%d / %d / %d |", + mesh->mNumVertices, + mesh->mNumBones, + mesh->mNumFaces + ); + const unsigned int ptypes = mesh->mPrimitiveTypes; + if (ptypes & aiPrimitiveType_POINT) { printf(" point"); } + if (ptypes & aiPrimitiveType_LINE) { printf(" line"); } + if (ptypes & aiPrimitiveType_TRIANGLE) { printf(" triangle"); } + if (ptypes & aiPrimitiveType_POLYGON) { printf(" polygon"); } + printf("]\n"); + } + + // materials + unsigned int total=0; + for(unsigned int i = 0;i < scene->mNumMaterials; ++i) { + aiString name; + if (AI_SUCCESS==aiGetMaterialString(scene->mMaterials[i],AI_MATKEY_NAME,&name)) { + printf("%s\n \'%s\'",(total++?"":"\nNamed Materials:" ),name.data); + } + } + if(total) { + printf("\n"); + } + + // textures + total=0; + for(unsigned int i = 0;i < scene->mNumMaterials; ++i) { + aiString name; + static const aiTextureType types[] = { + aiTextureType_NONE, + aiTextureType_DIFFUSE, + aiTextureType_SPECULAR, + aiTextureType_AMBIENT, + aiTextureType_EMISSIVE, + aiTextureType_HEIGHT, + aiTextureType_NORMALS, + aiTextureType_SHININESS, + aiTextureType_OPACITY, + aiTextureType_DISPLACEMENT, + aiTextureType_LIGHTMAP, + aiTextureType_REFLECTION, + aiTextureType_BASE_COLOR, + aiTextureType_NORMAL_CAMERA, + aiTextureType_EMISSION_COLOR, + aiTextureType_METALNESS, + aiTextureType_DIFFUSE_ROUGHNESS, + aiTextureType_AMBIENT_OCCLUSION, + aiTextureType_UNKNOWN + }; + for(unsigned int type = 0; type < sizeof(types)/sizeof(types[0]); ++type) { + for(unsigned int idx = 0;AI_SUCCESS==aiGetMaterialString(scene->mMaterials[i], + AI_MATKEY_TEXTURE(types[type],idx),&name); ++idx) { + printf("%s\n \'%s\'",(total++?"":"\nTexture Refs:" ),name.data); + } + } + } + if(total) { + printf("\n"); + } + + // animations + total=0; + for(unsigned int i = 0;i < scene->mNumAnimations; ++i) { + if (scene->mAnimations[i]->mName.length) { + printf("%s\n \'%s\'",(total++?"":"\nNamed Animations:" ),scene->mAnimations[i]->mName.data); + } + } + if(total) { + printf("\n"); + } + + // node hierarchy + printf("\nNode hierarchy:\n"); + PrintHierarchy(scene->mRootNode,"",verbose); + + printf("\n"); + return AssimpCmdError::Success; +} diff --git a/libs/assimp/tools/assimp_cmd/Main.cpp b/libs/assimp/tools/assimp_cmd/Main.cpp new file mode 100644 index 0000000..31b92b0 --- /dev/null +++ b/libs/assimp/tools/assimp_cmd/Main.cpp @@ -0,0 +1,564 @@ +/* +--------------------------------------------------------------------------- +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 Main.cpp + * @brief main() function of assimp_cmd + */ + +#include "Main.h" + +#include <assimp/ProgressHandler.hpp> +#include <iostream> + +class ConsoleProgressHandler : public ProgressHandler { +public: + ConsoleProgressHandler() : + ProgressHandler() { + // empty + } + + ~ConsoleProgressHandler() override { + // empty + } + + bool Update(float percentage) override { + std::cout << percentage * 100.0f << " %\n"; + return true; + } +}; +const char* AICMD_MSG_ABOUT = +"------------------------------------------------------ \n" +"Open Asset Import Library (\"Assimp\", https://github.com/assimp/assimp) \n" +" -- Commandline toolchain --\n" +"------------------------------------------------------ \n\n" + +"Version %i.%i %s%s%s%s%s(GIT commit %x)\n\n"; + +const char* AICMD_MSG_HELP = +"assimp <verb> <parameters>\n\n" +" verbs:\n" +" \tinfo - Quick file stats\n" +" \tlistext - List all known file extensions available for import\n" +" \tknowext - Check whether a file extension is recognized by Assimp\n" +#ifndef ASSIMP_BUILD_NO_EXPORT +" \texport - Export a file to one of the supported output formats\n" +" \tlistexport - List all supported export formats\n" +" \texportinfo - Show basic information on a specific export format\n" +#endif +" \textract - Extract embedded texture images\n" +" \tdump - Convert models to a binary or textual dump (ASSBIN/ASSXML)\n" +" \tcmpdump - Compare dumps created using \'assimp dump <file> -s ...\'\n" +" \tversion - Display Assimp version\n" +"\n Use \'assimp <verb> --help\' for detailed help on a command.\n" +; + +/*extern*/ Assimp::Importer* globalImporter = nullptr; + +#ifndef ASSIMP_BUILD_NO_EXPORT +/*extern*/ Assimp::Exporter* globalExporter = nullptr; +#endif + +// ------------------------------------------------------------------------------ +// Application entry point +int main (int argc, char* argv[]) +{ + if (argc <= 1) { + printf("assimp: No command specified. Use \'assimp help\' for a detailed command list\n"); + return AssimpCmdError::Success; + } + + // assimp version + // Display version information + if (! strcmp(argv[1], "version")) { + const unsigned int flags = aiGetCompileFlags(); + printf(AICMD_MSG_ABOUT, + aiGetVersionMajor(), + aiGetVersionMinor(), + (flags & ASSIMP_CFLAGS_DEBUG ? "-debug " : ""), + (flags & ASSIMP_CFLAGS_NOBOOST ? "-noboost " : ""), + (flags & ASSIMP_CFLAGS_SHARED ? "-shared " : ""), + (flags & ASSIMP_CFLAGS_SINGLETHREADED ? "-st " : ""), + (flags & ASSIMP_CFLAGS_STLPORT ? "-stlport " : ""), + aiGetVersionRevision()); + + return AssimpCmdError::Success; + } + + // assimp help + // Display some basic help (--help and -h work as well + // because people could try them intuitively) + if (!strcmp(argv[1], "help") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { + printf("%s",AICMD_MSG_HELP); + return AssimpCmdError::Success; + } + + // assimp cmpdump + // Compare two mini model dumps (regression suite) + if (! strcmp(argv[1], "cmpdump")) { + return Assimp_CompareDump (&argv[2],argc-2); + } + + // construct global importer and exporter instances + Assimp::Importer imp; + imp.SetPropertyBool("GLOB_MEASURE_TIME",true); + globalImporter = &imp; + +#ifndef ASSIMP_BUILD_NO_EXPORT + // + Assimp::Exporter exp; + globalExporter = &exp; +#endif + + // assimp listext + // List all file extensions supported by Assimp + if (! strcmp(argv[1], "listext")) { + aiString s; + imp.GetExtensionList(s); + + printf("%s\n",s.data); + return AssimpCmdError::Success; + } + +#ifndef ASSIMP_BUILD_NO_EXPORT + // assimp listexport + // List all export file formats supported by Assimp (not the file extensions, just the format identifiers!) + if (! strcmp(argv[1], "listexport")) { + aiString s; + + for(size_t i = 0, end = exp.GetExportFormatCount(); i < end; ++i) { + const aiExportFormatDesc* const e = exp.GetExportFormatDescription(i); + s.Append( e->id ); + if (i!=end-1) { + s.Append("\n"); + } + } + + printf("%s\n",s.data); + return AssimpCmdError::Success; + } + + + // assimp exportinfo + // stat an export format + if (! strcmp(argv[1], "exportinfo")) { + aiString s; + + if (argc<3) { + printf("Expected file format id\n"); + return AssimpCmdError::NoFileFormatSpecified; + } + + for(size_t i = 0, end = exp.GetExportFormatCount(); i < end; ++i) { + const aiExportFormatDesc* const e = exp.GetExportFormatDescription(i); + if (!strcmp(e->id,argv[2])) { + printf("%s\n%s\n%s\n",e->id,e->fileExtension,e->description); + return AssimpCmdError::Success; + } + } + + printf("Unknown file format id: \'%s\'\n",argv[2]); + return AssimpCmdError::UnknownFileFormat; + } + + // assimp export + // Export a model to a file + if (! strcmp(argv[1], "export")) { + return Assimp_Export (&argv[2],argc-2); + } + +#endif + + // assimp knowext + // Check whether a particular file extension is known by us, return 0 on success + if (! strcmp(argv[1], "knowext")) { + if (argc<3) { + printf("Expected file extension"); + return AssimpCmdError::NoFileExtensionSpecified; + } + const bool b = imp.IsExtensionSupported(argv[2]); + printf("File extension \'%s\' is %sknown\n",argv[2],(b?"":"not ")); + return b? AssimpCmdError::Success : AssimpCmdError::UnknownFileExtension; + } + + // assimp info + // Print basic model statistics + if (! strcmp(argv[1], "info")) { + return Assimp_Info ((const char**)&argv[2],argc-2); + } + + // assimp dump + // Dump a model to a file + if (! strcmp(argv[1], "dump")) { + return Assimp_Dump (&argv[2],argc-2); + } + + // assimp extract + // Extract an embedded texture from a file + if (! strcmp(argv[1], "extract")) { + return Assimp_Extract (&argv[2],argc-2); + } + + // assimp testbatchload + // Used by /test/other/streamload.py to load a list of files + // using the same importer instance to check for incompatible + // importers. + if (! strcmp(argv[1], "testbatchload")) { + return Assimp_TestBatchLoad (&argv[2],argc-2); + } + + printf("Unrecognized command. Use \'assimp help\' for a detailed command list\n"); + return AssimpCmdError::UnrecognizedCommand; +} + + +// ------------------------------------------------------------------------------ +void SetLogStreams(const ImportData& imp) +{ + printf("\nAttaching log stream ... OK\n"); + + unsigned int flags = 0; + if (imp.logFile.length()) { + flags |= aiDefaultLogStream_FILE; + } + if (imp.showLog) { + flags |= aiDefaultLogStream_STDERR; + } + DefaultLogger::create(imp.logFile.c_str(),imp.verbose ? Logger::VERBOSE : Logger::NORMAL,flags); +} + + +// ------------------------------------------------------------------------------ +void FreeLogStreams() +{ + DefaultLogger::kill(); +} + + +// ------------------------------------------------------------------------------ +void PrintHorBar() +{ + printf("-----------------------------------------------------------------\n"); +} + +// ------------------------------------------------------------------------------ +// Import a specific file +const aiScene* ImportModel( + const ImportData& imp, + const std::string& path) +{ + // Attach log streams + if (imp.log) { + SetLogStreams(imp); + } + printf("Launching asset import ... OK\n"); + + // Now validate this flag combination + if(!globalImporter->ValidateFlags(imp.ppFlags)) { + printf("ERROR: Unsupported post-processing flags \n"); + return NULL; + } + printf("Validating postprocessing flags ... OK\n"); + if (imp.showLog) { + PrintHorBar(); + } + + + // do the actual import, measure time + const clock_t first = clock(); + ConsoleProgressHandler *ph = new ConsoleProgressHandler; + globalImporter->SetProgressHandler(ph); + + const aiScene* scene = globalImporter->ReadFile(path,imp.ppFlags); + + if (imp.showLog) { + PrintHorBar(); + } + if (!scene) { + printf("ERROR: Failed to load file: %s\n", globalImporter->GetErrorString()); + return NULL; + } + + const clock_t second = ::clock(); + const double seconds = static_cast<double>(second-first) / CLOCKS_PER_SEC; + + printf("Importing file ... OK \n import took approx. %.5f seconds\n" + "\n",seconds); + + if (imp.log) { + FreeLogStreams(); + } + globalImporter->SetProgressHandler(nullptr); + delete ph; + + return scene; +} + +#ifndef ASSIMP_BUILD_NO_EXPORT +// ------------------------------------------------------------------------------ +bool ExportModel(const aiScene* pOut, + const ImportData& imp, + const std::string& path, + const char* pID) +{ + // Attach log streams + if (imp.log) { + SetLogStreams(imp); + } + printf("Launching asset export ... OK\n"); + + if (imp.showLog) { + PrintHorBar(); + } + + aiMatrix4x4 rx, ry, rz; + aiMatrix4x4::RotationX(imp.rot.x, rx); + aiMatrix4x4::RotationY(imp.rot.y, ry); + aiMatrix4x4::RotationZ(imp.rot.z, rz); + pOut->mRootNode->mTransformation *= rx; + pOut->mRootNode->mTransformation *= ry; + pOut->mRootNode->mTransformation *= rz; + + // do the actual export, measure time + const clock_t first = clock(); + const aiReturn res = globalExporter->Export(pOut,pID,path); + + if (imp.showLog) { + PrintHorBar(); + } + if (res != AI_SUCCESS) { + printf("Failed to write file\n"); + printf("ERROR: %s\n", globalExporter->GetErrorString()); + return false; + } + + const clock_t second = ::clock(); + const double seconds = static_cast<double>(second-first) / CLOCKS_PER_SEC; + + printf("Exporting file ... OK \n export took approx. %.5f seconds\n" + "\n",seconds); + + if (imp.log) { + FreeLogStreams(); + } + + return true; +} +#endif + +// ------------------------------------------------------------------------------ +// Process standard arguments +int ProcessStandardArguments( + ImportData& fill, + const char* const * params, + unsigned int num) +{ + // -ptv --pretransform-vertices + // -gsn --gen-smooth-normals + // -gn --gen-normals + // -cts --calc-tangent-space + // -jiv --join-identical-vertices + // -rrm --remove-redundant-materials + // -fd --find-degenerates + // -slm --split-large-meshes + // -lbw --limit-bone-weights + // -vds --validate-data-structure + // -icl --improve-cache-locality + // -sbpt --sort-by-ptype + // -lh --convert-to-lh + // -fuv --flip-uv + // -fwo --flip-winding-order + // -tuv --transform-uv-coords + // -guv --gen-uvcoords + // -fid --find-invalid-data + // -fixn --fix normals + // -tri --triangulate + // -fi --find-instances + // -og --optimize-graph + // -om --optimize-meshes + // -db --debone + // -sbc --split-by-bone-count + // -gs --global-scale + // + // -c<file> --config-file=<file> + + for (unsigned int i = 0; i < num;++i) + { + const char *param = params[ i ]; + printf( "param = %s\n", param ); + if (! strcmp( param, "-ptv") || ! strcmp( param, "--pretransform-vertices")) { + fill.ppFlags |= aiProcess_PreTransformVertices; + } + else if (! strcmp( param, "-gsn") || ! strcmp( param, "--gen-smooth-normals")) { + fill.ppFlags |= aiProcess_GenSmoothNormals; + } + else if (! strcmp( param, "-dn") || ! strcmp( param, "--drop-normals")) { + fill.ppFlags |= aiProcess_DropNormals; + } + else if (! strcmp( param, "-gn") || ! strcmp( param, "--gen-normals")) { + fill.ppFlags |= aiProcess_GenNormals; + } + else if (! strcmp( param, "-jiv") || ! strcmp( param, "--join-identical-vertices")) { + fill.ppFlags |= aiProcess_JoinIdenticalVertices; + } + else if (! strcmp( param, "-rrm") || ! strcmp( param, "--remove-redundant-materials")) { + fill.ppFlags |= aiProcess_RemoveRedundantMaterials; + } + else if (! strcmp( param, "-fd") || ! strcmp( param, "--find-degenerates")) { + fill.ppFlags |= aiProcess_FindDegenerates; + } + else if (! strcmp( param, "-slm") || ! strcmp( param, "--split-large-meshes")) { + fill.ppFlags |= aiProcess_SplitLargeMeshes; + } + else if (! strcmp( param, "-lbw") || ! strcmp( param, "--limit-bone-weights")) { + fill.ppFlags |= aiProcess_LimitBoneWeights; + } + else if (! strcmp( param, "-vds") || ! strcmp( param, "--validate-data-structure")) { + fill.ppFlags |= aiProcess_ValidateDataStructure; + } + else if (! strcmp( param, "-icl") || ! strcmp( param, "--improve-cache-locality")) { + fill.ppFlags |= aiProcess_ImproveCacheLocality; + } + else if (! strcmp( param, "-sbpt") || ! strcmp( param, "--sort-by-ptype")) { + fill.ppFlags |= aiProcess_SortByPType; + } + else if (! strcmp( param, "-lh") || ! strcmp( param, "--left-handed")) { + fill.ppFlags |= aiProcess_ConvertToLeftHanded; + } + else if (! strcmp( param, "-fuv") || ! strcmp( param, "--flip-uv")) { + fill.ppFlags |= aiProcess_FlipUVs; + } + else if (! strcmp( param, "-fwo") || ! strcmp( param, "--flip-winding-order")) { + fill.ppFlags |= aiProcess_FlipWindingOrder; + } + else if (! strcmp( param, "-tuv") || ! strcmp( param, "--transform-uv-coords")) { + fill.ppFlags |= aiProcess_TransformUVCoords; + } + else if (! strcmp( param, "-guv") || ! strcmp( param, "--gen-uvcoords")) { + fill.ppFlags |= aiProcess_GenUVCoords; + } + else if (! strcmp( param, "-fid") || ! strcmp( param, "--find-invalid-data")) { + fill.ppFlags |= aiProcess_FindInvalidData; + } + else if (! strcmp( param, "-fixn") || ! strcmp( param, "--fix-normals")) { + fill.ppFlags |= aiProcess_FixInfacingNormals; + } + else if (! strcmp( param, "-tri") || ! strcmp( param, "--triangulate")) { + fill.ppFlags |= aiProcess_Triangulate; + } + else if (! strcmp( param, "-cts") || ! strcmp( param, "--calc-tangent-space")) { + fill.ppFlags |= aiProcess_CalcTangentSpace; + } + else if (! strcmp( param, "-fi") || ! strcmp( param, "--find-instances")) { + fill.ppFlags |= aiProcess_FindInstances; + } + else if (! strcmp( param, "-og") || ! strcmp( param, "--optimize-graph")) { + fill.ppFlags |= aiProcess_OptimizeGraph; + } + else if (! strcmp( param, "-om") || ! strcmp( param, "--optimize-meshes")) { + fill.ppFlags |= aiProcess_OptimizeMeshes; + } + else if (! strcmp( param, "-db") || ! strcmp( param, "--debone")) { + fill.ppFlags |= aiProcess_Debone; + } + else if (! strcmp( param, "-sbc") || ! strcmp( param, "--split-by-bone-count")) { + fill.ppFlags |= aiProcess_SplitByBoneCount; + } + else if (!strcmp(param, "-embtex") || ! strcmp(param, "--embed-textures")) { + fill.ppFlags |= aiProcess_EmbedTextures; + } + else if (!strcmp(param, "-gs") || ! strcmp(param, "--global-scale")) { + fill.ppFlags |= aiProcess_GlobalScale; + } + else if (! strncmp( param, "-c",2) || ! strncmp( param, "--config=",9)) { + const unsigned int ofs = (params[i][1] == '-' ? 9 : 2); + + // use default configurations + if (!strncmp( param + ofs, "full", 4 )) { + fill.ppFlags |= aiProcessPreset_TargetRealtime_MaxQuality; + } else if (!strncmp( param + ofs, "default", 7 )) { + fill.ppFlags |= aiProcessPreset_TargetRealtime_Quality; + } else if (! strncmp( param +ofs,"fast",4)) { + fill.ppFlags |= aiProcessPreset_TargetRealtime_Fast; + } + } else if (! strcmp( param, "-l") || ! strcmp( param, "--show-log")) { + fill.showLog = true; + } + else if (! strcmp( param, "-v") || ! strcmp( param, "--verbose")) { + fill.verbose = true; + } + else if (!strncmp(params[i], "-rx=", 4) || !strncmp(params[i], "--rotation-x=", 13)) { + std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4)); + fill.rot.x = std::stof(value); + } + else if (!strncmp(params[i], "-ry=", 4) || !strncmp(params[i], "--rotation-y=", 13)) { + std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4)); + fill.rot.y = std::stof(value); + } + else if (!strncmp(params[i], "-rz=", 4) || !strncmp(params[i], "--rotation-z=", 13)) { + std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4)); + fill.rot.z = std::stof(value); + } + else if (! strncmp( param, "--log-out=",10) || ! strncmp( param, "-lo",3)) { + fill.logFile = std::string(params[i]+(params[i][1] == '-' ? 10 : 3)); + if (!fill.logFile.length()) { + fill.logFile = "assimp-log.txt"; + } + } + } + + if (fill.logFile.length() || fill.showLog || fill.verbose) { + fill.log = true; + } + + return AssimpCmdError::Success; +} + +// ------------------------------------------------------------------------------ +int Assimp_TestBatchLoad ( + const char* const* params, + unsigned int num) +{ + for(unsigned int i = 0; i < num; ++i) { + globalImporter->ReadFile(params[i],aiProcessPreset_TargetRealtime_MaxQuality); + // we're totally silent. scene destructs automatically. + } + return AssimpCmdError::Success; +} diff --git a/libs/assimp/tools/assimp_cmd/Main.h b/libs/assimp/tools/assimp_cmd/Main.h new file mode 100644 index 0000000..1d3dd8f --- /dev/null +++ b/libs/assimp/tools/assimp_cmd/Main.h @@ -0,0 +1,273 @@ +/* +--------------------------------------------------------------------------- +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 Main.h + * @brief Utility declarations for assimp_cmd + */ + +#ifndef AICMD_MAIN_INCLUDED +#define AICMD_MAIN_INCLUDED + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <limits> + +#include <assimp/postprocess.h> +#include <assimp/version.h> +#include <assimp/scene.h> +#include <assimp/Importer.hpp> +#include <assimp/DefaultLogger.hpp> + +#ifndef ASSIMP_BUILD_NO_EXPORT +# include <assimp/Exporter.hpp> +#endif + +#ifdef ASSIMP_BUILD_NO_OWN_ZLIB +#include <zlib.h> +#else +#include <../contrib/zlib/zlib.h> +#endif + + +#ifndef SIZE_MAX +# define SIZE_MAX (std::numeric_limits<size_t>::max()) +#endif + + +using namespace Assimp; + + +// Global assimp importer instance +extern Assimp::Importer* globalImporter; + +#ifndef ASSIMP_BUILD_NO_EXPORT +// Global assimp exporter instance +extern Assimp::Exporter* globalExporter; +#endif + +// ------------------------------------------------------------------------------ +/// Defines common import parameters +struct ImportData { + ImportData() + : ppFlags (0) + , showLog (false) + , verbose (false) + , log (false) + , rot (aiVector3D(0.f, 0.f, 0.f)) + {} + + /// Post-processing flags + unsigned int ppFlags; + + // Log to std::err? + bool showLog; + + // Log file + std::string logFile; + + // Verbose log mode? + bool verbose; + + // Need to log? + bool log; + + // Export With Rotation + aiVector3D rot; +}; + +/// \enum AssimpCmdError +/// \brief General error codes used among assimp_cmd's utilities. +enum AssimpCmdError { + Success = 0, + InvalidNumberOfArguments, + UnrecognizedCommand, + FailedToLoadInputFile, + FailedToOpenOutputFile, + NoFileFormatSpecified, + UnknownFileFormat, + NoFileExtensionSpecified, + UnknownFileExtension, + ExceptionWasRaised, + + // Add new error codes here... + + LastAssimpCmdError, // Must be last. +}; + +// ------------------------------------------------------------------------------ +/** Process standard arguments + * + * @param fill Filled by function + * @param params Command line parameters to be processed + * @param num NUmber of params + * @return An #AssimpCmdError value. */ +int ProcessStandardArguments(ImportData& fill, + const char* const* params, + unsigned int num); + +// ------------------------------------------------------------------------------ +/** Import a specific model file + * @param imp Import configuration to be used + * @param path Path to the file to be read */ +const aiScene* ImportModel( + const ImportData& imp, + const std::string& path); + +#ifndef ASSIMP_BUILD_NO_EXPORT + +// ------------------------------------------------------------------------------ +/** Export a specific model file + * @param imp Import configuration to be used + * @param path Path to the file to be written + * @param format Format id*/ +bool ExportModel(const aiScene* pOut, + const ImportData& imp, + const std::string& path, + const char* pID); + +#endif + +// ------------------------------------------------------------------------------ +/** assimp_dump utility + * @param params Command line parameters to 'assimp dump' + * @param Number of params + * @return An #AssimpCmdError value.*/ +int Assimp_Dump ( + const char* const* params, + unsigned int num); + +/// \enum AssimpCmdExportError +/// \brief Error codes used by the 'Export' utility. +enum AssimpCmdExportError { + FailedToImportModel = AssimpCmdError::LastAssimpCmdError, + FailedToExportModel, + + // Add new error codes here... + + LastAssimpCmdExportError, // Must be last. +}; + +// ------------------------------------------------------------------------------ +/** assimp_export utility + * @param params Command line parameters to 'assimp export' + * @param Number of params + * @return Either an #AssimpCmdError or #AssimpCmdExportError value. */ +int Assimp_Export ( + const char* const* params, + unsigned int num); + +/// \enum AssimpCmdExtractError +/// \brief Error codes used by the 'Image Extractor' utility. +enum AssimpCmdExtractError { + TextureIndexIsOutOfRange = AssimpCmdError::LastAssimpCmdError, + NoAvailableTextureEncoderFound, + FailedToExportCompressedTexture, + + // Add new error codes here... + + LastAssimpCmdExtractError, // Must be last. +}; + +// ------------------------------------------------------------------------------ +/** assimp_extract utility + * @param params Command line parameters to 'assimp extract' + * @param Number of params + * @return Either an #AssimpCmdError or #AssimpCmdExtractError value. */ +int Assimp_Extract ( + const char* const* params, + unsigned int num); + +/// \enum AssimpCmdCompareDumpError +/// \brief Error codes used by the 'Compare Dump' utility. +enum AssimpCmdCompareDumpError { + FailedToLoadExpectedInputFile = AssimpCmdError::LastAssimpCmdError, + FileComparaisonFailure, + UnknownFailure, + + // Add new error codes here... + + LastAssimpCmdCompareDumpError, // Must be last. +}; + +// ------------------------------------------------------------------------------ +/** assimp_cmpdump utility + * @param params Command line parameters to 'assimp cmpdump' + * @param Number of params + * @return Either an #AssimpCmdError or #AssimpCmdCompareDumpError. */ +int Assimp_CompareDump ( + const char* const* params, + unsigned int num); + +/// \enum AssimpCmdInfoError +/// \brief Error codes used by the 'Info' utility. +enum AssimpCmdInfoError { + InvalidCombinaisonOfArguments = AssimpCmdError::LastAssimpCmdError, + + // Add new error codes here... + + LastAssimpCmdInfoError, // Must be last. +}; + +// ------------------------------------------------------------------------------ +/** @brief assimp info utility + * @param params Command line parameters to 'assimp info' + * @param Number of params + * @return Either an #AssimpCmdError or #AssimpCmdInfoError value. */ +int Assimp_Info ( + const char* const* params, + unsigned int num); + +// ------------------------------------------------------------------------------ +/** @brief assimp testbatchload utility + * @param params Command line parameters to 'assimp testbatchload' + * @param Number of params + * @return An #AssimpCmdError value. */ +int Assimp_TestBatchLoad ( + const char* const* params, + unsigned int num); + + +#endif // !! AICMD_MAIN_INCLUDED diff --git a/libs/assimp/tools/assimp_cmd/WriteDump.cpp b/libs/assimp/tools/assimp_cmd/WriteDump.cpp new file mode 100644 index 0000000..4ada408 --- /dev/null +++ b/libs/assimp/tools/assimp_cmd/WriteDump.cpp @@ -0,0 +1,172 @@ +/* +--------------------------------------------------------------------------- +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 WriteDump.cpp + * @brief Implementation of the 'assimp dump' utility + */ + +#include "Main.h" +#include "PostProcessing/ProcessHelper.h" + +const char *AICMD_MSG_DUMP_HELP = + "assimp dump <model> [<out>] [-b] [-s] [-z] [common parameters]\n" + "\t -b Binary output \n" + "\t -s Shortened \n" + "\t -z Compressed \n" + "\t[See the assimp_cmd docs for a full list of all common parameters] \n" + "\t -cfast Fast post processing preset, runs just a few important steps \n" + "\t -cdefault Default post processing: runs all recommended steps\n" + "\t -cfull Fires almost all post processing steps \n"; + +#include "Common/assbin_chunks.h" +#include <assimp/DefaultIOSystem.h> +#include "AssetLib/Assbin/AssbinFileWriter.h" +#include "AssetLib/Assxml/AssxmlFileWriter.h" + +#include <memory> + +FILE *out = nullptr; +bool shortened = false; + +#ifndef ASSIMP_BUILD_NO_EXPORT + +// ----------------------------------------------------------------------------------- +int Assimp_Dump(const char *const *params, unsigned int num) { + const char *fail = "assimp dump: Invalid number of arguments. " + "See \'assimp dump --help\'\r\n"; + + // --help + if (!strcmp(params[0], "-h") || !strcmp(params[0], "--help") || !strcmp(params[0], "-?")) { + printf("%s", AICMD_MSG_DUMP_HELP); + return AssimpCmdError::Success; + } + + // asssimp dump in out [options] + if (num < 1) { + printf("%s", fail); + return AssimpCmdError::InvalidNumberOfArguments; + } + + std::string in = std::string(params[0]); + std::string cur_out = (num > 1 ? std::string(params[1]) : std::string("-")); + + // store full command line + std::string cmd; + for (unsigned int i = (cur_out[0] == '-' ? 1 : 2); i < num; ++i) { + if (!params[i]) continue; + cmd.append(params[i]); + cmd.append(" "); + } + + // get import flags + ImportData import; + ProcessStandardArguments(import, params + 1, num - 1); + + bool binary = false, cur_shortened = false, compressed = false; + + // process other flags + for (unsigned int i = 1; i < num; ++i) { + if (!params[i]) { + continue; + } + if (!strcmp(params[i], "-b") || !strcmp(params[i], "--binary")) { + binary = true; + } else if (!strcmp(params[i], "-s") || !strcmp(params[i], "--short")) { + cur_shortened = true; + } else if (!strcmp(params[i], "-z") || !strcmp(params[i], "--compressed")) { + compressed = true; + } +#if 0 + else if (i > 2 || params[i][0] == '-') { + ::printf("Unknown parameter: %s\n",params[i]); + return 10; + } +#endif + } + + if (cur_out[0] == '-') { + // take file name from input file + std::string::size_type pos = in.find_last_of('.'); + if (pos == std::string::npos) { + pos = in.length(); + } + + cur_out = in.substr(0, pos); + cur_out.append((binary ? ".assbin" : ".assxml")); + if (cur_shortened && binary) { + cur_out.append(".regress"); + } + } + + // import the main model + const aiScene *scene = ImportModel(import, in); + if (!scene) { + printf("assimp dump: Unable to load input file %s\n", in.c_str()); + return AssimpCmdError::FailedToLoadInputFile; + } + + try { + // Dump the main model, using the appropriate method. + std::unique_ptr<IOSystem> pIOSystem(new DefaultIOSystem()); + if (binary) { + DumpSceneToAssbin(cur_out.c_str(), cmd.c_str(), pIOSystem.get(), + scene, shortened, compressed); + } else { + DumpSceneToAssxml(cur_out.c_str(), cmd.c_str(), pIOSystem.get(), + scene, shortened); + } + } catch (const std::exception &e) { + printf("%s", ("assimp dump: " + std::string(e.what())).c_str()); + return AssimpCmdError::ExceptionWasRaised; + } catch (...) { + printf("assimp dump: An unknown exception occurred.\n"); + return AssimpCmdError::ExceptionWasRaised; + } + + printf("assimp dump: Wrote output dump %s\n", cur_out.c_str()); + return AssimpCmdError::Success; +} +#else +int Assimp_Dump(const char *const *, unsigned int ) { + printf("assimp dump: Export disabled.\n"); + return AssimpCmdError::UnrecognizedCommand; +} +#endif
\ No newline at end of file diff --git a/libs/assimp/tools/assimp_cmd/assimp_cmd.rc b/libs/assimp/tools/assimp_cmd/assimp_cmd.rc new file mode 100644 index 0000000..e671006 --- /dev/null +++ b/libs/assimp/tools/assimp_cmd/assimp_cmd.rc @@ -0,0 +1,51 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" +#include "../../revision.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Deutsch (Deutschland) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) +#ifdef _WIN32 +LANGUAGE LANG_GERMAN, SUBLANG_GERMAN +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ASSIMP_VIEW ICON "../shared/assimp_tools_icon.ico" + + + + + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED +#endif diff --git a/libs/assimp/tools/assimp_cmd/generic_inserter.hpp b/libs/assimp/tools/assimp_cmd/generic_inserter.hpp new file mode 100644 index 0000000..8053219 --- /dev/null +++ b/libs/assimp/tools/assimp_cmd/generic_inserter.hpp @@ -0,0 +1,113 @@ +/* Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. */ + + +#ifndef HEADER_GENERIC_INSERTER_HPP_INCLUDED +#define HEADER_GENERIC_INSERTER_HPP_INCLUDED + + +#include <ostream> +#include <new> // bad_alloc + + +template <typename char_type, typename traits_type, typename argument_type> +std::basic_ostream<char_type, traits_type>& generic_inserter(void (*print)(std::basic_ostream<char_type, traits_type>& os, argument_type const& arg), std::basic_ostream<char_type, traits_type>& os, argument_type const& arg) +{ + using namespace ::std; + + ios_base::iostate err = ios_base::goodbit; + try + { + typename basic_ostream<char_type, traits_type>::sentry sentry(os); + if (sentry) + { + print(os, arg); + err = os.rdstate(); + os.width(0); // Reset width in case the user didn't do it. + } + } + catch (bad_alloc const&) + { + err |= ios_base::badbit; // bad_alloc is considered fatal + ios_base::iostate const exception_mask = os.exceptions(); + + // Two cases: 1.) badbit is not set; 2.) badbit is set + if (((exception_mask & ios_base::failbit) != 0) && // failbit shall throw + ((exception_mask & ios_base::badbit) == 0)) // badbit shall not throw + { + // Do not throw unless failbit is set. + // If it is set throw ios_base::failure because we don't know what caused the failbit to be set. + os.setstate(err); + } + else if (exception_mask & ios_base::badbit) + { + try + { + // This will set the badbit and throw ios_base::failure. + os.setstate(err); + } + catch (ios_base::failure const&) + { + // Do nothing since we want bad_alloc to be rethrown. + } + throw; + } + // else: no exception must get out! + } + catch (...) + { + err |= ios_base::failbit; // Any other exception is considered "only" as a failure. + ios_base::iostate const exception_mask = os.exceptions(); + + // badbit is considered more important + if (((exception_mask & ios_base::badbit) != 0) && // badbit shall throw + ((err & ios_base::badbit) != 0)) // badbit is set + { + // Throw ios_base::failure because we don't know what caused the badbit to be set. + os.setstate(err); + } + else if ((exception_mask & ios_base::failbit) != 0) + { + try + { + // This will set the failbit and throw the exception ios_base::failure. + os.setstate(err); + } + catch (ios_base::failure const&) + { + // Do nothing since we want the original exception to be rethrown. + } + throw; + } + // else: no exception must get out! + } + + // Needed in the case that no exception has been thrown but the stream state has changed. + if (err) + os.setstate(err); + return os; +} + + +#endif // HEADER_GENERIC_INSERTER_HPP_INCLUDED diff --git a/libs/assimp/tools/assimp_cmd/resource.h b/libs/assimp/tools/assimp_cmd/resource.h new file mode 100644 index 0000000..caf3a0a --- /dev/null +++ b/libs/assimp/tools/assimp_cmd/resource.h @@ -0,0 +1,21 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by assimp_view.rc +// +#define IDC_MYICON 2 +#define IDD_ASSIMP_VIEW_DIALOG 102 +#define IDD_ABOUTBOX 103 +#define IDI_ASSIMP_VIEW 107 + + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 159 +#define _APS_NEXT_COMMAND_VALUE 32831 +#define _APS_NEXT_CONTROL_VALUE 1052 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif |