1 | /* |
2 | --------------------------------------------------------------------------- |
3 | Open Asset Import Library (assimp) |
4 | --------------------------------------------------------------------------- |
5 | |
6 | Copyright (c) 2006-2017, assimp team |
7 | |
8 | |
9 | All rights reserved. |
10 | |
11 | Redistribution and use of this software in source and binary forms, |
12 | with or without modification, are permitted provided that the following |
13 | conditions are met: |
14 | |
15 | * Redistributions of source code must retain the above |
16 | copyright notice, this list of conditions and the |
17 | following disclaimer. |
18 | |
19 | * Redistributions in binary form must reproduce the above |
20 | copyright notice, this list of conditions and the |
21 | following disclaimer in the documentation and/or other |
22 | materials provided with the distribution. |
23 | |
24 | * Neither the name of the assimp team, nor the names of its |
25 | contributors may be used to endorse or promote products |
26 | derived from this software without specific prior |
27 | written permission of the assimp team. |
28 | |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
39 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
40 | --------------------------------------------------------------------------- |
41 | */ |
42 | |
43 | /** @file SIBImporter.cpp |
44 | * @brief Implementation of the SIB importer class. |
45 | * |
46 | * The Nevercenter Silo SIB format is undocumented. |
47 | * All details here have been reverse engineered from |
48 | * studying the binary files output by Silo. |
49 | * |
50 | * Nevertheless, this implementation is reasonably complete. |
51 | */ |
52 | |
53 | |
54 | #ifndef ASSIMP_BUILD_NO_SIB_IMPORTER |
55 | |
56 | // internal headers |
57 | #include "SIBImporter.h" |
58 | #include "ByteSwapper.h" |
59 | #include "StreamReader.h" |
60 | #include "TinyFormatter.h" |
61 | //#include "../contrib/ConvertUTF/ConvertUTF.h" |
62 | #include "../contrib/utf8cpp/source/utf8.h" |
63 | #include <assimp/IOSystem.hpp> |
64 | #include <assimp/DefaultLogger.hpp> |
65 | #include <assimp/scene.h> |
66 | #include <assimp/importerdesc.h> |
67 | |
68 | #include <map> |
69 | |
70 | using namespace Assimp; |
71 | |
72 | static const aiImporterDesc desc = { |
73 | "Silo SIB Importer" , |
74 | "Richard Mitton (http://www.codersnotes.com/about)" , |
75 | "" , |
76 | "Does not apply subdivision." , |
77 | aiImporterFlags_SupportBinaryFlavour, |
78 | 0, 0, |
79 | 0, 0, |
80 | "sib" |
81 | }; |
82 | |
83 | struct SIBChunk { |
84 | uint32_t Tag; |
85 | uint32_t Size; |
86 | } PACK_STRUCT; |
87 | |
88 | enum { |
89 | POS, |
90 | NRM, |
91 | UV, |
92 | N |
93 | }; |
94 | |
95 | typedef std::pair<uint32_t, uint32_t> SIBPair; |
96 | |
97 | struct SIBEdge { |
98 | uint32_t faceA, faceB; |
99 | bool creased; |
100 | }; |
101 | |
102 | struct SIBMesh { |
103 | aiMatrix4x4 axis; |
104 | uint32_t numPts; |
105 | std::vector<aiVector3D> pos, nrm, uv; |
106 | std::vector<uint32_t> idx; |
107 | std::vector<uint32_t> faceStart; |
108 | std::vector<uint32_t> mtls; |
109 | std::vector<SIBEdge> edges; |
110 | std::map<SIBPair, uint32_t> edgeMap; |
111 | }; |
112 | |
113 | struct SIBObject { |
114 | aiString name; |
115 | aiMatrix4x4 axis; |
116 | size_t meshIdx, meshCount; |
117 | }; |
118 | |
119 | struct SIB { |
120 | std::vector<aiMaterial*> mtls; |
121 | std::vector<aiMesh*> meshes; |
122 | std::vector<aiLight*> lights; |
123 | std::vector<SIBObject> objs, insts; |
124 | }; |
125 | |
126 | // ------------------------------------------------------------------------------------------------ |
127 | static SIBEdge& GetEdge(SIBMesh* mesh, uint32_t posA, uint32_t posB) { |
128 | SIBPair pair = (posA < posB) ? SIBPair(posA, posB) : SIBPair(posB, posA); |
129 | std::map<SIBPair, uint32_t>::iterator it = mesh->edgeMap.find(pair); |
130 | if (it != mesh->edgeMap.end()) |
131 | return mesh->edges[it->second]; |
132 | |
133 | SIBEdge edge; |
134 | edge.creased = false; |
135 | edge.faceA = edge.faceB = 0xffffffff; |
136 | mesh->edgeMap[pair] = static_cast<uint32_t>(mesh->edges.size()); |
137 | mesh->edges.push_back(edge); |
138 | return mesh->edges.back(); |
139 | } |
140 | |
141 | // ------------------------------------------------------------------------------------------------ |
142 | // Helpers for reading chunked data. |
143 | |
144 | #define TAG(A,B,C,D) ((A << 24) | (B << 16) | (C << 8) | D) |
145 | |
146 | static SIBChunk ReadChunk(StreamReaderLE* stream) |
147 | { |
148 | SIBChunk chunk; |
149 | chunk.Tag = stream->GetU4(); |
150 | chunk.Size = stream->GetU4(); |
151 | if (chunk.Size > stream->GetRemainingSizeToLimit()) |
152 | DefaultLogger::get()->error("SIB: Chunk overflow" ); |
153 | ByteSwap::Swap4(&chunk.Tag); |
154 | return chunk; |
155 | } |
156 | |
157 | static aiColor3D ReadColor(StreamReaderLE* stream) |
158 | { |
159 | float r = stream->GetF4(); |
160 | float g = stream->GetF4(); |
161 | float b = stream->GetF4(); |
162 | stream->GetU4(); // Colors have an unused(?) 4th component. |
163 | return aiColor3D(r, g, b); |
164 | } |
165 | |
166 | static void UnknownChunk(StreamReaderLE* /*stream*/, const SIBChunk& chunk) |
167 | { |
168 | char temp[5] = { |
169 | static_cast<char>(( chunk.Tag>>24 ) & 0xff), |
170 | static_cast<char>(( chunk.Tag>>16 ) & 0xff), |
171 | static_cast<char>(( chunk.Tag>>8 ) & 0xff), |
172 | static_cast<char>(chunk.Tag & 0xff), '\0' |
173 | }; |
174 | |
175 | DefaultLogger::get()->warn((Formatter::format(), "SIB: Skipping unknown '" ,temp,"' chunk." )); |
176 | } |
177 | |
178 | // Reads a UTF-16LE string and returns it at UTF-8. |
179 | static aiString ReadString(StreamReaderLE *stream, uint32_t numWChars) { |
180 | if ( nullptr == stream || 0 == numWChars ) { |
181 | static const aiString empty; |
182 | return empty; |
183 | } |
184 | |
185 | // Allocate buffers (max expansion is 1 byte -> 4 bytes for UTF-8) |
186 | std::vector<unsigned char> str; |
187 | str.reserve( numWChars * 4 + 1 ); |
188 | uint16_t *temp = new uint16_t[ numWChars ]; |
189 | for ( uint32_t n = 0; n < numWChars; ++n ) { |
190 | temp[ n ] = stream->GetU2(); |
191 | } |
192 | |
193 | // Convert it and NUL-terminate. |
194 | const uint16_t *start( temp ), *end( temp + numWChars ); |
195 | utf8::utf16to8( start, end, back_inserter( str ) ); |
196 | str[ str.size() - 1 ] = '\0'; |
197 | |
198 | // Return the final string. |
199 | aiString result = aiString((const char *)&str[0]); |
200 | delete[] temp; |
201 | |
202 | return result; |
203 | } |
204 | |
205 | // ------------------------------------------------------------------------------------------------ |
206 | // Constructor to be privately used by Importer |
207 | SIBImporter::SIBImporter() { |
208 | // empty |
209 | } |
210 | |
211 | // ------------------------------------------------------------------------------------------------ |
212 | // Destructor, private as well |
213 | SIBImporter::~SIBImporter() { |
214 | // empty |
215 | } |
216 | |
217 | // ------------------------------------------------------------------------------------------------ |
218 | // Returns whether the class can handle the format of the given file. |
219 | bool SIBImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const { |
220 | return SimpleExtensionCheck(pFile, "sib" ); |
221 | } |
222 | |
223 | // ------------------------------------------------------------------------------------------------ |
224 | const aiImporterDesc* SIBImporter::GetInfo () const { |
225 | return &desc; |
226 | } |
227 | |
228 | // ------------------------------------------------------------------------------------------------ |
229 | static void ReadVerts(SIBMesh* mesh, StreamReaderLE* stream, uint32_t count) { |
230 | if ( nullptr == mesh || nullptr == stream ) { |
231 | return; |
232 | } |
233 | |
234 | mesh->pos.resize(count); |
235 | for ( uint32_t n=0; n<count; ++n ) { |
236 | mesh->pos[ n ].x = stream->GetF4(); |
237 | mesh->pos[ n ].y = stream->GetF4(); |
238 | mesh->pos[ n ].z = stream->GetF4(); |
239 | } |
240 | } |
241 | |
242 | // ------------------------------------------------------------------------------------------------ |
243 | static void ReadFaces(SIBMesh* mesh, StreamReaderLE* stream) |
244 | { |
245 | uint32_t ptIdx = 0; |
246 | while (stream->GetRemainingSizeToLimit() > 0) |
247 | { |
248 | uint32_t numPoints = stream->GetU4(); |
249 | |
250 | // Store room for the N index channels, plus the point count. |
251 | size_t pos = mesh->idx.size() + 1; |
252 | mesh->idx.resize(pos + numPoints*N); |
253 | mesh->idx[pos-1] = numPoints; |
254 | uint32_t *idx = &mesh->idx[pos]; |
255 | |
256 | mesh->faceStart.push_back(static_cast<uint32_t>(pos-1)); |
257 | mesh->mtls.push_back(0); |
258 | |
259 | // Read all the position data. |
260 | // UV/normals will be supplied later. |
261 | // Positions are supplied indexed already, so we preserve that |
262 | // mapping. UVs are supplied uniquely, so we allocate unique indices. |
263 | for (uint32_t n=0;n<numPoints;n++,idx+=N,ptIdx++) |
264 | { |
265 | uint32_t p = stream->GetU4(); |
266 | if (p >= mesh->pos.size()) |
267 | throw DeadlyImportError("Vertex index is out of range." ); |
268 | idx[POS] = p; |
269 | idx[NRM] = ptIdx; |
270 | idx[UV] = ptIdx; |
271 | } |
272 | } |
273 | |
274 | // Allocate data channels for normals/UVs. |
275 | mesh->nrm.resize(ptIdx, aiVector3D(0,0,0)); |
276 | mesh->uv.resize(ptIdx, aiVector3D(0,0,0)); |
277 | |
278 | mesh->numPts = ptIdx; |
279 | } |
280 | |
281 | // ------------------------------------------------------------------------------------------------ |
282 | static void ReadUVs(SIBMesh* mesh, StreamReaderLE* stream) |
283 | { |
284 | while (stream->GetRemainingSizeToLimit() > 0) |
285 | { |
286 | uint32_t faceIdx = stream->GetU4(); |
287 | uint32_t numPoints = stream->GetU4(); |
288 | |
289 | if (faceIdx >= mesh->faceStart.size()) |
290 | throw DeadlyImportError("Invalid face index." ); |
291 | |
292 | uint32_t pos = mesh->faceStart[faceIdx]; |
293 | uint32_t *idx = &mesh->idx[pos + 1]; |
294 | |
295 | for (uint32_t n=0;n<numPoints;n++,idx+=N) |
296 | { |
297 | uint32_t id = idx[UV]; |
298 | mesh->uv[id].x = stream->GetF4(); |
299 | mesh->uv[id].y = stream->GetF4(); |
300 | } |
301 | } |
302 | } |
303 | |
304 | // ------------------------------------------------------------------------------------------------ |
305 | static void ReadMtls(SIBMesh* mesh, StreamReaderLE* stream) |
306 | { |
307 | // Material assignments are stored run-length encoded. |
308 | // Also, we add 1 to each material so that we can use mtl #0 |
309 | // as the default material. |
310 | uint32_t prevFace = stream->GetU4(); |
311 | uint32_t prevMtl = stream->GetU4() + 1; |
312 | while (stream->GetRemainingSizeToLimit() > 0) |
313 | { |
314 | uint32_t face = stream->GetU4(); |
315 | uint32_t mtl = stream->GetU4() + 1; |
316 | while (prevFace < face) |
317 | { |
318 | if (prevFace >= mesh->mtls.size()) |
319 | throw DeadlyImportError("Invalid face index." ); |
320 | mesh->mtls[prevFace++] = prevMtl; |
321 | } |
322 | |
323 | prevFace = face; |
324 | prevMtl = mtl; |
325 | } |
326 | |
327 | while (prevFace < mesh->mtls.size()) |
328 | mesh->mtls[prevFace++] = prevMtl; |
329 | } |
330 | |
331 | // ------------------------------------------------------------------------------------------------ |
332 | static void ReadAxis(aiMatrix4x4& axis, StreamReaderLE* stream) |
333 | { |
334 | axis.a4 = stream->GetF4(); |
335 | axis.b4 = stream->GetF4(); |
336 | axis.c4 = stream->GetF4(); |
337 | axis.d4 = 1; |
338 | axis.a1 = stream->GetF4(); |
339 | axis.b1 = stream->GetF4(); |
340 | axis.c1 = stream->GetF4(); |
341 | axis.d1 = 0; |
342 | axis.a2 = stream->GetF4(); |
343 | axis.b2 = stream->GetF4(); |
344 | axis.c2 = stream->GetF4(); |
345 | axis.d2 = 0; |
346 | axis.a3 = stream->GetF4(); |
347 | axis.b3 = stream->GetF4(); |
348 | axis.c3 = stream->GetF4(); |
349 | axis.d3 = 0; |
350 | } |
351 | |
352 | // ------------------------------------------------------------------------------------------------ |
353 | static void ReadEdges(SIBMesh* mesh, StreamReaderLE* stream) |
354 | { |
355 | while (stream->GetRemainingSizeToLimit() > 0) |
356 | { |
357 | uint32_t posA = stream->GetU4(); |
358 | uint32_t posB = stream->GetU4(); |
359 | GetEdge(mesh, posA, posB); |
360 | } |
361 | } |
362 | |
363 | // ------------------------------------------------------------------------------------------------ |
364 | static void ReadCreases(SIBMesh* mesh, StreamReaderLE* stream) |
365 | { |
366 | while (stream->GetRemainingSizeToLimit() > 0) |
367 | { |
368 | uint32_t edge = stream->GetU4(); |
369 | if (edge >= mesh->edges.size()) |
370 | throw DeadlyImportError("SIB: Invalid edge index." ); |
371 | mesh->edges[edge].creased = true; |
372 | } |
373 | } |
374 | |
375 | // ------------------------------------------------------------------------------------------------ |
376 | static void ConnectFaces(SIBMesh* mesh) |
377 | { |
378 | // Find faces connected to each edge. |
379 | size_t numFaces = mesh->faceStart.size(); |
380 | for (size_t faceIdx=0;faceIdx<numFaces;faceIdx++) |
381 | { |
382 | uint32_t *idx = &mesh->idx[mesh->faceStart[faceIdx]]; |
383 | uint32_t numPoints = *idx++; |
384 | uint32_t prev = idx[(numPoints-1)*N+POS]; |
385 | |
386 | for (uint32_t i=0;i<numPoints;i++,idx+=N) |
387 | { |
388 | uint32_t next = idx[POS]; |
389 | |
390 | // Find this edge. |
391 | SIBEdge& edge = GetEdge(mesh, prev, next); |
392 | |
393 | // Link this face onto it. |
394 | // This gives potentially undesirable normals when used |
395 | // with non-2-manifold surfaces, but then so does Silo to begin with. |
396 | if (edge.faceA == 0xffffffff) |
397 | edge.faceA = static_cast<uint32_t>(faceIdx); |
398 | else if (edge.faceB == 0xffffffff) |
399 | edge.faceB = static_cast<uint32_t>(faceIdx); |
400 | |
401 | prev = next; |
402 | } |
403 | } |
404 | } |
405 | |
406 | // ------------------------------------------------------------------------------------------------ |
407 | static aiVector3D CalculateVertexNormal(SIBMesh* mesh, uint32_t faceIdx, uint32_t pos, |
408 | const std::vector<aiVector3D>& faceNormals) |
409 | { |
410 | // Creased edges complicate this. We need to find the start/end range of the |
411 | // ring of faces that touch this position. |
412 | // We do this in two passes. The first pass is to find the end of the range, |
413 | // the second is to work backwards to the start and calculate the final normal. |
414 | aiVector3D vtxNormal; |
415 | for (int pass=0;pass<2;pass++) |
416 | { |
417 | vtxNormal = aiVector3D(0, 0, 0); |
418 | uint32_t startFaceIdx = faceIdx; |
419 | uint32_t prevFaceIdx = faceIdx; |
420 | |
421 | // Process each connected face. |
422 | while (true) |
423 | { |
424 | // Accumulate the face normal. |
425 | vtxNormal += faceNormals[faceIdx]; |
426 | |
427 | uint32_t nextFaceIdx = 0xffffffff; |
428 | |
429 | // Move to the next edge sharing this position. |
430 | uint32_t* idx = &mesh->idx[mesh->faceStart[faceIdx]]; |
431 | uint32_t numPoints = *idx++; |
432 | uint32_t posA = idx[(numPoints-1)*N+POS]; |
433 | for (uint32_t n=0;n<numPoints;n++,idx+=N) |
434 | { |
435 | uint32_t posB = idx[POS]; |
436 | |
437 | // Test if this edge shares our target position. |
438 | if (posA == pos || posB == pos) |
439 | { |
440 | SIBEdge& edge = GetEdge(mesh, posA, posB); |
441 | |
442 | // Non-manifold meshes can produce faces which share |
443 | // positions but have no edge entry, so check it. |
444 | if (edge.faceA == faceIdx || edge.faceB == faceIdx) |
445 | { |
446 | // Move to whichever side we didn't just come from. |
447 | if (!edge.creased) { |
448 | if (edge.faceA != prevFaceIdx && edge.faceA != faceIdx && edge.faceA != 0xffffffff) |
449 | nextFaceIdx = edge.faceA; |
450 | else if (edge.faceB != prevFaceIdx && edge.faceB != faceIdx && edge.faceB != 0xffffffff) |
451 | nextFaceIdx = edge.faceB; |
452 | } |
453 | } |
454 | } |
455 | |
456 | posA = posB; |
457 | } |
458 | |
459 | // Stop once we hit either an creased/unconnected edge, or we |
460 | // wrapped around and hit our start point. |
461 | if (nextFaceIdx == 0xffffffff || nextFaceIdx == startFaceIdx) |
462 | break; |
463 | |
464 | prevFaceIdx = faceIdx; |
465 | faceIdx = nextFaceIdx; |
466 | } |
467 | } |
468 | |
469 | // Normalize it. |
470 | float len = vtxNormal.Length(); |
471 | if (len > 0.000000001f) |
472 | vtxNormal /= len; |
473 | return vtxNormal; |
474 | } |
475 | |
476 | // ------------------------------------------------------------------------------------------------ |
477 | static void CalculateNormals(SIBMesh* mesh) |
478 | { |
479 | size_t numFaces = mesh->faceStart.size(); |
480 | |
481 | // Calculate face normals. |
482 | std::vector<aiVector3D> faceNormals(numFaces); |
483 | for (size_t faceIdx=0;faceIdx<numFaces;faceIdx++) |
484 | { |
485 | uint32_t* idx = &mesh->idx[mesh->faceStart[faceIdx]]; |
486 | uint32_t numPoints = *idx++; |
487 | |
488 | aiVector3D faceNormal(0, 0, 0); |
489 | |
490 | uint32_t *prev = &idx[(numPoints-1)*N]; |
491 | |
492 | for (uint32_t i=0;i<numPoints;i++) |
493 | { |
494 | uint32_t *next = &idx[i*N]; |
495 | |
496 | faceNormal += mesh->pos[prev[POS]] ^ mesh->pos[next[POS]]; |
497 | prev = next; |
498 | } |
499 | |
500 | faceNormals[faceIdx] = faceNormal; |
501 | } |
502 | |
503 | // Calculate vertex normals. |
504 | for (size_t faceIdx=0;faceIdx<numFaces;faceIdx++) |
505 | { |
506 | uint32_t* idx = &mesh->idx[mesh->faceStart[faceIdx]]; |
507 | uint32_t numPoints = *idx++; |
508 | |
509 | for (uint32_t i=0;i<numPoints;i++) |
510 | { |
511 | uint32_t pos = idx[i*N+POS]; |
512 | uint32_t nrm = idx[i*N+NRM]; |
513 | aiVector3D vtxNorm = CalculateVertexNormal(mesh, static_cast<uint32_t>(faceIdx), pos, faceNormals); |
514 | mesh->nrm[nrm] = vtxNorm; |
515 | } |
516 | } |
517 | } |
518 | |
519 | // ------------------------------------------------------------------------------------------------ |
520 | struct TempMesh |
521 | { |
522 | std::vector<aiVector3D> vtx; |
523 | std::vector<aiVector3D> nrm; |
524 | std::vector<aiVector3D> uv; |
525 | std::vector<aiFace> faces; |
526 | }; |
527 | |
528 | static void ReadShape(SIB* sib, StreamReaderLE* stream) |
529 | { |
530 | SIBMesh smesh; |
531 | aiString name; |
532 | |
533 | while (stream->GetRemainingSizeToLimit() >= sizeof(SIBChunk)) |
534 | { |
535 | SIBChunk chunk = ReadChunk(stream); |
536 | unsigned oldLimit = stream->SetReadLimit(stream->GetCurrentPos() + chunk.Size); |
537 | |
538 | switch (chunk.Tag) |
539 | { |
540 | case TAG('M','I','R','P'): break; // mirror plane maybe? |
541 | case TAG('I','M','R','P'): break; // instance mirror? (not supported here yet) |
542 | case TAG('D','I','N','F'): break; // display info, not needed |
543 | case TAG('P','I','N','F'): break; // ? |
544 | case TAG('V','M','I','R'): break; // ? |
545 | case TAG('F','M','I','R'): break; // ? |
546 | case TAG('T','X','S','M'): break; // ? |
547 | case TAG('F','A','H','S'): break; // ? |
548 | case TAG('V','R','T','S'): ReadVerts(&smesh, stream, chunk.Size/12); break; |
549 | case TAG('F','A','C','S'): ReadFaces(&smesh, stream); break; |
550 | case TAG('F','T','V','S'): ReadUVs(&smesh, stream); break; |
551 | case TAG('S','N','A','M'): name = ReadString(stream, chunk.Size/2); break; |
552 | case TAG('F','A','M','A'): ReadMtls(&smesh, stream); break; |
553 | case TAG('A','X','I','S'): ReadAxis(smesh.axis, stream); break; |
554 | case TAG('E','D','G','S'): ReadEdges(&smesh, stream); break; |
555 | case TAG('E','C','R','S'): ReadCreases(&smesh, stream); break; |
556 | default: UnknownChunk(stream, chunk); break; |
557 | } |
558 | |
559 | stream->SetCurrentPos(stream->GetReadLimit()); |
560 | stream->SetReadLimit(oldLimit); |
561 | } |
562 | |
563 | ai_assert(smesh.faceStart.size() == smesh.mtls.size()); // sanity check |
564 | |
565 | // Silo doesn't store any normals in the file - we need to compute |
566 | // them ourselves. We can't let AssImp handle it as AssImp doesn't |
567 | // know about our creased edges. |
568 | ConnectFaces(&smesh); |
569 | CalculateNormals(&smesh); |
570 | |
571 | // Construct the transforms. |
572 | aiMatrix4x4 worldToLocal = smesh.axis; |
573 | worldToLocal.Inverse(); |
574 | aiMatrix4x4 worldToLocalN = worldToLocal; |
575 | worldToLocalN.a4 = worldToLocalN.b4 = worldToLocalN.c4 = 0.0f; |
576 | worldToLocalN.Inverse().Transpose(); |
577 | |
578 | // Allocate final mesh data. |
579 | // We'll allocate one mesh for each material. (we'll strip unused ones after) |
580 | std::vector<TempMesh> meshes(sib->mtls.size()); |
581 | |
582 | // Un-index the source data and apply to each vertex. |
583 | for (unsigned fi=0;fi<smesh.faceStart.size();fi++) |
584 | { |
585 | uint32_t start = smesh.faceStart[fi]; |
586 | uint32_t mtl = smesh.mtls[fi]; |
587 | uint32_t *idx = &smesh.idx[start]; |
588 | |
589 | if (mtl >= meshes.size()) |
590 | { |
591 | DefaultLogger::get()->error("SIB: Face material index is invalid." ); |
592 | mtl = 0; |
593 | } |
594 | |
595 | TempMesh& dest = meshes[mtl]; |
596 | |
597 | aiFace face; |
598 | face.mNumIndices = *idx++; |
599 | face.mIndices = new unsigned[face.mNumIndices]; |
600 | for (unsigned pt=0;pt<face.mNumIndices;pt++,idx+=N) |
601 | { |
602 | size_t vtxIdx = dest.vtx.size(); |
603 | face.mIndices[pt] = static_cast<unsigned int>(vtxIdx); |
604 | |
605 | // De-index it. We don't need to validate here as |
606 | // we did it when creating the data. |
607 | aiVector3D pos = smesh.pos[idx[POS]]; |
608 | aiVector3D nrm = smesh.nrm[idx[NRM]]; |
609 | aiVector3D uv = smesh.uv[idx[UV]]; |
610 | |
611 | // The verts are supplied in world-space, so let's |
612 | // transform them back into the local space of this mesh: |
613 | pos = worldToLocal * pos; |
614 | nrm = worldToLocalN * nrm; |
615 | |
616 | dest.vtx.push_back(pos); |
617 | dest.nrm.push_back(nrm); |
618 | dest.uv.push_back(uv); |
619 | } |
620 | dest.faces.push_back(face); |
621 | } |
622 | |
623 | SIBObject obj; |
624 | obj.name = name; |
625 | obj.axis = smesh.axis; |
626 | obj.meshIdx = sib->meshes.size(); |
627 | |
628 | // Now that we know the size of everything, |
629 | // we can build the final one-material-per-mesh data. |
630 | for (size_t n=0;n<meshes.size();n++) |
631 | { |
632 | TempMesh& src = meshes[n]; |
633 | if (src.faces.empty()) |
634 | continue; |
635 | |
636 | aiMesh* mesh = new aiMesh; |
637 | mesh->mName = name; |
638 | mesh->mNumFaces = static_cast<unsigned int>(src.faces.size()); |
639 | mesh->mFaces = new aiFace[mesh->mNumFaces]; |
640 | mesh->mNumVertices = static_cast<unsigned int>(src.vtx.size()); |
641 | mesh->mVertices = new aiVector3D[mesh->mNumVertices]; |
642 | mesh->mNormals = new aiVector3D[mesh->mNumVertices]; |
643 | mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; |
644 | mesh->mNumUVComponents[0] = 2; |
645 | mesh->mMaterialIndex = static_cast<unsigned int>(n); |
646 | |
647 | for (unsigned i=0;i<mesh->mNumVertices;i++) |
648 | { |
649 | mesh->mVertices[i] = src.vtx[i]; |
650 | mesh->mNormals[i] = src.nrm[i]; |
651 | mesh->mTextureCoords[0][i] = src.uv[i]; |
652 | } |
653 | for (unsigned i=0;i<mesh->mNumFaces;i++) |
654 | { |
655 | mesh->mFaces[i] = src.faces[i]; |
656 | } |
657 | |
658 | sib->meshes.push_back(mesh); |
659 | } |
660 | |
661 | obj.meshCount = sib->meshes.size() - obj.meshIdx; |
662 | sib->objs.push_back(obj); |
663 | } |
664 | |
665 | // ------------------------------------------------------------------------------------------------ |
666 | static void ReadMaterial(SIB* sib, StreamReaderLE* stream) |
667 | { |
668 | aiColor3D diff = ReadColor(stream); |
669 | aiColor3D ambi = ReadColor(stream); |
670 | aiColor3D spec = ReadColor(stream); |
671 | aiColor3D emis = ReadColor(stream); |
672 | float shiny = (float)stream->GetU4(); |
673 | |
674 | uint32_t nameLen = stream->GetU4(); |
675 | aiString name = ReadString(stream, nameLen/2); |
676 | uint32_t texLen = stream->GetU4(); |
677 | aiString tex = ReadString(stream, texLen/2); |
678 | |
679 | aiMaterial* mtl = new aiMaterial(); |
680 | mtl->AddProperty(&diff, 1, AI_MATKEY_COLOR_DIFFUSE); |
681 | mtl->AddProperty(&ambi, 1, AI_MATKEY_COLOR_AMBIENT); |
682 | mtl->AddProperty(&spec, 1, AI_MATKEY_COLOR_SPECULAR); |
683 | mtl->AddProperty(&emis, 1, AI_MATKEY_COLOR_EMISSIVE); |
684 | mtl->AddProperty(&shiny, 1, AI_MATKEY_SHININESS); |
685 | mtl->AddProperty(&name, AI_MATKEY_NAME); |
686 | if (tex.length > 0) { |
687 | mtl->AddProperty(&tex, AI_MATKEY_TEXTURE_DIFFUSE(0)); |
688 | mtl->AddProperty(&tex, AI_MATKEY_TEXTURE_AMBIENT(0)); |
689 | } |
690 | |
691 | sib->mtls.push_back(mtl); |
692 | } |
693 | |
694 | // ------------------------------------------------------------------------------------------------ |
695 | static void ReadLightInfo(aiLight* light, StreamReaderLE* stream) |
696 | { |
697 | uint32_t type = stream->GetU4(); |
698 | switch (type) { |
699 | case 0: light->mType = aiLightSource_POINT; break; |
700 | case 1: light->mType = aiLightSource_SPOT; break; |
701 | case 2: light->mType = aiLightSource_DIRECTIONAL; break; |
702 | default: light->mType = aiLightSource_UNDEFINED; break; |
703 | } |
704 | |
705 | light->mPosition.x = stream->GetF4(); |
706 | light->mPosition.y = stream->GetF4(); |
707 | light->mPosition.z = stream->GetF4(); |
708 | light->mDirection.x = stream->GetF4(); |
709 | light->mDirection.y = stream->GetF4(); |
710 | light->mDirection.z = stream->GetF4(); |
711 | light->mColorDiffuse = ReadColor(stream); |
712 | light->mColorAmbient = ReadColor(stream); |
713 | light->mColorSpecular = ReadColor(stream); |
714 | ai_real spotExponent = stream->GetF4(); |
715 | ai_real spotCutoff = stream->GetF4(); |
716 | light->mAttenuationConstant = stream->GetF4(); |
717 | light->mAttenuationLinear = stream->GetF4(); |
718 | light->mAttenuationQuadratic = stream->GetF4(); |
719 | |
720 | // Silo uses the OpenGL default lighting model for it's |
721 | // spot cutoff/exponent. AssImp unfortunately, does not. |
722 | // Let's try and approximate it by solving for the |
723 | // 99% and 1% percentiles. |
724 | // OpenGL: I = cos(angle)^E |
725 | // Solving: angle = acos(I^(1/E)) |
726 | ai_real E = ai_real( 1.0 ) / std::max(spotExponent, (ai_real)0.00001); |
727 | ai_real inner = std::acos(std::pow((ai_real)0.99, E)); |
728 | ai_real outer = std::acos(std::pow((ai_real)0.01, E)); |
729 | |
730 | // Apply the cutoff. |
731 | outer = std::min(outer, AI_DEG_TO_RAD(spotCutoff)); |
732 | |
733 | light->mAngleInnerCone = std::min(inner, outer); |
734 | light->mAngleOuterCone = outer; |
735 | } |
736 | |
737 | static void ReadLight(SIB* sib, StreamReaderLE* stream) |
738 | { |
739 | aiLight* light = new aiLight(); |
740 | |
741 | while (stream->GetRemainingSizeToLimit() >= sizeof(SIBChunk)) |
742 | { |
743 | SIBChunk chunk = ReadChunk(stream); |
744 | unsigned oldLimit = stream->SetReadLimit(stream->GetCurrentPos() + chunk.Size); |
745 | |
746 | switch (chunk.Tag) |
747 | { |
748 | case TAG('L','N','F','O'): ReadLightInfo(light, stream); break; |
749 | case TAG('S','N','A','M'): light->mName = ReadString(stream, chunk.Size/2); break; |
750 | default: UnknownChunk(stream, chunk); break; |
751 | } |
752 | |
753 | stream->SetCurrentPos(stream->GetReadLimit()); |
754 | stream->SetReadLimit(oldLimit); |
755 | } |
756 | |
757 | sib->lights.push_back(light); |
758 | } |
759 | |
760 | // ------------------------------------------------------------------------------------------------ |
761 | static void ReadScale(aiMatrix4x4& axis, StreamReaderLE* stream) |
762 | { |
763 | aiMatrix4x4 scale; |
764 | scale.a1 = stream->GetF4(); |
765 | scale.b1 = stream->GetF4(); |
766 | scale.c1 = stream->GetF4(); |
767 | scale.d1 = stream->GetF4(); |
768 | scale.a2 = stream->GetF4(); |
769 | scale.b2 = stream->GetF4(); |
770 | scale.c2 = stream->GetF4(); |
771 | scale.d2 = stream->GetF4(); |
772 | scale.a3 = stream->GetF4(); |
773 | scale.b3 = stream->GetF4(); |
774 | scale.c3 = stream->GetF4(); |
775 | scale.d3 = stream->GetF4(); |
776 | scale.a4 = stream->GetF4(); |
777 | scale.b4 = stream->GetF4(); |
778 | scale.c4 = stream->GetF4(); |
779 | scale.d4 = stream->GetF4(); |
780 | |
781 | axis = axis * scale; |
782 | } |
783 | |
784 | static void ReadInstance(SIB* sib, StreamReaderLE* stream) |
785 | { |
786 | SIBObject inst; |
787 | uint32_t shapeIndex = 0; |
788 | |
789 | while (stream->GetRemainingSizeToLimit() >= sizeof(SIBChunk)) |
790 | { |
791 | SIBChunk chunk = ReadChunk(stream); |
792 | unsigned oldLimit = stream->SetReadLimit(stream->GetCurrentPos() + chunk.Size); |
793 | |
794 | switch (chunk.Tag) |
795 | { |
796 | case TAG('D','I','N','F'): break; // display info, not needed |
797 | case TAG('P','I','N','F'): break; // ? |
798 | case TAG('A','X','I','S'): ReadAxis(inst.axis, stream); break; |
799 | case TAG('I','N','S','I'): shapeIndex = stream->GetU4(); break; |
800 | case TAG('S','M','T','X'): ReadScale(inst.axis, stream); break; |
801 | case TAG('S','N','A','M'): inst.name = ReadString(stream, chunk.Size/2); break; |
802 | default: UnknownChunk(stream, chunk); break; |
803 | } |
804 | |
805 | stream->SetCurrentPos(stream->GetReadLimit()); |
806 | stream->SetReadLimit(oldLimit); |
807 | } |
808 | |
809 | if ( shapeIndex >= sib->objs.size() ) { |
810 | throw DeadlyImportError( "SIB: Invalid shape index." ); |
811 | } |
812 | |
813 | const SIBObject& src = sib->objs[shapeIndex]; |
814 | inst.meshIdx = src.meshIdx; |
815 | inst.meshCount = src.meshCount; |
816 | sib->insts.push_back(inst); |
817 | } |
818 | |
819 | // ------------------------------------------------------------------------------------------------ |
820 | static void CheckVersion(StreamReaderLE* stream) |
821 | { |
822 | uint32_t version = stream->GetU4(); |
823 | if ( version < 1 || version > 2 ) { |
824 | throw DeadlyImportError( "SIB: Unsupported file version." ); |
825 | } |
826 | } |
827 | |
828 | static void ReadScene(SIB* sib, StreamReaderLE* stream) |
829 | { |
830 | // Parse each chunk in turn. |
831 | while (stream->GetRemainingSizeToLimit() >= sizeof(SIBChunk)) |
832 | { |
833 | SIBChunk chunk = ReadChunk(stream); |
834 | unsigned oldLimit = stream->SetReadLimit(stream->GetCurrentPos() + chunk.Size); |
835 | |
836 | switch (chunk.Tag) |
837 | { |
838 | case TAG('H','E','A','D'): CheckVersion(stream); break; |
839 | case TAG('S','H','A','P'): ReadShape(sib, stream); break; |
840 | case TAG('G','R','P','S'): break; // group assignment, we don't import this |
841 | case TAG('T','E','X','P'): break; // ? |
842 | case TAG('I','N','S','T'): ReadInstance(sib, stream); break; |
843 | case TAG('M','A','T','R'): ReadMaterial(sib, stream); break; |
844 | case TAG('L','G','H','T'): ReadLight(sib, stream); break; |
845 | default: UnknownChunk(stream, chunk); break; |
846 | } |
847 | |
848 | stream->SetCurrentPos(stream->GetReadLimit()); |
849 | stream->SetReadLimit(oldLimit); |
850 | } |
851 | } |
852 | |
853 | // ------------------------------------------------------------------------------------------------ |
854 | // Imports the given file into the given scene structure. |
855 | void SIBImporter::InternReadFile(const std::string& pFile, |
856 | aiScene* pScene, IOSystem* pIOHandler) |
857 | { |
858 | StreamReaderLE stream(pIOHandler->Open(pFile, "rb" )); |
859 | |
860 | // We should have at least one chunk |
861 | if (stream.GetRemainingSize() < 16) |
862 | throw DeadlyImportError("SIB file is either empty or corrupt: " + pFile); |
863 | |
864 | SIB sib; |
865 | |
866 | // Default material. |
867 | aiMaterial* defmtl = new aiMaterial; |
868 | aiString defname = aiString(AI_DEFAULT_MATERIAL_NAME); |
869 | defmtl->AddProperty(&defname, AI_MATKEY_NAME); |
870 | sib.mtls.push_back(defmtl); |
871 | |
872 | // Read it all. |
873 | ReadScene(&sib, &stream); |
874 | |
875 | // Join the instances and objects together. |
876 | size_t firstInst = sib.objs.size(); |
877 | sib.objs.insert(sib.objs.end(), sib.insts.begin(), sib.insts.end()); |
878 | sib.insts.clear(); |
879 | |
880 | // Transfer to the aiScene. |
881 | pScene->mNumMaterials = static_cast<unsigned int>(sib.mtls.size()); |
882 | pScene->mNumMeshes = static_cast<unsigned int>(sib.meshes.size()); |
883 | pScene->mNumLights = static_cast<unsigned int>(sib.lights.size()); |
884 | pScene->mMaterials = pScene->mNumMaterials ? new aiMaterial*[pScene->mNumMaterials] : NULL; |
885 | pScene->mMeshes = pScene->mNumMeshes ? new aiMesh*[pScene->mNumMeshes] : NULL; |
886 | pScene->mLights = pScene->mNumLights ? new aiLight*[pScene->mNumLights] : NULL; |
887 | if (pScene->mNumMaterials) |
888 | memcpy(pScene->mMaterials, &sib.mtls[0], sizeof(aiMaterial*) * pScene->mNumMaterials); |
889 | if (pScene->mNumMeshes) |
890 | memcpy(pScene->mMeshes, &sib.meshes[0], sizeof(aiMesh*) * pScene->mNumMeshes); |
891 | if (pScene->mNumLights) |
892 | memcpy(pScene->mLights, &sib.lights[0], sizeof(aiLight*) * pScene->mNumLights); |
893 | |
894 | // Construct the root node. |
895 | size_t childIdx = 0; |
896 | aiNode *root = new aiNode(); |
897 | root->mName.Set("<SIBRoot>" ); |
898 | root->mNumChildren = static_cast<unsigned int>(sib.objs.size() + sib.lights.size()); |
899 | root->mChildren = root->mNumChildren ? new aiNode*[root->mNumChildren] : NULL; |
900 | pScene->mRootNode = root; |
901 | |
902 | // Add nodes for each object. |
903 | for (size_t n=0;n<sib.objs.size();n++) |
904 | { |
905 | ai_assert(root->mChildren); |
906 | SIBObject& obj = sib.objs[n]; |
907 | aiNode* node = new aiNode; |
908 | root->mChildren[childIdx++] = node; |
909 | node->mName = obj.name; |
910 | node->mParent = root; |
911 | node->mTransformation = obj.axis; |
912 | |
913 | node->mNumMeshes = static_cast<unsigned int>(obj.meshCount); |
914 | node->mMeshes = node->mNumMeshes ? new unsigned[node->mNumMeshes] : NULL; |
915 | for (unsigned i=0;i<node->mNumMeshes;i++) |
916 | node->mMeshes[i] = static_cast<unsigned int>(obj.meshIdx + i); |
917 | |
918 | // Mark instanced objects as being so. |
919 | if (n >= firstInst) |
920 | { |
921 | node->mMetaData = aiMetadata::Alloc( 1 ); |
922 | node->mMetaData->Set( 0, "IsInstance" , true ); |
923 | } |
924 | } |
925 | |
926 | // Add nodes for each light. |
927 | // (no transformation as the light is already in world space) |
928 | for (size_t n=0;n<sib.lights.size();n++) |
929 | { |
930 | ai_assert(root->mChildren); |
931 | aiLight* light = sib.lights[n]; |
932 | if ( nullptr != light ) { |
933 | aiNode* node = new aiNode; |
934 | root->mChildren[ childIdx++ ] = node; |
935 | node->mName = light->mName; |
936 | node->mParent = root; |
937 | } |
938 | } |
939 | } |
940 | |
941 | #endif // !! ASSIMP_BUILD_NO_SIB_IMPORTER |
942 | |