/src/assimp/code/AssetLib/Assbin/AssbinFileWriter.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Open Asset Import Library (assimp) |
3 | | ---------------------------------------------------------------------- |
4 | | |
5 | | Copyright (c) 2006-2025, 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 | | /** @file AssbinFileWriter.cpp |
42 | | * @brief Implementation of Assbin file writer. |
43 | | */ |
44 | | |
45 | | #include "AssbinFileWriter.h" |
46 | | #include "Common/assbin_chunks.h" |
47 | | #include "PostProcessing/ProcessHelper.h" |
48 | | |
49 | | #include <assimp/Exceptional.h> |
50 | | #include <assimp/version.h> |
51 | | #include <assimp/IOStream.hpp> |
52 | | |
53 | | #include "zlib.h" |
54 | | |
55 | | #include <ctime> |
56 | | |
57 | | #if _MSC_VER |
58 | | #pragma warning(push) |
59 | | #pragma warning(disable : 4706) |
60 | | #endif // _MSC_VER |
61 | | |
62 | | namespace Assimp { |
63 | | |
64 | | template <typename T> |
65 | 0 | size_t Write(IOStream *stream, const T &v) { |
66 | 0 | return stream->Write(&v, sizeof(T), 1); |
67 | 0 | } Unexecuted instantiation: unsigned long Assimp::Write<bool>(Assimp::IOStream*, bool const&) Unexecuted instantiation: unsigned long Assimp::Write<int>(Assimp::IOStream*, int const&) Unexecuted instantiation: unsigned long Assimp::Write<unsigned long>(Assimp::IOStream*, unsigned long const&) |
68 | | |
69 | | // ----------------------------------------------------------------------------------- |
70 | | // Serialize an aiString |
71 | | template <> |
72 | 0 | inline size_t Write<aiString>(IOStream *stream, const aiString &s) { |
73 | 0 | const size_t s2 = (uint32_t)s.length; |
74 | 0 | stream->Write(&s, 4, 1); |
75 | 0 | stream->Write(s.data, s2, 1); |
76 | |
|
77 | 0 | return s2 + 4; |
78 | 0 | } |
79 | | |
80 | | // ----------------------------------------------------------------------------------- |
81 | | // Serialize an unsigned int as uint32_t |
82 | | template <> |
83 | 0 | inline size_t Write<unsigned int>(IOStream *stream, const unsigned int &w) { |
84 | 0 | const uint32_t t = (uint32_t)w; |
85 | 0 | if (w > t) { |
86 | | // this shouldn't happen, integers in Assimp data structures never exceed 2^32 |
87 | 0 | throw DeadlyExportError("loss of data due to 64 -> 32 bit integer conversion"); |
88 | 0 | } |
89 | | |
90 | 0 | stream->Write(&t, 4, 1); |
91 | |
|
92 | 0 | return 4; |
93 | 0 | } |
94 | | |
95 | | // ----------------------------------------------------------------------------------- |
96 | | // Serialize an unsigned int as uint16_t |
97 | | template <> |
98 | 0 | inline size_t Write<uint16_t>(IOStream *stream, const uint16_t &w) { |
99 | 0 | static_assert(sizeof(uint16_t) == 2, "sizeof(uint16_t)==2"); |
100 | 0 | stream->Write(&w, 2, 1); |
101 | |
|
102 | 0 | return 2; |
103 | 0 | } |
104 | | |
105 | | // ----------------------------------------------------------------------------------- |
106 | | // Serialize a float |
107 | | template <> |
108 | 0 | inline size_t Write<float>(IOStream *stream, const float &f) { |
109 | 0 | static_assert(sizeof(float) == 4, "sizeof(float)==4"); |
110 | 0 | stream->Write(&f, 4, 1); |
111 | |
|
112 | 0 | return 4; |
113 | 0 | } |
114 | | |
115 | | // ----------------------------------------------------------------------------------- |
116 | | // Serialize a double |
117 | | template <> |
118 | 0 | inline size_t Write<double>(IOStream *stream, const double &f) { |
119 | 0 | static_assert(sizeof(double) == 8, "sizeof(double)==8"); |
120 | 0 | stream->Write(&f, 8, 1); |
121 | |
|
122 | 0 | return 8; |
123 | 0 | } |
124 | | |
125 | | // ----------------------------------------------------------------------------------- |
126 | | // Serialize a vec3 |
127 | | template <> |
128 | 0 | inline size_t Write<aiVector3D>(IOStream *stream, const aiVector3D &v) { |
129 | 0 | size_t t = Write<ai_real>(stream, v.x); |
130 | 0 | t += Write<float>(stream, v.y); |
131 | 0 | t += Write<float>(stream, v.z); |
132 | |
|
133 | 0 | return t; |
134 | 0 | } |
135 | | |
136 | | // ----------------------------------------------------------------------------------- |
137 | | // Serialize a color value |
138 | | template <> |
139 | 0 | inline size_t Write<aiColor3D>(IOStream *stream, const aiColor3D &v) { |
140 | 0 | size_t t = Write<ai_real>(stream, v.r); |
141 | 0 | t += Write<float>(stream, v.g); |
142 | 0 | t += Write<float>(stream, v.b); |
143 | |
|
144 | 0 | return t; |
145 | 0 | } |
146 | | |
147 | | // ----------------------------------------------------------------------------------- |
148 | | // Serialize a color value |
149 | | template <> |
150 | 0 | inline size_t Write<aiColor4D>(IOStream *stream, const aiColor4D &v) { |
151 | 0 | size_t t = Write<ai_real>(stream, v.r); |
152 | 0 | t += Write<float>(stream, v.g); |
153 | 0 | t += Write<float>(stream, v.b); |
154 | 0 | t += Write<float>(stream, v.a); |
155 | |
|
156 | 0 | return t; |
157 | 0 | } |
158 | | |
159 | | // ----------------------------------------------------------------------------------- |
160 | | // Serialize a quaternion |
161 | | template <> |
162 | 0 | inline size_t Write<aiQuaternion>(IOStream *stream, const aiQuaternion &v) { |
163 | 0 | size_t t = Write<ai_real>(stream, v.w); |
164 | 0 | t += Write<float>(stream, v.x); |
165 | 0 | t += Write<float>(stream, v.y); |
166 | 0 | t += Write<float>(stream, v.z); |
167 | 0 | ai_assert(t == 16); |
168 | |
|
169 | 0 | return t; |
170 | 0 | } |
171 | | |
172 | | // ----------------------------------------------------------------------------------- |
173 | | // Serialize a vertex weight |
174 | | template <> |
175 | 0 | inline size_t Write<aiVertexWeight>(IOStream *stream, const aiVertexWeight &v) { |
176 | 0 | size_t t = Write<unsigned int>(stream, v.mVertexId); |
177 | |
|
178 | 0 | return t + Write<float>(stream, v.mWeight); |
179 | 0 | } |
180 | | |
181 | | constexpr size_t MatrixSize = 64; |
182 | | |
183 | | // ----------------------------------------------------------------------------------- |
184 | | // Serialize a mat4x4 |
185 | | template <> |
186 | 0 | inline size_t Write<aiMatrix4x4>(IOStream *stream, const aiMatrix4x4 &m) { |
187 | 0 | for (unsigned int i = 0; i < 4; ++i) { |
188 | 0 | for (unsigned int i2 = 0; i2 < 4; ++i2) { |
189 | 0 | Write<ai_real>(stream, m[i][i2]); |
190 | 0 | } |
191 | 0 | } |
192 | |
|
193 | 0 | return MatrixSize; |
194 | 0 | } |
195 | | |
196 | | // ----------------------------------------------------------------------------------- |
197 | | // Serialize an aiVectorKey |
198 | | template <> |
199 | 0 | inline size_t Write<aiVectorKey>(IOStream *stream, const aiVectorKey &v) { |
200 | 0 | const size_t t = Write<double>(stream, v.mTime); |
201 | 0 | return t + Write<aiVector3D>(stream, v.mValue); |
202 | 0 | } |
203 | | |
204 | | // ----------------------------------------------------------------------------------- |
205 | | // Serialize an aiQuatKey |
206 | | template <> |
207 | 0 | inline size_t Write<aiQuatKey>(IOStream *stream, const aiQuatKey &v) { |
208 | 0 | const size_t t = Write<double>(stream, v.mTime); |
209 | 0 | return t + Write<aiQuaternion>(stream, v.mValue); |
210 | 0 | } |
211 | | |
212 | | template <typename T> |
213 | 0 | inline size_t WriteBounds(IOStream *stream, const T *in, unsigned int size) { |
214 | 0 | T minc, maxc; |
215 | 0 | ArrayBounds(in, size, minc, maxc); |
216 | |
|
217 | 0 | const size_t t = Write<T>(stream, minc); |
218 | 0 | return t + Write<T>(stream, maxc); |
219 | 0 | } Unexecuted instantiation: unsigned long Assimp::WriteBounds<aiVector3t<float> >(Assimp::IOStream*, aiVector3t<float> const*, unsigned int) Unexecuted instantiation: unsigned long Assimp::WriteBounds<aiColor4t<float> >(Assimp::IOStream*, aiColor4t<float> const*, unsigned int) Unexecuted instantiation: unsigned long Assimp::WriteBounds<aiVertexWeight>(Assimp::IOStream*, aiVertexWeight const*, unsigned int) Unexecuted instantiation: unsigned long Assimp::WriteBounds<aiVectorKey>(Assimp::IOStream*, aiVectorKey const*, unsigned int) Unexecuted instantiation: unsigned long Assimp::WriteBounds<aiQuatKey>(Assimp::IOStream*, aiQuatKey const*, unsigned int) |
220 | | |
221 | | // We use this to write out non-byte arrays so that we write using the specializations. |
222 | | // This way we avoid writing out extra bytes that potentially come from struct alignment. |
223 | | template <typename T> |
224 | 0 | inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) { |
225 | 0 | size_t n = 0; |
226 | 0 | for (unsigned int i = 0; i < size; i++) |
227 | 0 | n += Write<T>(stream, in[i]); |
228 | |
|
229 | 0 | return n; |
230 | 0 | } Unexecuted instantiation: unsigned long Assimp::WriteArray<aiVector3t<float> >(Assimp::IOStream*, aiVector3t<float> const*, unsigned int) Unexecuted instantiation: unsigned long Assimp::WriteArray<aiColor4t<float> >(Assimp::IOStream*, aiColor4t<float> const*, unsigned int) Unexecuted instantiation: unsigned long Assimp::WriteArray<aiVertexWeight>(Assimp::IOStream*, aiVertexWeight const*, unsigned int) Unexecuted instantiation: unsigned long Assimp::WriteArray<aiVectorKey>(Assimp::IOStream*, aiVectorKey const*, unsigned int) Unexecuted instantiation: unsigned long Assimp::WriteArray<aiQuatKey>(Assimp::IOStream*, aiQuatKey const*, unsigned int) |
231 | | |
232 | | // ---------------------------------------------------------------------------------- |
233 | | /** @class AssbinChunkWriter |
234 | | * @brief Chunk writer mechanism for the .assbin file structure |
235 | | * |
236 | | * This is a standard in-memory IOStream (most of the code is based on BlobIOStream), |
237 | | * the difference being that this takes another IOStream as a "container" in the |
238 | | * constructor, and when it is destroyed, it appends the magic number, the chunk size, |
239 | | * and the chunk contents to the container stream. This allows relatively easy chunk |
240 | | * chunk construction, even recursively. |
241 | | */ |
242 | | class AssbinChunkWriter : public IOStream { |
243 | | private: |
244 | | uint8_t *buffer; |
245 | | uint32_t magic; |
246 | | IOStream *container; |
247 | | size_t cur_size, cursor, initial; |
248 | | |
249 | | private: |
250 | | // ------------------------------------------------------------------- |
251 | 0 | void Grow(size_t need = 0) { |
252 | 0 | size_t new_size = std::max(initial, std::max(need, cur_size + (cur_size >> 1))); |
253 | |
|
254 | 0 | const uint8_t *const old = buffer; |
255 | 0 | buffer = new uint8_t[new_size]; |
256 | |
|
257 | 0 | if (old) { |
258 | 0 | memcpy(buffer, old, cur_size); |
259 | 0 | delete[] old; |
260 | 0 | } |
261 | |
|
262 | 0 | cur_size = new_size; |
263 | 0 | } |
264 | | |
265 | | public: |
266 | | AssbinChunkWriter(IOStream *container, uint32_t magic, size_t initial = 4096) : |
267 | 0 | buffer(nullptr), |
268 | 0 | magic(magic), |
269 | 0 | container(container), |
270 | 0 | cur_size(0), |
271 | 0 | cursor(0), |
272 | 0 | initial(initial) { |
273 | | // empty |
274 | 0 | } |
275 | | |
276 | 0 | ~AssbinChunkWriter() override { |
277 | 0 | if (container) { |
278 | 0 | container->Write(&magic, sizeof(uint32_t), 1); |
279 | 0 | container->Write(&cursor, sizeof(uint32_t), 1); |
280 | 0 | container->Write(buffer, 1, cursor); |
281 | 0 | } |
282 | 0 | if (buffer) delete[] buffer; |
283 | 0 | } |
284 | | |
285 | 0 | void *GetBufferPointer() { return buffer; } |
286 | | |
287 | 0 | size_t Read(void * /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) override { |
288 | 0 | return 0; |
289 | 0 | } |
290 | | |
291 | 0 | aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) override { |
292 | 0 | return aiReturn_FAILURE; |
293 | 0 | } |
294 | | |
295 | 0 | size_t Tell() const override { |
296 | 0 | return cursor; |
297 | 0 | } |
298 | | |
299 | 0 | void Flush() override { |
300 | | // not implemented |
301 | 0 | } |
302 | | |
303 | 0 | size_t FileSize() const override { |
304 | 0 | return cursor; |
305 | 0 | } |
306 | | |
307 | 0 | size_t Write(const void *pvBuffer, size_t pSize, size_t pCount) override { |
308 | 0 | pSize *= pCount; |
309 | 0 | if (cursor + pSize > cur_size) { |
310 | 0 | Grow(cursor + pSize); |
311 | 0 | } |
312 | |
|
313 | 0 | memcpy(buffer + cursor, pvBuffer, pSize); |
314 | 0 | cursor += pSize; |
315 | |
|
316 | 0 | return pCount; |
317 | 0 | } |
318 | | }; |
319 | | |
320 | | // ---------------------------------------------------------------------------------- |
321 | | /** @class AssbinFileWriter |
322 | | * @brief Assbin file writer class |
323 | | * |
324 | | * This class writes an .assbin file, and is responsible for the file layout. |
325 | | */ |
326 | | class AssbinFileWriter { |
327 | | private: |
328 | | bool shortened; |
329 | | bool compressed; |
330 | | |
331 | | protected: |
332 | | // ----------------------------------------------------------------------------------- |
333 | 0 | void WriteBinaryNode(IOStream *container, const aiNode *node) { |
334 | 0 | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AINODE); |
335 | |
|
336 | 0 | unsigned int nb_metadata = (node->mMetaData != nullptr ? node->mMetaData->mNumProperties : 0); |
337 | |
|
338 | 0 | Write<aiString>(&chunk, node->mName); |
339 | 0 | Write<aiMatrix4x4>(&chunk, node->mTransformation); |
340 | 0 | Write<unsigned int>(&chunk, node->mNumChildren); |
341 | 0 | Write<unsigned int>(&chunk, node->mNumMeshes); |
342 | 0 | Write<unsigned int>(&chunk, nb_metadata); |
343 | |
|
344 | 0 | for (unsigned int i = 0; i < node->mNumMeshes; ++i) { |
345 | 0 | Write<unsigned int>(&chunk, node->mMeshes[i]); |
346 | 0 | } |
347 | |
|
348 | 0 | for (unsigned int i = 0; i < node->mNumChildren; ++i) { |
349 | 0 | WriteBinaryNode(&chunk, node->mChildren[i]); |
350 | 0 | } |
351 | |
|
352 | 0 | for (unsigned int i = 0; i < nb_metadata; ++i) { |
353 | 0 | const aiString &key = node->mMetaData->mKeys[i]; |
354 | 0 | aiMetadataType type = node->mMetaData->mValues[i].mType; |
355 | 0 | void *value = node->mMetaData->mValues[i].mData; |
356 | |
|
357 | 0 | Write<aiString>(&chunk, key); |
358 | 0 | Write<uint16_t>(&chunk, (uint16_t)type); |
359 | |
|
360 | 0 | switch (type) { |
361 | 0 | case AI_BOOL: |
362 | 0 | Write<bool>(&chunk, *((bool *)value)); |
363 | 0 | break; |
364 | 0 | case AI_INT32: |
365 | 0 | Write<int32_t>(&chunk, *((int32_t *)value)); |
366 | 0 | break; |
367 | 0 | case AI_UINT64: |
368 | 0 | Write<uint64_t>(&chunk, *((uint64_t *)value)); |
369 | 0 | break; |
370 | 0 | case AI_FLOAT: |
371 | 0 | Write<float>(&chunk, *((float *)value)); |
372 | 0 | break; |
373 | 0 | case AI_DOUBLE: |
374 | 0 | Write<double>(&chunk, *((double *)value)); |
375 | 0 | break; |
376 | 0 | case AI_AISTRING: |
377 | 0 | Write<aiString>(&chunk, *((aiString *)value)); |
378 | 0 | break; |
379 | 0 | case AI_AIVECTOR3D: |
380 | 0 | Write<aiVector3D>(&chunk, *((aiVector3D *)value)); |
381 | 0 | break; |
382 | | #ifdef SWIG |
383 | | case FORCE_32BIT: |
384 | | #endif // SWIG |
385 | 0 | default: |
386 | 0 | break; |
387 | 0 | } |
388 | 0 | } |
389 | 0 | } |
390 | | |
391 | | // ----------------------------------------------------------------------------------- |
392 | 0 | void WriteBinaryTexture(IOStream *container, const aiTexture *tex) { |
393 | 0 | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AITEXTURE); |
394 | |
|
395 | 0 | Write<unsigned int>(&chunk, tex->mWidth); |
396 | 0 | Write<unsigned int>(&chunk, tex->mHeight); |
397 | | // Write the texture format, but don't include the null terminator. |
398 | 0 | chunk.Write(tex->achFormatHint, sizeof(char), HINTMAXTEXTURELEN - 1); |
399 | |
|
400 | 0 | if (!shortened) { |
401 | 0 | if (!tex->mHeight) { |
402 | 0 | chunk.Write(tex->pcData, 1, tex->mWidth); |
403 | 0 | } else { |
404 | 0 | chunk.Write(tex->pcData, 1, tex->mWidth * tex->mHeight * 4); |
405 | 0 | } |
406 | 0 | } |
407 | 0 | } |
408 | | |
409 | | // ----------------------------------------------------------------------------------- |
410 | 0 | void WriteBinaryBone(IOStream *container, const aiBone *b) { |
411 | 0 | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIBONE); |
412 | |
|
413 | 0 | Write<aiString>(&chunk, b->mName); |
414 | 0 | Write<unsigned int>(&chunk, b->mNumWeights); |
415 | 0 | Write<aiMatrix4x4>(&chunk, b->mOffsetMatrix); |
416 | | |
417 | | // for the moment we write dumb min/max values for the bones, too. |
418 | | // maybe I'll add a better, hash-like solution later |
419 | 0 | if (shortened) { |
420 | 0 | WriteBounds(&chunk, b->mWeights, b->mNumWeights); |
421 | 0 | } // else write as usual |
422 | 0 | else |
423 | 0 | WriteArray<aiVertexWeight>(&chunk, b->mWeights, b->mNumWeights); |
424 | 0 | } |
425 | | |
426 | | // ----------------------------------------------------------------------------------- |
427 | 0 | void WriteBinaryMesh(IOStream *container, const aiMesh *mesh) { |
428 | 0 | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMESH); |
429 | |
|
430 | 0 | Write<unsigned int>(&chunk, mesh->mPrimitiveTypes); |
431 | 0 | Write<unsigned int>(&chunk, mesh->mNumVertices); |
432 | 0 | Write<unsigned int>(&chunk, mesh->mNumFaces); |
433 | 0 | Write<unsigned int>(&chunk, mesh->mNumBones); |
434 | 0 | Write<unsigned int>(&chunk, mesh->mMaterialIndex); |
435 | | |
436 | | // first of all, write bits for all existent vertex components |
437 | 0 | unsigned int c = 0; |
438 | 0 | if (mesh->mVertices) { |
439 | 0 | c |= ASSBIN_MESH_HAS_POSITIONS; |
440 | 0 | } |
441 | 0 | if (mesh->mNormals) { |
442 | 0 | c |= ASSBIN_MESH_HAS_NORMALS; |
443 | 0 | } |
444 | 0 | if (mesh->mTangents && mesh->mBitangents) { |
445 | 0 | c |= ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS; |
446 | 0 | } |
447 | 0 | for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++n) { |
448 | 0 | if (!mesh->mTextureCoords[n]) { |
449 | 0 | break; |
450 | 0 | } |
451 | 0 | c |= ASSBIN_MESH_HAS_TEXCOORD(n); |
452 | 0 | } |
453 | 0 | for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++n) { |
454 | 0 | if (!mesh->mColors[n]) { |
455 | 0 | break; |
456 | 0 | } |
457 | 0 | c |= ASSBIN_MESH_HAS_COLOR(n); |
458 | 0 | } |
459 | 0 | Write<unsigned int>(&chunk, c); |
460 | |
|
461 | 0 | aiVector3D minVec, maxVec; |
462 | 0 | if (mesh->mVertices) { |
463 | 0 | if (shortened) { |
464 | 0 | WriteBounds(&chunk, mesh->mVertices, mesh->mNumVertices); |
465 | 0 | } // else write as usual |
466 | 0 | else |
467 | 0 | WriteArray<aiVector3D>(&chunk, mesh->mVertices, mesh->mNumVertices); |
468 | 0 | } |
469 | 0 | if (mesh->mNormals) { |
470 | 0 | if (shortened) { |
471 | 0 | WriteBounds(&chunk, mesh->mNormals, mesh->mNumVertices); |
472 | 0 | } // else write as usual |
473 | 0 | else |
474 | 0 | WriteArray<aiVector3D>(&chunk, mesh->mNormals, mesh->mNumVertices); |
475 | 0 | } |
476 | 0 | if (mesh->mTangents && mesh->mBitangents) { |
477 | 0 | if (shortened) { |
478 | 0 | WriteBounds(&chunk, mesh->mTangents, mesh->mNumVertices); |
479 | 0 | WriteBounds(&chunk, mesh->mBitangents, mesh->mNumVertices); |
480 | 0 | } // else write as usual |
481 | 0 | else { |
482 | 0 | WriteArray<aiVector3D>(&chunk, mesh->mTangents, mesh->mNumVertices); |
483 | 0 | WriteArray<aiVector3D>(&chunk, mesh->mBitangents, mesh->mNumVertices); |
484 | 0 | } |
485 | 0 | } |
486 | 0 | for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++n) { |
487 | 0 | if (!mesh->mColors[n]) |
488 | 0 | break; |
489 | | |
490 | 0 | if (shortened) { |
491 | 0 | WriteBounds(&chunk, mesh->mColors[n], mesh->mNumVertices); |
492 | 0 | } // else write as usual |
493 | 0 | else |
494 | 0 | WriteArray<aiColor4D>(&chunk, mesh->mColors[n], mesh->mNumVertices); |
495 | 0 | } |
496 | 0 | for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++n) { |
497 | 0 | if (!mesh->mTextureCoords[n]) |
498 | 0 | break; |
499 | | |
500 | | // write number of UV components |
501 | 0 | Write<unsigned int>(&chunk, mesh->mNumUVComponents[n]); |
502 | |
|
503 | 0 | if (shortened) { |
504 | 0 | WriteBounds(&chunk, mesh->mTextureCoords[n], mesh->mNumVertices); |
505 | 0 | } // else write as usual |
506 | 0 | else |
507 | 0 | WriteArray<aiVector3D>(&chunk, mesh->mTextureCoords[n], mesh->mNumVertices); |
508 | 0 | } |
509 | | |
510 | | // write faces. There are no floating-point calculations involved |
511 | | // in these, so we can write a simple hash over the face data |
512 | | // to the dump file. We generate a single 32 Bit hash for 512 faces |
513 | | // using Assimp's standard hashing function. |
514 | 0 | if (shortened) { |
515 | 0 | unsigned int processed = 0; |
516 | 0 | for (unsigned int job; (job = std::min(mesh->mNumFaces - processed, 512u)); processed += job) { |
517 | 0 | uint32_t hash = 0; |
518 | 0 | for (unsigned int a = 0; a < job; ++a) { |
519 | |
|
520 | 0 | const aiFace &f = mesh->mFaces[processed + a]; |
521 | 0 | uint32_t tmp = f.mNumIndices; |
522 | 0 | hash = SuperFastHash(reinterpret_cast<const char *>(&tmp), sizeof tmp, hash); |
523 | 0 | for (unsigned int i = 0; i < f.mNumIndices; ++i) { |
524 | 0 | static_assert(AI_MAX_VERTICES <= 0xffffffff, "AI_MAX_VERTICES <= 0xffffffff"); |
525 | 0 | tmp = static_cast<uint32_t>(f.mIndices[i]); |
526 | 0 | hash = SuperFastHash(reinterpret_cast<const char *>(&tmp), sizeof tmp, hash); |
527 | 0 | } |
528 | 0 | } |
529 | 0 | Write<unsigned int>(&chunk, hash); |
530 | 0 | } |
531 | 0 | } else // else write as usual |
532 | 0 | { |
533 | | // if there are less than 2^16 vertices, we can simply use 16 bit integers ... |
534 | 0 | for (unsigned int i = 0; i < mesh->mNumFaces; ++i) { |
535 | 0 | const aiFace &f = mesh->mFaces[i]; |
536 | |
|
537 | 0 | static_assert(AI_MAX_FACE_INDICES <= 0xffff, "AI_MAX_FACE_INDICES <= 0xffff"); |
538 | 0 | Write<uint16_t>(&chunk, static_cast<uint16_t>(f.mNumIndices)); |
539 | |
|
540 | 0 | for (unsigned int a = 0; a < f.mNumIndices; ++a) { |
541 | 0 | if (mesh->mNumVertices < (1u << 16)) { |
542 | 0 | Write<uint16_t>(&chunk, static_cast<uint16_t>(f.mIndices[a])); |
543 | 0 | } else { |
544 | 0 | Write<unsigned int>(&chunk, f.mIndices[a]); |
545 | 0 | } |
546 | 0 | } |
547 | 0 | } |
548 | 0 | } |
549 | | |
550 | | // write bones |
551 | 0 | if (mesh->mNumBones) { |
552 | 0 | for (unsigned int a = 0; a < mesh->mNumBones; ++a) { |
553 | 0 | const aiBone *b = mesh->mBones[a]; |
554 | 0 | WriteBinaryBone(&chunk, b); |
555 | 0 | } |
556 | 0 | } |
557 | 0 | } |
558 | | |
559 | | // ----------------------------------------------------------------------------------- |
560 | 0 | void WriteBinaryMaterialProperty(IOStream *container, const aiMaterialProperty *prop) { |
561 | 0 | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMATERIALPROPERTY); |
562 | |
|
563 | 0 | Write<aiString>(&chunk, prop->mKey); |
564 | 0 | Write<unsigned int>(&chunk, prop->mSemantic); |
565 | 0 | Write<unsigned int>(&chunk, prop->mIndex); |
566 | |
|
567 | 0 | Write<unsigned int>(&chunk, prop->mDataLength); |
568 | 0 | Write<unsigned int>(&chunk, (unsigned int)prop->mType); |
569 | 0 | chunk.Write(prop->mData, 1, prop->mDataLength); |
570 | 0 | } |
571 | | |
572 | | // ----------------------------------------------------------------------------------- |
573 | 0 | void WriteBinaryMaterial(IOStream *container, const aiMaterial *mat) { |
574 | 0 | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMATERIAL); |
575 | |
|
576 | 0 | Write<unsigned int>(&chunk, mat->mNumProperties); |
577 | 0 | for (unsigned int i = 0; i < mat->mNumProperties; ++i) { |
578 | 0 | WriteBinaryMaterialProperty(&chunk, mat->mProperties[i]); |
579 | 0 | } |
580 | 0 | } |
581 | | |
582 | | // ----------------------------------------------------------------------------------- |
583 | 0 | void WriteBinaryNodeAnim(IOStream *container, const aiNodeAnim *nd) { |
584 | 0 | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AINODEANIM); |
585 | |
|
586 | 0 | Write<aiString>(&chunk, nd->mNodeName); |
587 | 0 | Write<unsigned int>(&chunk, nd->mNumPositionKeys); |
588 | 0 | Write<unsigned int>(&chunk, nd->mNumRotationKeys); |
589 | 0 | Write<unsigned int>(&chunk, nd->mNumScalingKeys); |
590 | 0 | Write<unsigned int>(&chunk, nd->mPreState); |
591 | 0 | Write<unsigned int>(&chunk, nd->mPostState); |
592 | |
|
593 | 0 | if (nd->mPositionKeys) { |
594 | 0 | if (shortened) { |
595 | 0 | WriteBounds(&chunk, nd->mPositionKeys, nd->mNumPositionKeys); |
596 | |
|
597 | 0 | } // else write as usual |
598 | 0 | else |
599 | 0 | WriteArray<aiVectorKey>(&chunk, nd->mPositionKeys, nd->mNumPositionKeys); |
600 | 0 | } |
601 | 0 | if (nd->mRotationKeys) { |
602 | 0 | if (shortened) { |
603 | 0 | WriteBounds(&chunk, nd->mRotationKeys, nd->mNumRotationKeys); |
604 | |
|
605 | 0 | } // else write as usual |
606 | 0 | else |
607 | 0 | WriteArray<aiQuatKey>(&chunk, nd->mRotationKeys, nd->mNumRotationKeys); |
608 | 0 | } |
609 | 0 | if (nd->mScalingKeys) { |
610 | 0 | if (shortened) { |
611 | 0 | WriteBounds(&chunk, nd->mScalingKeys, nd->mNumScalingKeys); |
612 | |
|
613 | 0 | } // else write as usual |
614 | 0 | else |
615 | 0 | WriteArray<aiVectorKey>(&chunk, nd->mScalingKeys, nd->mNumScalingKeys); |
616 | 0 | } |
617 | 0 | } |
618 | | |
619 | | // ----------------------------------------------------------------------------------- |
620 | 0 | void WriteBinaryAnim(IOStream *container, const aiAnimation *anim) { |
621 | 0 | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIANIMATION); |
622 | |
|
623 | 0 | Write<aiString>(&chunk, anim->mName); |
624 | 0 | Write<double>(&chunk, anim->mDuration); |
625 | 0 | Write<double>(&chunk, anim->mTicksPerSecond); |
626 | 0 | Write<unsigned int>(&chunk, anim->mNumChannels); |
627 | |
|
628 | 0 | for (unsigned int a = 0; a < anim->mNumChannels; ++a) { |
629 | 0 | const aiNodeAnim *nd = anim->mChannels[a]; |
630 | 0 | WriteBinaryNodeAnim(&chunk, nd); |
631 | 0 | } |
632 | 0 | } |
633 | | |
634 | | // ----------------------------------------------------------------------------------- |
635 | 0 | void WriteBinaryLight(IOStream *container, const aiLight *l) { |
636 | 0 | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AILIGHT); |
637 | |
|
638 | 0 | Write<aiString>(&chunk, l->mName); |
639 | 0 | Write<unsigned int>(&chunk, l->mType); |
640 | |
|
641 | 0 | Write<aiVector3D>(&chunk, l->mPosition); |
642 | 0 | Write<aiVector3D>(&chunk, l->mDirection); |
643 | 0 | Write<aiVector3D>(&chunk, l->mUp); |
644 | |
|
645 | 0 | if (l->mType != aiLightSource_DIRECTIONAL) { |
646 | 0 | Write<float>(&chunk, l->mAttenuationConstant); |
647 | 0 | Write<float>(&chunk, l->mAttenuationLinear); |
648 | 0 | Write<float>(&chunk, l->mAttenuationQuadratic); |
649 | 0 | } |
650 | |
|
651 | 0 | Write<aiColor3D>(&chunk, l->mColorDiffuse); |
652 | 0 | Write<aiColor3D>(&chunk, l->mColorSpecular); |
653 | 0 | Write<aiColor3D>(&chunk, l->mColorAmbient); |
654 | |
|
655 | 0 | if (l->mType == aiLightSource_SPOT) { |
656 | 0 | Write<float>(&chunk, l->mAngleInnerCone); |
657 | 0 | Write<float>(&chunk, l->mAngleOuterCone); |
658 | 0 | } |
659 | 0 | } |
660 | | |
661 | | // ----------------------------------------------------------------------------------- |
662 | 0 | void WriteBinaryCamera(IOStream *container, const aiCamera *cam) { |
663 | 0 | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AICAMERA); |
664 | |
|
665 | 0 | Write<aiString>(&chunk, cam->mName); |
666 | 0 | Write<aiVector3D>(&chunk, cam->mPosition); |
667 | 0 | Write<aiVector3D>(&chunk, cam->mLookAt); |
668 | 0 | Write<aiVector3D>(&chunk, cam->mUp); |
669 | 0 | Write<float>(&chunk, cam->mHorizontalFOV); |
670 | 0 | Write<float>(&chunk, cam->mClipPlaneNear); |
671 | 0 | Write<float>(&chunk, cam->mClipPlaneFar); |
672 | 0 | Write<float>(&chunk, cam->mAspect); |
673 | 0 | } |
674 | | |
675 | | // ----------------------------------------------------------------------------------- |
676 | 0 | void WriteBinaryScene(IOStream *container, const aiScene *scene) { |
677 | 0 | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AISCENE); |
678 | | |
679 | | // basic scene information |
680 | 0 | Write<unsigned int>(&chunk, scene->mFlags); |
681 | 0 | Write<unsigned int>(&chunk, scene->mNumMeshes); |
682 | 0 | Write<unsigned int>(&chunk, scene->mNumMaterials); |
683 | 0 | Write<unsigned int>(&chunk, scene->mNumAnimations); |
684 | 0 | Write<unsigned int>(&chunk, scene->mNumTextures); |
685 | 0 | Write<unsigned int>(&chunk, scene->mNumLights); |
686 | 0 | Write<unsigned int>(&chunk, scene->mNumCameras); |
687 | | |
688 | | // write node graph |
689 | 0 | WriteBinaryNode(&chunk, scene->mRootNode); |
690 | | |
691 | | // write all meshes |
692 | 0 | for (unsigned int i = 0; i < scene->mNumMeshes; ++i) { |
693 | 0 | const aiMesh *mesh = scene->mMeshes[i]; |
694 | 0 | WriteBinaryMesh(&chunk, mesh); |
695 | 0 | } |
696 | | |
697 | | // write materials |
698 | 0 | for (unsigned int i = 0; i < scene->mNumMaterials; ++i) { |
699 | 0 | const aiMaterial *mat = scene->mMaterials[i]; |
700 | 0 | WriteBinaryMaterial(&chunk, mat); |
701 | 0 | } |
702 | | |
703 | | // write all animations |
704 | 0 | for (unsigned int i = 0; i < scene->mNumAnimations; ++i) { |
705 | 0 | const aiAnimation *anim = scene->mAnimations[i]; |
706 | 0 | WriteBinaryAnim(&chunk, anim); |
707 | 0 | } |
708 | | |
709 | | // write all textures |
710 | 0 | for (unsigned int i = 0; i < scene->mNumTextures; ++i) { |
711 | 0 | const aiTexture *mesh = scene->mTextures[i]; |
712 | 0 | WriteBinaryTexture(&chunk, mesh); |
713 | 0 | } |
714 | | |
715 | | // write lights |
716 | 0 | for (unsigned int i = 0; i < scene->mNumLights; ++i) { |
717 | 0 | const aiLight *l = scene->mLights[i]; |
718 | 0 | WriteBinaryLight(&chunk, l); |
719 | 0 | } |
720 | | |
721 | | // write cameras |
722 | 0 | for (unsigned int i = 0; i < scene->mNumCameras; ++i) { |
723 | 0 | const aiCamera *cam = scene->mCameras[i]; |
724 | 0 | WriteBinaryCamera(&chunk, cam); |
725 | 0 | } |
726 | 0 | } |
727 | | |
728 | | public: |
729 | | AssbinFileWriter(bool shortened, bool compressed) : |
730 | 0 | shortened(shortened), compressed(compressed) { |
731 | 0 | } |
732 | | |
733 | | // ----------------------------------------------------------------------------------- |
734 | | // Write a binary model dump |
735 | 0 | void WriteBinaryDump(const char *pFile, const char *cmd, IOSystem *pIOSystem, const aiScene *pScene) { |
736 | 0 | IOStream *out = pIOSystem->Open(pFile, "wb"); |
737 | 0 | if (!out) |
738 | 0 | throw std::runtime_error("Unable to open output file " + std::string(pFile) + '\n'); |
739 | | |
740 | 0 | auto CloseIOStream = [&]() { |
741 | 0 | if (out) { |
742 | 0 | pIOSystem->Close(out); |
743 | 0 | out = nullptr; // Ensure this is only done once. |
744 | 0 | } |
745 | 0 | }; |
746 | |
|
747 | 0 | try { |
748 | 0 | time_t tt = time(nullptr); |
749 | | #if _WIN32 |
750 | | tm *p = gmtime(&tt); |
751 | | #else |
752 | 0 | struct tm now; |
753 | 0 | tm *p = gmtime_r(&tt, &now); |
754 | 0 | #endif |
755 | | |
756 | | // header |
757 | 0 | char s[64]; |
758 | 0 | memset(s, 0, 64); |
759 | | #if _MSC_VER >= 1400 |
760 | | sprintf_s(s, "ASSIMP.binary-dump.%s", asctime(p)); |
761 | | #else |
762 | 0 | ai_snprintf(s, 64, "ASSIMP.binary-dump.%s", asctime(p)); |
763 | 0 | #endif |
764 | 0 | out->Write(s, 44, 1); |
765 | | // == 44 bytes |
766 | |
|
767 | 0 | Write<unsigned int>(out, ASSBIN_VERSION_MAJOR); |
768 | 0 | Write<unsigned int>(out, ASSBIN_VERSION_MINOR); |
769 | 0 | Write<unsigned int>(out, aiGetVersionRevision()); |
770 | 0 | Write<unsigned int>(out, aiGetCompileFlags()); |
771 | 0 | Write<uint16_t>(out, shortened); |
772 | 0 | Write<uint16_t>(out, compressed); |
773 | | // == 20 bytes |
774 | |
|
775 | 0 | char buff[256] = { 0 }; |
776 | 0 | ai_snprintf(buff, 256, "%s", pFile); |
777 | 0 | out->Write(buff, sizeof(char), 256); |
778 | |
|
779 | 0 | memset(buff, 0, sizeof(buff)); |
780 | 0 | ai_snprintf(buff, 128, "%s", cmd); |
781 | 0 | out->Write(buff, sizeof(char), 128); |
782 | | |
783 | | // leave 64 bytes free for future extensions |
784 | 0 | memset(buff, 0xcd, 64); |
785 | 0 | out->Write(buff, sizeof(char), 64); |
786 | | // == 435 bytes |
787 | | |
788 | | // ==== total header size: 512 bytes |
789 | 0 | ai_assert(out->Tell() == ASSBIN_HEADER_LENGTH); |
790 | | |
791 | | // Up to here the data is uncompressed. For compressed files, the rest |
792 | | // is compressed using standard DEFLATE from zlib. |
793 | 0 | if (compressed) { |
794 | 0 | AssbinChunkWriter uncompressedStream(nullptr, 0); |
795 | 0 | WriteBinaryScene(&uncompressedStream, pScene); |
796 | |
|
797 | 0 | uLongf uncompressedSize = static_cast<uLongf>(uncompressedStream.Tell()); |
798 | 0 | uLongf compressedSize = (uLongf)compressBound(uncompressedSize); |
799 | 0 | uint8_t *compressedBuffer = new uint8_t[compressedSize]; |
800 | |
|
801 | 0 | int res = compress2(compressedBuffer, &compressedSize, (const Bytef *)uncompressedStream.GetBufferPointer(), uncompressedSize, 9); |
802 | 0 | if (res != Z_OK) { |
803 | 0 | delete[] compressedBuffer; |
804 | 0 | throw DeadlyExportError("Compression failed."); |
805 | 0 | } |
806 | | |
807 | 0 | out->Write(&uncompressedSize, sizeof(uint32_t), 1); |
808 | 0 | out->Write(compressedBuffer, sizeof(char), compressedSize); |
809 | |
|
810 | 0 | delete[] compressedBuffer; |
811 | 0 | } else { |
812 | 0 | WriteBinaryScene(out, pScene); |
813 | 0 | } |
814 | | |
815 | 0 | CloseIOStream(); |
816 | 0 | } catch (...) { |
817 | 0 | CloseIOStream(); |
818 | 0 | throw; |
819 | 0 | } |
820 | 0 | } |
821 | | }; |
822 | | |
823 | | void DumpSceneToAssbin( |
824 | | const char *pFile, const char *cmd, IOSystem *pIOSystem, |
825 | 0 | const aiScene *pScene, bool shortened, bool compressed) { |
826 | 0 | AssbinFileWriter fileWriter(shortened, compressed); |
827 | 0 | fileWriter.WriteBinaryDump(pFile, cmd, pIOSystem, pScene); |
828 | 0 | } |
829 | | #if _MSC_VER |
830 | | #pragma warning(pop) |
831 | | #endif // _MSC_VER |
832 | | |
833 | | } // end of namespace Assimp |