/src/assimp/code/AssetLib/Assbin/AssbinFileWriter.cpp
Line | Count | Source |
1 | | /* |
2 | | Open Asset Import Library (assimp) |
3 | | ---------------------------------------------------------------------- |
4 | | |
5 | | Copyright (c) 2006-2026, 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 | 25.8k | size_t Write(IOStream *stream, const T &v) { |
66 | 25.8k | return stream->Write(&v, sizeof(T), 1); |
67 | 25.8k | } unsigned long Assimp::Write<bool>(Assimp::IOStream*, bool const&) Line | Count | Source | 65 | 2.38k | size_t Write(IOStream *stream, const T &v) { | 66 | 2.38k | return stream->Write(&v, sizeof(T), 1); | 67 | 2.38k | } |
unsigned long Assimp::Write<int>(Assimp::IOStream*, int const&) Line | Count | Source | 65 | 22.8k | size_t Write(IOStream *stream, const T &v) { | 66 | 22.8k | return stream->Write(&v, sizeof(T), 1); | 67 | 22.8k | } |
unsigned long Assimp::Write<unsigned long>(Assimp::IOStream*, unsigned long const&) Line | Count | Source | 65 | 589 | size_t Write(IOStream *stream, const T &v) { | 66 | 589 | return stream->Write(&v, sizeof(T), 1); | 67 | 589 | } |
|
68 | | |
69 | | // ----------------------------------------------------------------------------------- |
70 | | // Serialize an aiString |
71 | | template <> |
72 | 231k | inline size_t Write<aiString>(IOStream *stream, const aiString &s) { |
73 | 231k | const size_t s2 = (uint32_t)s.length; |
74 | 231k | stream->Write(&s, 4, 1); |
75 | 231k | stream->Write(s.data, s2, 1); |
76 | | |
77 | 231k | return s2 + 4; |
78 | 231k | } |
79 | | |
80 | | // ----------------------------------------------------------------------------------- |
81 | | // Serialize an unsigned int as uint32_t |
82 | | template <> |
83 | 948k | inline size_t Write<unsigned int>(IOStream *stream, const unsigned int &w) { |
84 | 948k | const uint32_t t = (uint32_t)w; |
85 | 948k | 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 | 948k | stream->Write(&t, 4, 1); |
91 | | |
92 | 948k | return 4; |
93 | 948k | } |
94 | | |
95 | | // ----------------------------------------------------------------------------------- |
96 | | // Serialize an unsigned int as uint16_t |
97 | | template <> |
98 | 2.54M | inline size_t Write<uint16_t>(IOStream *stream, const uint16_t &w) { |
99 | 2.54M | static_assert(sizeof(uint16_t) == 2, "sizeof(uint16_t)==2"); |
100 | 2.54M | stream->Write(&w, 2, 1); |
101 | | |
102 | 2.54M | return 2; |
103 | 2.54M | } |
104 | | |
105 | | // ----------------------------------------------------------------------------------- |
106 | | // Serialize a float |
107 | | template <> |
108 | 7.38M | inline size_t Write<float>(IOStream *stream, const float &f) { |
109 | 7.38M | static_assert(sizeof(float) == 4, "sizeof(float)==4"); |
110 | 7.38M | stream->Write(&f, 4, 1); |
111 | | |
112 | 7.38M | return 4; |
113 | 7.38M | } |
114 | | |
115 | | // ----------------------------------------------------------------------------------- |
116 | | // Serialize a double |
117 | | template <> |
118 | 66.1k | inline size_t Write<double>(IOStream *stream, const double &f) { |
119 | 66.1k | static_assert(sizeof(double) == 8, "sizeof(double)==8"); |
120 | 66.1k | stream->Write(&f, 8, 1); |
121 | | |
122 | 66.1k | return 8; |
123 | 66.1k | } |
124 | | |
125 | | // ----------------------------------------------------------------------------------- |
126 | | // Serialize a vec3 |
127 | | template <> |
128 | 1.56M | inline size_t Write<aiVector3D>(IOStream *stream, const aiVector3D &v) { |
129 | 1.56M | size_t t = Write<ai_real>(stream, v.x); |
130 | 1.56M | t += Write<float>(stream, v.y); |
131 | 1.56M | t += Write<float>(stream, v.z); |
132 | | |
133 | 1.56M | return t; |
134 | 1.56M | } |
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 | 44.4k | inline size_t Write<aiColor4D>(IOStream *stream, const aiColor4D &v) { |
151 | 44.4k | size_t t = Write<ai_real>(stream, v.r); |
152 | 44.4k | t += Write<float>(stream, v.g); |
153 | 44.4k | t += Write<float>(stream, v.b); |
154 | 44.4k | t += Write<float>(stream, v.a); |
155 | | |
156 | 44.4k | return t; |
157 | 44.4k | } |
158 | | |
159 | | // ----------------------------------------------------------------------------------- |
160 | | // Serialize a quaternion |
161 | | template <> |
162 | 46.3k | inline size_t Write<aiQuaternion>(IOStream *stream, const aiQuaternion &v) { |
163 | 46.3k | size_t t = Write<ai_real>(stream, v.w); |
164 | 46.3k | t += Write<float>(stream, v.x); |
165 | 46.3k | t += Write<float>(stream, v.y); |
166 | 46.3k | t += Write<float>(stream, v.z); |
167 | 46.3k | ai_assert(t == 16); |
168 | | |
169 | 46.3k | return t; |
170 | 46.3k | } |
171 | | |
172 | | // ----------------------------------------------------------------------------------- |
173 | | // Serialize a vertex weight |
174 | | template <> |
175 | 64.7k | inline size_t Write<aiVertexWeight>(IOStream *stream, const aiVertexWeight &v) { |
176 | 64.7k | size_t t = Write<unsigned int>(stream, v.mVertexId); |
177 | | |
178 | 64.7k | return t + Write<float>(stream, v.mWeight); |
179 | 64.7k | } |
180 | | |
181 | | constexpr size_t MatrixSize = 64; |
182 | | |
183 | | // ----------------------------------------------------------------------------------- |
184 | | // Serialize a mat4x4 |
185 | | template <> |
186 | 141k | inline size_t Write<aiMatrix4x4>(IOStream *stream, const aiMatrix4x4 &m) { |
187 | 705k | for (unsigned int i = 0; i < 4; ++i) { |
188 | 2.82M | for (unsigned int i2 = 0; i2 < 4; ++i2) { |
189 | 2.25M | Write<ai_real>(stream, m[i][i2]); |
190 | 2.25M | } |
191 | 564k | } |
192 | | |
193 | 141k | return MatrixSize; |
194 | 141k | } |
195 | | |
196 | | // ----------------------------------------------------------------------------------- |
197 | | // Serialize an aiVectorKey |
198 | | template <> |
199 | 18.4k | inline size_t Write<aiVectorKey>(IOStream *stream, const aiVectorKey &v) { |
200 | 18.4k | const size_t t = Write<double>(stream, v.mTime); |
201 | 18.4k | return t + Write<aiVector3D>(stream, v.mValue); |
202 | 18.4k | } |
203 | | |
204 | | // ----------------------------------------------------------------------------------- |
205 | | // Serialize an aiQuatKey |
206 | | template <> |
207 | 46.3k | inline size_t Write<aiQuatKey>(IOStream *stream, const aiQuatKey &v) { |
208 | 46.3k | const size_t t = Write<double>(stream, v.mTime); |
209 | 46.3k | return t + Write<aiQuaternion>(stream, v.mValue); |
210 | 46.3k | } |
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 | 91.9k | inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) { |
225 | 91.9k | size_t n = 0; |
226 | 1.80M | for (unsigned int i = 0; i < size; i++) |
227 | 1.71M | n += Write<T>(stream, in[i]); |
228 | | |
229 | 91.9k | return n; |
230 | 91.9k | } unsigned long Assimp::WriteArray<aiVector3t<float> >(Assimp::IOStream*, aiVector3t<float> const*, unsigned int) Line | Count | Source | 224 | 76.7k | inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) { | 225 | 76.7k | size_t n = 0; | 226 | 1.61M | for (unsigned int i = 0; i < size; i++) | 227 | 1.54M | n += Write<T>(stream, in[i]); | 228 | | | 229 | 76.7k | return n; | 230 | 76.7k | } |
unsigned long Assimp::WriteArray<aiColor4t<float> >(Assimp::IOStream*, aiColor4t<float> const*, unsigned int) Line | Count | Source | 224 | 5.83k | inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) { | 225 | 5.83k | size_t n = 0; | 226 | 50.3k | for (unsigned int i = 0; i < size; i++) | 227 | 44.4k | n += Write<T>(stream, in[i]); | 228 | | | 229 | 5.83k | return n; | 230 | 5.83k | } |
unsigned long Assimp::WriteArray<aiVertexWeight>(Assimp::IOStream*, aiVertexWeight const*, unsigned int) Line | Count | Source | 224 | 2.43k | inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) { | 225 | 2.43k | size_t n = 0; | 226 | 67.1k | for (unsigned int i = 0; i < size; i++) | 227 | 64.7k | n += Write<T>(stream, in[i]); | 228 | | | 229 | 2.43k | return n; | 230 | 2.43k | } |
unsigned long Assimp::WriteArray<aiVectorKey>(Assimp::IOStream*, aiVectorKey const*, unsigned int) Line | Count | Source | 224 | 4.62k | inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) { | 225 | 4.62k | size_t n = 0; | 226 | 23.0k | for (unsigned int i = 0; i < size; i++) | 227 | 18.4k | n += Write<T>(stream, in[i]); | 228 | | | 229 | 4.62k | return n; | 230 | 4.62k | } |
unsigned long Assimp::WriteArray<aiQuatKey>(Assimp::IOStream*, aiQuatKey const*, unsigned int) Line | Count | Source | 224 | 2.31k | inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) { | 225 | 2.31k | size_t n = 0; | 226 | 48.6k | for (unsigned int i = 0; i < size; i++) | 227 | 46.3k | n += Write<T>(stream, in[i]); | 228 | | | 229 | 2.31k | return n; | 230 | 2.31k | } |
|
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 final : public IOStream { |
243 | | public: |
244 | | AssbinChunkWriter(IOStream *container, uint32_t magic, size_t initial = 4096) : |
245 | 230k | buffer(nullptr), |
246 | 230k | magic(magic), |
247 | 230k | container(container), |
248 | 230k | cur_size(0), |
249 | 230k | cursor(0), |
250 | 230k | initial(initial) { |
251 | | // empty |
252 | 230k | } |
253 | | |
254 | 230k | ~AssbinChunkWriter() override { |
255 | 230k | if (container) { |
256 | 230k | container->Write(&magic, sizeof(uint32_t), 1); |
257 | 230k | container->Write(&cursor, sizeof(uint32_t), 1); |
258 | 230k | container->Write(buffer, 1, cursor); |
259 | 230k | } |
260 | 230k | if (buffer) delete[] buffer; |
261 | 230k | } |
262 | | |
263 | 0 | void *GetBufferPointer() { return buffer; } |
264 | | |
265 | 0 | size_t Read(void * /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) override { |
266 | 0 | return 0; |
267 | 0 | } |
268 | | |
269 | 0 | aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) override { |
270 | 0 | return aiReturn_FAILURE; |
271 | 0 | } |
272 | | |
273 | 0 | size_t Tell() const override { |
274 | 0 | return cursor; |
275 | 0 | } |
276 | | |
277 | 0 | void Flush() override { |
278 | | // not implemented |
279 | 0 | } |
280 | | |
281 | 0 | size_t FileSize() const override { |
282 | 0 | return cursor; |
283 | 0 | } |
284 | | |
285 | 12.1M | size_t Write(const void *pvBuffer, size_t pSize, size_t pCount) override { |
286 | 12.1M | pSize *= pCount; |
287 | 12.1M | if (cursor + pSize > cur_size) { |
288 | 238k | Grow(cursor + pSize); |
289 | 238k | } |
290 | | |
291 | 12.1M | memcpy(buffer + cursor, pvBuffer, pSize); |
292 | 12.1M | cursor += pSize; |
293 | | |
294 | 12.1M | return pCount; |
295 | 12.1M | } |
296 | | |
297 | | private: |
298 | | // ------------------------------------------------------------------- |
299 | 238k | void Grow(size_t need = 0) { |
300 | 238k | size_t new_size = std::max(initial, std::max(need, cur_size + (cur_size >> 1))); |
301 | | |
302 | 238k | const uint8_t *const old = buffer; |
303 | 238k | buffer = new uint8_t[new_size]; |
304 | | |
305 | 238k | if (old) { |
306 | 8.50k | memcpy(buffer, old, cur_size); |
307 | 8.50k | delete[] old; |
308 | 8.50k | } |
309 | | |
310 | 238k | cur_size = new_size; |
311 | 238k | } |
312 | | |
313 | | private: |
314 | | uint8_t *buffer; |
315 | | uint32_t magic; |
316 | | IOStream *container; |
317 | | size_t cur_size, cursor, initial; |
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 | 138k | void WriteBinaryNode(IOStream *container, const aiNode *node) { |
334 | 138k | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AINODE); |
335 | | |
336 | 138k | unsigned int nb_metadata = (node->mMetaData != nullptr ? node->mMetaData->mNumProperties : 0); |
337 | | |
338 | 138k | Write<aiString>(&chunk, node->mName); |
339 | 138k | Write<aiMatrix4x4>(&chunk, node->mTransformation); |
340 | 138k | Write<unsigned int>(&chunk, node->mNumChildren); |
341 | 138k | Write<unsigned int>(&chunk, node->mNumMeshes); |
342 | 138k | Write<unsigned int>(&chunk, nb_metadata); |
343 | | |
344 | 166k | for (unsigned int i = 0; i < node->mNumMeshes; ++i) { |
345 | 27.7k | Write<unsigned int>(&chunk, node->mMeshes[i]); |
346 | 27.7k | } |
347 | | |
348 | 272k | for (unsigned int i = 0; i < node->mNumChildren; ++i) { |
349 | 133k | WriteBinaryNode(&chunk, node->mChildren[i]); |
350 | 133k | } |
351 | | |
352 | 173k | for (unsigned int i = 0; i < nb_metadata; ++i) { |
353 | 34.5k | const aiString &key = node->mMetaData->mKeys[i]; |
354 | 34.5k | aiMetadataType type = node->mMetaData->mValues[i].mType; |
355 | 34.5k | void *value = node->mMetaData->mValues[i].mData; |
356 | | |
357 | 34.5k | Write<aiString>(&chunk, key); |
358 | 34.5k | Write<uint16_t>(&chunk, (uint16_t)type); |
359 | | |
360 | 34.5k | switch (type) { |
361 | 2.38k | case AI_BOOL: |
362 | 2.38k | Write<bool>(&chunk, *((bool *)value)); |
363 | 2.38k | break; |
364 | 22.8k | case AI_INT32: |
365 | 22.8k | Write<int32_t>(&chunk, *((int32_t *)value)); |
366 | 22.8k | break; |
367 | 589 | case AI_UINT64: |
368 | 589 | Write<uint64_t>(&chunk, *((uint64_t *)value)); |
369 | 589 | break; |
370 | 895 | case AI_FLOAT: |
371 | 895 | Write<float>(&chunk, *((float *)value)); |
372 | 895 | break; |
373 | 0 | case AI_DOUBLE: |
374 | 0 | Write<double>(&chunk, *((double *)value)); |
375 | 0 | break; |
376 | 4.32k | case AI_AISTRING: |
377 | 4.32k | Write<aiString>(&chunk, *((aiString *)value)); |
378 | 4.32k | break; |
379 | 3.55k | case AI_AIVECTOR3D: |
380 | 3.55k | Write<aiVector3D>(&chunk, *((aiVector3D *)value)); |
381 | 3.55k | break; |
382 | | #ifdef SWIG |
383 | | case FORCE_32BIT: |
384 | | #endif // SWIG |
385 | 0 | default: |
386 | 0 | break; |
387 | 34.5k | } |
388 | 34.5k | } |
389 | 138k | } |
390 | | |
391 | | // ----------------------------------------------------------------------------------- |
392 | 348 | void WriteBinaryTexture(IOStream *container, const aiTexture *tex) { |
393 | 348 | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AITEXTURE); |
394 | | |
395 | 348 | Write<unsigned int>(&chunk, tex->mWidth); |
396 | 348 | Write<unsigned int>(&chunk, tex->mHeight); |
397 | | // Write the texture format, but don't include the null terminator. |
398 | 348 | chunk.Write(tex->achFormatHint, sizeof(char), HINTMAXTEXTURELEN - 1); |
399 | | |
400 | 348 | if (!shortened) { |
401 | 348 | if (!tex->mHeight) { |
402 | 71 | chunk.Write(tex->pcData, 1, tex->mWidth); |
403 | 277 | } else { |
404 | 277 | chunk.Write(tex->pcData, 1, tex->mWidth * tex->mHeight * 4); |
405 | 277 | } |
406 | 348 | } |
407 | 348 | } |
408 | | |
409 | | // ----------------------------------------------------------------------------------- |
410 | 2.43k | void WriteBinaryBone(IOStream *container, const aiBone *b) { |
411 | 2.43k | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIBONE); |
412 | | |
413 | 2.43k | Write<aiString>(&chunk, b->mName); |
414 | 2.43k | Write<unsigned int>(&chunk, b->mNumWeights); |
415 | 2.43k | 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 | 2.43k | if (shortened) { |
420 | 0 | WriteBounds(&chunk, b->mWeights, b->mNumWeights); |
421 | 0 | } // else write as usual |
422 | 2.43k | else |
423 | 2.43k | WriteArray<aiVertexWeight>(&chunk, b->mWeights, b->mNumWeights); |
424 | 2.43k | } |
425 | | |
426 | | // ----------------------------------------------------------------------------------- |
427 | 27.6k | void WriteBinaryMesh(IOStream *container, const aiMesh *mesh) { |
428 | 27.6k | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMESH); |
429 | | |
430 | 27.6k | Write<unsigned int>(&chunk, mesh->mPrimitiveTypes); |
431 | 27.6k | Write<unsigned int>(&chunk, mesh->mNumVertices); |
432 | 27.6k | Write<unsigned int>(&chunk, mesh->mNumFaces); |
433 | 27.6k | Write<unsigned int>(&chunk, mesh->mNumBones); |
434 | 27.6k | Write<unsigned int>(&chunk, mesh->mMaterialIndex); |
435 | | |
436 | | // first of all, write bits for all existent vertex components |
437 | 27.6k | unsigned int c = 0; |
438 | 27.6k | if (mesh->mVertices) { |
439 | 27.6k | c |= ASSBIN_MESH_HAS_POSITIONS; |
440 | 27.6k | } |
441 | 27.6k | if (mesh->mNormals) { |
442 | 26.8k | c |= ASSBIN_MESH_HAS_NORMALS; |
443 | 26.8k | } |
444 | 27.6k | if (mesh->mTangents && mesh->mBitangents) { |
445 | 7.35k | c |= ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS; |
446 | 7.35k | } |
447 | 35.1k | for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++n) { |
448 | 35.1k | if (!mesh->mTextureCoords[n]) { |
449 | 27.6k | break; |
450 | 27.6k | } |
451 | 7.51k | c |= ASSBIN_MESH_HAS_TEXCOORD(n); |
452 | 7.51k | } |
453 | 33.4k | for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++n) { |
454 | 33.4k | if (!mesh->mColors[n]) { |
455 | 27.6k | break; |
456 | 27.6k | } |
457 | 5.83k | c |= ASSBIN_MESH_HAS_COLOR(n); |
458 | 5.83k | } |
459 | 27.6k | Write<unsigned int>(&chunk, c); |
460 | | |
461 | 27.6k | aiVector3D minVec, maxVec; |
462 | 27.6k | if (mesh->mVertices) { |
463 | 27.6k | if (shortened) { |
464 | 0 | WriteBounds(&chunk, mesh->mVertices, mesh->mNumVertices); |
465 | 0 | } // else write as usual |
466 | 27.6k | else |
467 | 27.6k | WriteArray<aiVector3D>(&chunk, mesh->mVertices, mesh->mNumVertices); |
468 | 27.6k | } |
469 | 27.6k | if (mesh->mNormals) { |
470 | 26.8k | if (shortened) { |
471 | 0 | WriteBounds(&chunk, mesh->mNormals, mesh->mNumVertices); |
472 | 0 | } // else write as usual |
473 | 26.8k | else |
474 | 26.8k | WriteArray<aiVector3D>(&chunk, mesh->mNormals, mesh->mNumVertices); |
475 | 26.8k | } |
476 | 27.6k | if (mesh->mTangents && mesh->mBitangents) { |
477 | 7.35k | 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 | 7.35k | else { |
482 | 7.35k | WriteArray<aiVector3D>(&chunk, mesh->mTangents, mesh->mNumVertices); |
483 | 7.35k | WriteArray<aiVector3D>(&chunk, mesh->mBitangents, mesh->mNumVertices); |
484 | 7.35k | } |
485 | 7.35k | } |
486 | 33.4k | for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++n) { |
487 | 33.4k | if (!mesh->mColors[n]) |
488 | 27.6k | break; |
489 | | |
490 | 5.83k | if (shortened) { |
491 | 0 | WriteBounds(&chunk, mesh->mColors[n], mesh->mNumVertices); |
492 | 0 | } // else write as usual |
493 | 5.83k | else |
494 | 5.83k | WriteArray<aiColor4D>(&chunk, mesh->mColors[n], mesh->mNumVertices); |
495 | 5.83k | } |
496 | 35.1k | for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++n) { |
497 | 35.1k | if (!mesh->mTextureCoords[n]) |
498 | 27.6k | break; |
499 | | |
500 | | // write number of UV components |
501 | 7.51k | Write<unsigned int>(&chunk, mesh->mNumUVComponents[n]); |
502 | | |
503 | 7.51k | if (shortened) { |
504 | 0 | WriteBounds(&chunk, mesh->mTextureCoords[n], mesh->mNumVertices); |
505 | 0 | } // else write as usual |
506 | 7.51k | else |
507 | 7.51k | WriteArray<aiVector3D>(&chunk, mesh->mTextureCoords[n], mesh->mNumVertices); |
508 | 7.51k | } |
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 | 27.6k | 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 | 27.6k | { |
533 | | // if there are less than 2^16 vertices, we can simply use 16 bit integers ... |
534 | 689k | for (unsigned int i = 0; i < mesh->mNumFaces; ++i) { |
535 | 661k | const aiFace &f = mesh->mFaces[i]; |
536 | | |
537 | 661k | static_assert(AI_MAX_FACE_INDICES <= 0xffff, "AI_MAX_FACE_INDICES <= 0xffff"); |
538 | 661k | Write<uint16_t>(&chunk, static_cast<uint16_t>(f.mNumIndices)); |
539 | | |
540 | 2.50M | for (unsigned int a = 0; a < f.mNumIndices; ++a) { |
541 | 1.83M | if (mesh->mNumVertices < (1u << 16)) { |
542 | 1.83M | Write<uint16_t>(&chunk, static_cast<uint16_t>(f.mIndices[a])); |
543 | 1.83M | } else { |
544 | 0 | Write<unsigned int>(&chunk, f.mIndices[a]); |
545 | 0 | } |
546 | 1.83M | } |
547 | 661k | } |
548 | 27.6k | } |
549 | | |
550 | | // write bones |
551 | 27.6k | if (mesh->mNumBones) { |
552 | 3.28k | for (unsigned int a = 0; a < mesh->mNumBones; ++a) { |
553 | 2.43k | const aiBone *b = mesh->mBones[a]; |
554 | 2.43k | WriteBinaryBone(&chunk, b); |
555 | 2.43k | } |
556 | 842 | } |
557 | 27.6k | } |
558 | | |
559 | | // ----------------------------------------------------------------------------------- |
560 | 48.2k | void WriteBinaryMaterialProperty(IOStream *container, const aiMaterialProperty *prop) { |
561 | 48.2k | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMATERIALPROPERTY); |
562 | | |
563 | 48.2k | Write<aiString>(&chunk, prop->mKey); |
564 | 48.2k | Write<unsigned int>(&chunk, prop->mSemantic); |
565 | 48.2k | Write<unsigned int>(&chunk, prop->mIndex); |
566 | | |
567 | 48.2k | Write<unsigned int>(&chunk, prop->mDataLength); |
568 | 48.2k | Write<unsigned int>(&chunk, (unsigned int)prop->mType); |
569 | 48.2k | chunk.Write(prop->mData, 1, prop->mDataLength); |
570 | 48.2k | } |
571 | | |
572 | | // ----------------------------------------------------------------------------------- |
573 | 4.86k | void WriteBinaryMaterial(IOStream *container, const aiMaterial *mat) { |
574 | 4.86k | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMATERIAL); |
575 | | |
576 | 4.86k | Write<unsigned int>(&chunk, mat->mNumProperties); |
577 | 53.1k | for (unsigned int i = 0; i < mat->mNumProperties; ++i) { |
578 | 48.2k | WriteBinaryMaterialProperty(&chunk, mat->mProperties[i]); |
579 | 48.2k | } |
580 | 4.86k | } |
581 | | |
582 | | // ----------------------------------------------------------------------------------- |
583 | 2.31k | void WriteBinaryNodeAnim(IOStream *container, const aiNodeAnim *nd) { |
584 | 2.31k | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AINODEANIM); |
585 | | |
586 | 2.31k | Write<aiString>(&chunk, nd->mNodeName); |
587 | 2.31k | Write<unsigned int>(&chunk, nd->mNumPositionKeys); |
588 | 2.31k | Write<unsigned int>(&chunk, nd->mNumRotationKeys); |
589 | 2.31k | Write<unsigned int>(&chunk, nd->mNumScalingKeys); |
590 | 2.31k | Write<unsigned int>(&chunk, nd->mPreState); |
591 | 2.31k | Write<unsigned int>(&chunk, nd->mPostState); |
592 | | |
593 | 2.31k | if (nd->mPositionKeys) { |
594 | 2.31k | if (shortened) { |
595 | 0 | WriteBounds(&chunk, nd->mPositionKeys, nd->mNumPositionKeys); |
596 | |
|
597 | 0 | } // else write as usual |
598 | 2.31k | else |
599 | 2.31k | WriteArray<aiVectorKey>(&chunk, nd->mPositionKeys, nd->mNumPositionKeys); |
600 | 2.31k | } |
601 | 2.31k | if (nd->mRotationKeys) { |
602 | 2.31k | if (shortened) { |
603 | 0 | WriteBounds(&chunk, nd->mRotationKeys, nd->mNumRotationKeys); |
604 | |
|
605 | 0 | } // else write as usual |
606 | 2.31k | else |
607 | 2.31k | WriteArray<aiQuatKey>(&chunk, nd->mRotationKeys, nd->mNumRotationKeys); |
608 | 2.31k | } |
609 | 2.31k | if (nd->mScalingKeys) { |
610 | 2.31k | if (shortened) { |
611 | 0 | WriteBounds(&chunk, nd->mScalingKeys, nd->mNumScalingKeys); |
612 | |
|
613 | 0 | } // else write as usual |
614 | 2.31k | else |
615 | 2.31k | WriteArray<aiVectorKey>(&chunk, nd->mScalingKeys, nd->mNumScalingKeys); |
616 | 2.31k | } |
617 | 2.31k | } |
618 | | |
619 | | // ----------------------------------------------------------------------------------- |
620 | 678 | void WriteBinaryAnim(IOStream *container, const aiAnimation *anim) { |
621 | 678 | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIANIMATION); |
622 | | |
623 | 678 | Write<aiString>(&chunk, anim->mName); |
624 | 678 | Write<double>(&chunk, anim->mDuration); |
625 | 678 | Write<double>(&chunk, anim->mTicksPerSecond); |
626 | 678 | Write<unsigned int>(&chunk, anim->mNumChannels); |
627 | | |
628 | 2.98k | for (unsigned int a = 0; a < anim->mNumChannels; ++a) { |
629 | 2.31k | const aiNodeAnim *nd = anim->mChannels[a]; |
630 | 2.31k | WriteBinaryNodeAnim(&chunk, nd); |
631 | 2.31k | } |
632 | 678 | } |
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 | 133 | void WriteBinaryCamera(IOStream *container, const aiCamera *cam) { |
663 | 133 | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AICAMERA); |
664 | | |
665 | 133 | Write<aiString>(&chunk, cam->mName); |
666 | 133 | Write<aiVector3D>(&chunk, cam->mPosition); |
667 | 133 | Write<aiVector3D>(&chunk, cam->mLookAt); |
668 | 133 | Write<aiVector3D>(&chunk, cam->mUp); |
669 | 133 | Write<float>(&chunk, cam->mHorizontalFOV); |
670 | 133 | Write<float>(&chunk, cam->mClipPlaneNear); |
671 | 133 | Write<float>(&chunk, cam->mClipPlaneFar); |
672 | 133 | Write<float>(&chunk, cam->mAspect); |
673 | 133 | } |
674 | | |
675 | | // ----------------------------------------------------------------------------------- |
676 | 4.85k | void WriteBinaryScene(IOStream *container, const aiScene *scene) { |
677 | 4.85k | AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AISCENE); |
678 | | |
679 | | // basic scene information |
680 | 4.85k | Write<unsigned int>(&chunk, scene->mFlags); |
681 | 4.85k | Write<unsigned int>(&chunk, scene->mNumMeshes); |
682 | 4.85k | Write<unsigned int>(&chunk, scene->mNumMaterials); |
683 | 4.85k | Write<unsigned int>(&chunk, scene->mNumAnimations); |
684 | 4.85k | Write<unsigned int>(&chunk, scene->mNumTextures); |
685 | 4.85k | Write<unsigned int>(&chunk, scene->mNumLights); |
686 | 4.85k | Write<unsigned int>(&chunk, scene->mNumCameras); |
687 | | |
688 | | // write node graph |
689 | 4.85k | WriteBinaryNode(&chunk, scene->mRootNode); |
690 | | |
691 | | // write all meshes |
692 | 32.5k | for (unsigned int i = 0; i < scene->mNumMeshes; ++i) { |
693 | 27.6k | const aiMesh *mesh = scene->mMeshes[i]; |
694 | 27.6k | WriteBinaryMesh(&chunk, mesh); |
695 | 27.6k | } |
696 | | |
697 | | // write materials |
698 | 9.71k | for (unsigned int i = 0; i < scene->mNumMaterials; ++i) { |
699 | 4.86k | const aiMaterial *mat = scene->mMaterials[i]; |
700 | 4.86k | WriteBinaryMaterial(&chunk, mat); |
701 | 4.86k | } |
702 | | |
703 | | // write all animations |
704 | 5.53k | for (unsigned int i = 0; i < scene->mNumAnimations; ++i) { |
705 | 678 | const aiAnimation *anim = scene->mAnimations[i]; |
706 | 678 | WriteBinaryAnim(&chunk, anim); |
707 | 678 | } |
708 | | |
709 | | // write all textures |
710 | 5.20k | for (unsigned int i = 0; i < scene->mNumTextures; ++i) { |
711 | 348 | const aiTexture *mesh = scene->mTextures[i]; |
712 | 348 | WriteBinaryTexture(&chunk, mesh); |
713 | 348 | } |
714 | | |
715 | | // write lights |
716 | 4.85k | 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 | 4.98k | for (unsigned int i = 0; i < scene->mNumCameras; ++i) { |
723 | 133 | const aiCamera *cam = scene->mCameras[i]; |
724 | 133 | WriteBinaryCamera(&chunk, cam); |
725 | 133 | } |
726 | 4.85k | } |
727 | | |
728 | | public: |
729 | | AssbinFileWriter(bool shortened, bool compressed) : |
730 | 4.85k | shortened(shortened), compressed(compressed) { |
731 | 4.85k | } |
732 | | |
733 | | // ----------------------------------------------------------------------------------- |
734 | | // Write a binary model dump |
735 | 4.85k | void WriteBinaryDump(const char *pFile, const char *cmd, IOSystem *pIOSystem, const aiScene *pScene) { |
736 | 4.85k | IOStream *out = pIOSystem->Open(pFile, "wb"); |
737 | 4.85k | if (!out) |
738 | 0 | throw std::runtime_error("Unable to open output file " + std::string(pFile) + '\n'); |
739 | | |
740 | 4.85k | auto CloseIOStream = [&]() { |
741 | 4.85k | if (out) { |
742 | 4.85k | pIOSystem->Close(out); |
743 | 4.85k | out = nullptr; // Ensure this is only done once. |
744 | 4.85k | } |
745 | 4.85k | }; |
746 | | |
747 | 4.85k | try { |
748 | 4.85k | time_t tt = time(nullptr); |
749 | | #if _WIN32 |
750 | | tm *p = gmtime(&tt); |
751 | | #else |
752 | 4.85k | struct tm now; |
753 | 4.85k | tm *p = gmtime_r(&tt, &now); |
754 | 4.85k | #endif |
755 | | |
756 | | // header |
757 | 4.85k | char s[64]; |
758 | 4.85k | memset(s, 0, 64); |
759 | | #if _MSC_VER >= 1400 |
760 | | sprintf_s(s, "ASSIMP.binary-dump.%s", asctime(p)); |
761 | | #else |
762 | 4.85k | ai_snprintf(s, 64, "ASSIMP.binary-dump.%s", asctime(p)); |
763 | 4.85k | #endif |
764 | 4.85k | out->Write(s, 44, 1); |
765 | | // == 44 bytes |
766 | | |
767 | 4.85k | Write<unsigned int>(out, ASSBIN_VERSION_MAJOR); |
768 | 4.85k | Write<unsigned int>(out, ASSBIN_VERSION_MINOR); |
769 | 4.85k | Write<unsigned int>(out, aiGetVersionRevision()); |
770 | 4.85k | Write<unsigned int>(out, aiGetCompileFlags()); |
771 | 4.85k | Write<uint16_t>(out, shortened); |
772 | 4.85k | Write<uint16_t>(out, compressed); |
773 | | // == 20 bytes |
774 | | |
775 | 4.85k | char buff[256] = { 0 }; |
776 | 4.85k | ai_snprintf(buff, 256, "%s", pFile); |
777 | 4.85k | out->Write(buff, sizeof(char), 256); |
778 | | |
779 | 4.85k | memset(buff, 0, sizeof(buff)); |
780 | 4.85k | ai_snprintf(buff, 128, "%s", cmd); |
781 | 4.85k | out->Write(buff, sizeof(char), 128); |
782 | | |
783 | | // leave 64 bytes free for future extensions |
784 | 4.85k | memset(buff, 0xcd, 64); |
785 | 4.85k | out->Write(buff, sizeof(char), 64); |
786 | | // == 435 bytes |
787 | | |
788 | | // ==== total header size: 512 bytes |
789 | 4.85k | 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 | 4.85k | 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 | 4.85k | } else { |
812 | 4.85k | WriteBinaryScene(out, pScene); |
813 | 4.85k | } |
814 | | |
815 | 4.85k | CloseIOStream(); |
816 | 4.85k | } catch (...) { |
817 | 0 | CloseIOStream(); |
818 | 0 | throw; |
819 | 0 | } |
820 | 4.85k | } |
821 | | }; |
822 | | |
823 | | void DumpSceneToAssbin( |
824 | | const char *pFile, const char *cmd, IOSystem *pIOSystem, |
825 | 4.85k | const aiScene *pScene, bool shortened, bool compressed) { |
826 | 4.85k | AssbinFileWriter fileWriter(shortened, compressed); |
827 | 4.85k | fileWriter.WriteBinaryDump(pFile, cmd, pIOSystem, pScene); |
828 | 4.85k | } |
829 | | #if _MSC_VER |
830 | | #pragma warning(pop) |
831 | | #endif // _MSC_VER |
832 | | |
833 | | } // end of namespace Assimp |