/src/assimp/code/AssetLib/IQM/IQMImporter.cpp
Line | Count | Source |
1 | | /* |
2 | | Open Asset Import Library (assimp) |
3 | | ---------------------------------------------------------------------- |
4 | | |
5 | | Copyright (c) 2006-2021, assimp team |
6 | | |
7 | | All rights reserved. |
8 | | |
9 | | Redistribution and use of this software in source and binary forms, |
10 | | with or without modification, are permitted provided that the |
11 | | following conditions are met: |
12 | | |
13 | | * Redistributions of source code must retain the above |
14 | | copyright notice, this list of conditions and the |
15 | | following disclaimer. |
16 | | |
17 | | * Redistributions in binary form must reproduce the above |
18 | | copyright notice, this list of conditions and the |
19 | | following disclaimer in the documentation and/or other |
20 | | materials provided with the distribution. |
21 | | |
22 | | * Neither the name of the assimp team, nor the names of its |
23 | | contributors may be used to endorse or promote products |
24 | | derived from this software without specific prior |
25 | | written permission of the assimp team. |
26 | | |
27 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
28 | | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
29 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
30 | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
31 | | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
32 | | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
33 | | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
34 | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
35 | | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
36 | | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
37 | | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
38 | | |
39 | | ---------------------------------------------------------------------- |
40 | | */ |
41 | | |
42 | | #ifndef ASSIMP_BUILD_NO_IQM_IMPORTER |
43 | | |
44 | | #include <assimp/DefaultIOSystem.h> |
45 | | #include <assimp/IOStreamBuffer.h> |
46 | | #include <assimp/ai_assert.h> |
47 | | #include <assimp/importerdesc.h> |
48 | | #include <assimp/scene.h> |
49 | | #include <assimp/DefaultLogger.hpp> |
50 | | #include <assimp/Importer.hpp> |
51 | | #include <assimp/ByteSwapper.h> |
52 | | #include <memory> |
53 | | #include <numeric> |
54 | | |
55 | | #include "IQMImporter.h" |
56 | | #include "iqm.h" |
57 | | |
58 | | // RESOURCES: |
59 | | // http://sauerbraten.org/iqm/ |
60 | | // https://github.com/lsalzman/iqm |
61 | | |
62 | 0 | inline void swap_block( uint32_t *block, size_t size ){ |
63 | 0 | (void)block; // suppress 'unreferenced formal parameter' MSVC warning |
64 | 0 | size >>= 2; |
65 | 0 | for ( size_t i = 0; i < size; ++i ) |
66 | 0 | AI_SWAP4( block[ i ] ); |
67 | 0 | } |
68 | | |
69 | | static constexpr aiImporterDesc desc = { |
70 | | "Inter-Quake Model Importer", |
71 | | "", |
72 | | "", |
73 | | "", |
74 | | aiImporterFlags_SupportBinaryFlavour, |
75 | | 0, |
76 | | 0, |
77 | | 0, |
78 | | 0, |
79 | | "iqm" |
80 | | }; |
81 | | |
82 | | namespace Assimp { |
83 | | |
84 | | // ------------------------------------------------------------------------------------------------ |
85 | | // Default constructor |
86 | | IQMImporter::IQMImporter() : |
87 | 327 | mScene(nullptr) { |
88 | | // empty |
89 | 327 | } |
90 | | |
91 | | // ------------------------------------------------------------------------------------------------ |
92 | | // Returns true, if file is a binary Inter-Quake Model file. |
93 | 27 | bool IQMImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { |
94 | 27 | const std::string extension = GetExtension(pFile); |
95 | | |
96 | 27 | if (extension == "iqm") |
97 | 0 | return true; |
98 | 27 | else if (!extension.length() || checkSig) { |
99 | 27 | if (!pIOHandler) { |
100 | 0 | return true; |
101 | 0 | } |
102 | 27 | std::unique_ptr<IOStream> pStream(pIOHandler->Open(pFile, "rb")); |
103 | 27 | unsigned char data[15]; |
104 | 27 | if (!pStream || 15 != pStream->Read(data, 1, 15)) { |
105 | 0 | return false; |
106 | 0 | } |
107 | 27 | return !memcmp(data, "INTERQUAKEMODEL", 15); |
108 | 27 | } |
109 | 0 | return false; |
110 | 27 | } |
111 | | |
112 | | // ------------------------------------------------------------------------------------------------ |
113 | 333 | const aiImporterDesc *IQMImporter::GetInfo() const { |
114 | 333 | return &desc; |
115 | 333 | } |
116 | | |
117 | | // ------------------------------------------------------------------------------------------------ |
118 | | // Model 3D import implementation |
119 | 0 | void IQMImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSystem *pIOHandler) { |
120 | | // Read file into memory |
121 | 0 | std::unique_ptr<IOStream> pStream(pIOHandler->Open(file, "rb")); |
122 | 0 | if (!pStream) { |
123 | 0 | throw DeadlyImportError("Failed to open file ", file, "."); |
124 | 0 | } |
125 | | |
126 | | // Get the file-size and validate it, throwing an exception when fails |
127 | 0 | const size_t fileSize = pStream->FileSize(); |
128 | 0 | if (fileSize < sizeof( iqmheader )) { |
129 | 0 | throw DeadlyImportError("IQM-file ", file, " is too small."); |
130 | 0 | } |
131 | 0 | std::vector<unsigned char> buffer(fileSize); |
132 | 0 | unsigned char *data = buffer.data(); |
133 | 0 | if (fileSize != pStream->Read(data, 1, fileSize)) { |
134 | 0 | throw DeadlyImportError("Failed to read the file ", file, "."); |
135 | 0 | } |
136 | | |
137 | | // get header |
138 | 0 | iqmheader &hdr = reinterpret_cast<iqmheader&>( *data ); |
139 | 0 | swap_block( &hdr.version, sizeof( iqmheader ) - sizeof( iqmheader::magic ) ); |
140 | | |
141 | | // extra check for header |
142 | 0 | if (memcmp(data, IQM_MAGIC, sizeof( IQM_MAGIC ) ) |
143 | 0 | || hdr.version != IQM_VERSION |
144 | 0 | || hdr.filesize != fileSize) { |
145 | 0 | throw DeadlyImportError("Bad binary header in file ", file, "."); |
146 | 0 | } |
147 | | |
148 | 0 | ASSIMP_LOG_DEBUG("IQM: loading ", file); |
149 | | |
150 | | // create the root node |
151 | 0 | pScene->mRootNode = new aiNode( "<IQMRoot>" ); |
152 | | // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system |
153 | 0 | pScene->mRootNode->mTransformation = aiMatrix4x4( |
154 | 0 | 1.f, 0.f, 0.f, 0.f, |
155 | 0 | 0.f, 0.f, 1.f, 0.f, |
156 | 0 | 0.f, -1.f, 0.f, 0.f, |
157 | 0 | 0.f, 0.f, 0.f, 1.f); |
158 | 0 | pScene->mRootNode->mNumMeshes = hdr.num_meshes; |
159 | 0 | pScene->mRootNode->mMeshes = new unsigned int[hdr.num_meshes]; |
160 | 0 | std::iota( pScene->mRootNode->mMeshes, pScene->mRootNode->mMeshes + pScene->mRootNode->mNumMeshes, 0 ); |
161 | |
|
162 | 0 | mScene = pScene; |
163 | | |
164 | | // Allocate output storage |
165 | 0 | pScene->mNumMeshes = 0; |
166 | 0 | pScene->mMeshes = new aiMesh *[hdr.num_meshes](); // Set arrays to zero to ensue proper destruction if an exception is raised |
167 | |
|
168 | 0 | pScene->mNumMaterials = 0; |
169 | 0 | pScene->mMaterials = new aiMaterial *[hdr.num_meshes](); |
170 | | |
171 | | // swap vertex arrays beforehand... |
172 | 0 | for( auto array = reinterpret_cast<iqmvertexarray*>( data + hdr.ofs_vertexarrays ), end = array + hdr.num_vertexarrays; array != end; ++array ) |
173 | 0 | { |
174 | 0 | swap_block( &array->type, sizeof( iqmvertexarray ) ); |
175 | 0 | } |
176 | | |
177 | | // Read all surfaces from the file |
178 | 0 | for( auto imesh = reinterpret_cast<iqmmesh*>( data + hdr.ofs_meshes ), end_ = imesh + hdr.num_meshes; imesh != end_; ++imesh ) |
179 | 0 | { |
180 | 0 | swap_block( &imesh->name, sizeof( iqmmesh ) ); |
181 | | // Allocate output mesh & material |
182 | 0 | auto mesh = pScene->mMeshes[pScene->mNumMeshes++] = new aiMesh(); |
183 | 0 | mesh->mMaterialIndex = pScene->mNumMaterials; |
184 | 0 | auto mat = pScene->mMaterials[pScene->mNumMaterials++] = new aiMaterial(); |
185 | |
|
186 | 0 | { |
187 | 0 | auto text = reinterpret_cast<char*>( data + hdr.ofs_text ); |
188 | 0 | aiString name( text + imesh->material ); |
189 | 0 | mat->AddProperty( &name, AI_MATKEY_NAME ); |
190 | 0 | mat->AddProperty( &name, AI_MATKEY_TEXTURE_DIFFUSE(0) ); |
191 | 0 | } |
192 | | |
193 | | // Fill mesh information |
194 | 0 | mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; |
195 | 0 | mesh->mNumFaces = 0; |
196 | 0 | mesh->mFaces = new aiFace[imesh->num_triangles]; |
197 | | |
198 | | // Fill in all triangles |
199 | 0 | for( auto tri = reinterpret_cast<iqmtriangle*>( data + hdr.ofs_triangles ) + imesh->first_triangle, end = tri + imesh->num_triangles; tri != end; ++tri ) |
200 | 0 | { |
201 | 0 | swap_block( tri->vertex, sizeof( tri->vertex ) ); |
202 | 0 | auto& face = mesh->mFaces[mesh->mNumFaces++]; |
203 | 0 | face.mNumIndices = 3; |
204 | 0 | face.mIndices = new unsigned int[3]{ tri->vertex[0] - imesh->first_vertex, |
205 | 0 | tri->vertex[2] - imesh->first_vertex, |
206 | 0 | tri->vertex[1] - imesh->first_vertex }; |
207 | 0 | } |
208 | | |
209 | | // Fill in all vertices |
210 | 0 | for( auto array = reinterpret_cast<const iqmvertexarray*>( data + hdr.ofs_vertexarrays ), end__ = array + hdr.num_vertexarrays; array != end__; ++array ) |
211 | 0 | { |
212 | 0 | const unsigned int nVerts = imesh->num_vertexes; |
213 | 0 | const unsigned int step = array->size; |
214 | |
|
215 | 0 | switch ( array->type ) |
216 | 0 | { |
217 | 0 | case IQM_POSITION: |
218 | 0 | if( array->format == IQM_FLOAT && step >= 3 ){ |
219 | 0 | mesh->mNumVertices = nVerts; |
220 | 0 | auto v = mesh->mVertices = new aiVector3D[nVerts]; |
221 | 0 | for( auto f = reinterpret_cast<const float*>( data + array->offset ) + imesh->first_vertex * step, |
222 | 0 | end = f + nVerts * step; f != end; f += step, ++v ) |
223 | 0 | { |
224 | 0 | *v = { AI_BE( f[0] ), |
225 | 0 | AI_BE( f[1] ), |
226 | 0 | AI_BE( f[2] ) }; |
227 | 0 | } |
228 | 0 | } |
229 | 0 | break; |
230 | 0 | case IQM_TEXCOORD: |
231 | 0 | if( array->format == IQM_FLOAT && step >= 2) |
232 | 0 | { |
233 | 0 | auto v = mesh->mTextureCoords[0] = new aiVector3D[nVerts]; |
234 | 0 | mesh->mNumUVComponents[0] = 2; |
235 | 0 | for( auto f = reinterpret_cast<const float*>( data + array->offset ) + imesh->first_vertex * step, |
236 | 0 | end = f + nVerts * step; f != end; f += step, ++v ) |
237 | 0 | { |
238 | 0 | *v = { AI_BE( f[0] ), |
239 | 0 | 1 - AI_BE( f[1] ), 0 }; |
240 | 0 | } |
241 | 0 | } |
242 | 0 | break; |
243 | 0 | case IQM_NORMAL: |
244 | 0 | if (array->format == IQM_FLOAT && step >= 3) |
245 | 0 | { |
246 | 0 | auto v = mesh->mNormals = new aiVector3D[nVerts]; |
247 | 0 | for( auto f = reinterpret_cast<const float*>( data + array->offset ) + imesh->first_vertex * step, |
248 | 0 | end = f + nVerts * step; f != end; f += step, ++v ) |
249 | 0 | { |
250 | 0 | *v = { AI_BE( f[0] ), |
251 | 0 | AI_BE( f[1] ), |
252 | 0 | AI_BE( f[2] ) }; |
253 | 0 | } |
254 | 0 | } |
255 | 0 | break; |
256 | 0 | case IQM_COLOR: |
257 | 0 | if (array->format == IQM_UBYTE && step >= 3) |
258 | 0 | { |
259 | 0 | auto v = mesh->mColors[0] = new aiColor4D[nVerts]; |
260 | 0 | for( auto f = ( data + array->offset ) + imesh->first_vertex * step, |
261 | 0 | end = f + nVerts * step; f != end; f += step, ++v ) |
262 | 0 | { |
263 | 0 | *v = { ( f[0] ) / 255.f, |
264 | 0 | ( f[1] ) / 255.f, |
265 | 0 | ( f[2] ) / 255.f, |
266 | 0 | step == 3? 1 : ( f[3] ) / 255.f }; |
267 | 0 | } |
268 | 0 | } |
269 | 0 | else if (array->format == IQM_FLOAT && step >= 3) |
270 | 0 | { |
271 | 0 | auto v = mesh->mColors[0] = new aiColor4D[nVerts]; |
272 | 0 | for( auto f = reinterpret_cast<const float*>( data + array->offset ) + imesh->first_vertex * step, |
273 | 0 | end = f + nVerts * step; f != end; f += step, ++v ) |
274 | 0 | { |
275 | 0 | *v = { AI_BE( f[0] ), |
276 | 0 | AI_BE( f[1] ), |
277 | 0 | AI_BE( f[2] ), |
278 | 0 | step == 3? 1 : AI_BE( f[3] ) }; |
279 | 0 | } |
280 | 0 | } |
281 | 0 | break; |
282 | 0 | case IQM_TANGENT: |
283 | | #if 0 |
284 | | if (array->format == IQM_FLOAT && step >= 3) |
285 | | { |
286 | | auto v = mesh->mTangents = new aiVector3D[nVerts]; |
287 | | for( auto f = reinterpret_cast<const float*>( data + array->offset ) + imesh->first_vertex * step, |
288 | | end = f + nVerts * step; f != end; f += step, ++v ) |
289 | | { |
290 | | *v = { AI_BE( f[0] ), |
291 | | AI_BE( f[1] ), |
292 | | AI_BE( f[2] ) }; |
293 | | } |
294 | | } |
295 | | #endif |
296 | 0 | break; |
297 | 0 | case IQM_BLENDINDEXES: |
298 | 0 | case IQM_BLENDWEIGHTS: |
299 | 0 | case IQM_CUSTOM: |
300 | 0 | break; // these attributes are not relevant. |
301 | | |
302 | 0 | default: |
303 | 0 | break; |
304 | 0 | } |
305 | 0 | } |
306 | 0 | } |
307 | 0 | } |
308 | | |
309 | | |
310 | | // ------------------------------------------------------------------------------------------------ |
311 | | |
312 | | } // Namespace Assimp |
313 | | |
314 | | #endif // !! ASSIMP_BUILD_NO_IQM_IMPORTER |