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 Implementation of the LWO importer class for the older LWOB |
44 | file formats, including materials */ |
45 | |
46 | |
47 | #ifndef ASSIMP_BUILD_NO_LWO_IMPORTER |
48 | |
49 | // Internal headers |
50 | #include "LWOLoader.h" |
51 | using namespace Assimp; |
52 | |
53 | |
54 | // ------------------------------------------------------------------------------------------------ |
55 | void LWOImporter::LoadLWOBFile() |
56 | { |
57 | LE_NCONST uint8_t* const end = mFileBuffer + fileSize; |
58 | bool running = true; |
59 | while (running) |
60 | { |
61 | if (mFileBuffer + sizeof(IFF::ChunkHeader) > end)break; |
62 | const IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer); |
63 | |
64 | if (mFileBuffer + head.length > end) |
65 | { |
66 | throw DeadlyImportError("LWOB: Invalid chunk length" ); |
67 | break; |
68 | } |
69 | uint8_t* const next = mFileBuffer+head.length; |
70 | switch (head.type) |
71 | { |
72 | // vertex list |
73 | case AI_LWO_PNTS: |
74 | { |
75 | if (!mCurLayer->mTempPoints.empty()) |
76 | DefaultLogger::get()->warn("LWO: PNTS chunk encountered twice" ); |
77 | else LoadLWOPoints(head.length); |
78 | break; |
79 | } |
80 | // face list |
81 | case AI_LWO_POLS: |
82 | { |
83 | |
84 | if (!mCurLayer->mFaces.empty()) |
85 | DefaultLogger::get()->warn("LWO: POLS chunk encountered twice" ); |
86 | else LoadLWOBPolygons(head.length); |
87 | break; |
88 | } |
89 | // list of tags |
90 | case AI_LWO_SRFS: |
91 | { |
92 | if (!mTags->empty()) |
93 | DefaultLogger::get()->warn("LWO: SRFS chunk encountered twice" ); |
94 | else LoadLWOTags(head.length); |
95 | break; |
96 | } |
97 | |
98 | // surface chunk |
99 | case AI_LWO_SURF: |
100 | { |
101 | LoadLWOBSurface(head.length); |
102 | break; |
103 | } |
104 | } |
105 | mFileBuffer = next; |
106 | } |
107 | } |
108 | |
109 | // ------------------------------------------------------------------------------------------------ |
110 | void LWOImporter::LoadLWOBPolygons(unsigned int length) |
111 | { |
112 | // first find out how many faces and vertices we'll finally need |
113 | LE_NCONST uint16_t* const end = (LE_NCONST uint16_t*)(mFileBuffer+length); |
114 | LE_NCONST uint16_t* cursor = (LE_NCONST uint16_t*)mFileBuffer; |
115 | |
116 | // perform endianness conversions |
117 | #ifndef AI_BUILD_BIG_ENDIAN |
118 | while (cursor < end)ByteSwap::Swap2(cursor++); |
119 | cursor = (LE_NCONST uint16_t*)mFileBuffer; |
120 | #endif |
121 | |
122 | unsigned int iNumFaces = 0,iNumVertices = 0; |
123 | CountVertsAndFacesLWOB(iNumVertices,iNumFaces,cursor,end); |
124 | |
125 | // allocate the output array and copy face indices |
126 | if (iNumFaces) |
127 | { |
128 | cursor = (LE_NCONST uint16_t*)mFileBuffer; |
129 | |
130 | mCurLayer->mFaces.resize(iNumFaces); |
131 | FaceList::iterator it = mCurLayer->mFaces.begin(); |
132 | CopyFaceIndicesLWOB(it,cursor,end); |
133 | } |
134 | } |
135 | |
136 | // ------------------------------------------------------------------------------------------------ |
137 | void LWOImporter::CountVertsAndFacesLWOB(unsigned int& verts, unsigned int& faces, |
138 | LE_NCONST uint16_t*& cursor, const uint16_t* const end, unsigned int max) |
139 | { |
140 | while (cursor < end && max--) |
141 | { |
142 | uint16_t numIndices; |
143 | // must have 2 shorts left for numIndices and surface |
144 | if (end - cursor < 2) { |
145 | throw DeadlyImportError("LWOB: Unexpected end of file" ); |
146 | } |
147 | ::memcpy(&numIndices, cursor++, 2); |
148 | // must have enough left for indices and surface |
149 | if (end - cursor < (1 + numIndices)) { |
150 | throw DeadlyImportError("LWOB: Unexpected end of file" ); |
151 | } |
152 | verts += numIndices; |
153 | faces++; |
154 | cursor += numIndices; |
155 | int16_t surface; |
156 | ::memcpy(&surface, cursor++, 2); |
157 | if (surface < 0) |
158 | { |
159 | // there are detail polygons |
160 | ::memcpy(&numIndices, cursor++, 2); |
161 | CountVertsAndFacesLWOB(verts,faces,cursor,end,numIndices); |
162 | } |
163 | } |
164 | } |
165 | |
166 | // ------------------------------------------------------------------------------------------------ |
167 | void LWOImporter::CopyFaceIndicesLWOB(FaceList::iterator& it, |
168 | LE_NCONST uint16_t*& cursor, |
169 | const uint16_t* const end, |
170 | unsigned int max) |
171 | { |
172 | while (cursor < end && max--) |
173 | { |
174 | LWO::Face& face = *it;++it; |
175 | uint16_t numIndices; |
176 | ::memcpy(&numIndices, cursor++, 2); |
177 | face.mNumIndices = numIndices; |
178 | if(face.mNumIndices) |
179 | { |
180 | if (cursor + face.mNumIndices >= end) |
181 | { |
182 | break; |
183 | } |
184 | face.mIndices = new unsigned int[face.mNumIndices]; |
185 | for (unsigned int i = 0; i < face.mNumIndices;++i) |
186 | { |
187 | unsigned int & mi = face.mIndices[i]; |
188 | uint16_t index; |
189 | ::memcpy(&index, cursor++, 2); |
190 | mi = index; |
191 | if (mi > mCurLayer->mTempPoints.size()) |
192 | { |
193 | DefaultLogger::get()->warn("LWOB: face index is out of range" ); |
194 | mi = (unsigned int)mCurLayer->mTempPoints.size()-1; |
195 | } |
196 | } |
197 | } |
198 | else DefaultLogger::get()->warn("LWOB: Face has 0 indices" ); |
199 | int16_t surface; |
200 | ::memcpy(&surface, cursor++, 2); |
201 | if (surface < 0) |
202 | { |
203 | surface = -surface; |
204 | |
205 | // there are detail polygons. |
206 | uint16_t numPolygons; |
207 | ::memcpy(&numPolygons, cursor++, 2); |
208 | if (cursor < end) |
209 | { |
210 | CopyFaceIndicesLWOB(it,cursor,end,numPolygons); |
211 | } |
212 | } |
213 | face.surfaceIndex = surface-1; |
214 | } |
215 | } |
216 | |
217 | // ------------------------------------------------------------------------------------------------ |
218 | LWO::Texture* LWOImporter::SetupNewTextureLWOB(LWO::TextureList& list,unsigned int size) |
219 | { |
220 | list.push_back(LWO::Texture()); |
221 | LWO::Texture* tex = &list.back(); |
222 | |
223 | std::string type; |
224 | GetS0(type,size); |
225 | const char* s = type.c_str(); |
226 | |
227 | if(strstr(s, "Image Map" )) |
228 | { |
229 | // Determine mapping type |
230 | if(strstr(s, "Planar" )) |
231 | tex->mapMode = LWO::Texture::Planar; |
232 | else if(strstr(s, "Cylindrical" )) |
233 | tex->mapMode = LWO::Texture::Cylindrical; |
234 | else if(strstr(s, "Spherical" )) |
235 | tex->mapMode = LWO::Texture::Spherical; |
236 | else if(strstr(s, "Cubic" )) |
237 | tex->mapMode = LWO::Texture::Cubic; |
238 | else if(strstr(s, "Front" )) |
239 | tex->mapMode = LWO::Texture::FrontProjection; |
240 | } |
241 | else |
242 | { |
243 | // procedural or gradient, not supported |
244 | DefaultLogger::get()->error("LWOB: Unsupported legacy texture: " + type); |
245 | } |
246 | |
247 | return tex; |
248 | } |
249 | |
250 | // ------------------------------------------------------------------------------------------------ |
251 | void LWOImporter::LoadLWOBSurface(unsigned int size) |
252 | { |
253 | LE_NCONST uint8_t* const end = mFileBuffer + size; |
254 | |
255 | mSurfaces->push_back( LWO::Surface () ); |
256 | LWO::Surface& surf = mSurfaces->back(); |
257 | LWO::Texture* pTex = NULL; |
258 | |
259 | GetS0(surf.mName,size); |
260 | bool running = true; |
261 | while (running) { |
262 | if (mFileBuffer + 6 >= end) |
263 | break; |
264 | |
265 | IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer); |
266 | |
267 | /* A single test file (sonycam.lwo) seems to have invalid surface chunks. |
268 | * I'm assuming it's the fault of a single, unknown exporter so there are |
269 | * probably THOUSANDS of them. Here's a dirty workaround: |
270 | * |
271 | * We don't break if the chunk limit is exceeded. Instead, we're computing |
272 | * how much storage is actually left and work with this value from now on. |
273 | */ |
274 | if (mFileBuffer + head.length > end) { |
275 | DefaultLogger::get()->error("LWOB: Invalid surface chunk length. Trying to continue." ); |
276 | head.length = (uint16_t) (end - mFileBuffer); |
277 | } |
278 | |
279 | uint8_t* const next = mFileBuffer+head.length; |
280 | switch (head.type) |
281 | { |
282 | // diffuse color |
283 | case AI_LWO_COLR: |
284 | { |
285 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,COLR,3); |
286 | surf.mColor.r = GetU1() / 255.0f; |
287 | surf.mColor.g = GetU1() / 255.0f; |
288 | surf.mColor.b = GetU1() / 255.0f; |
289 | break; |
290 | } |
291 | // diffuse strength ... |
292 | case AI_LWO_DIFF: |
293 | { |
294 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,DIFF,2); |
295 | surf.mDiffuseValue = GetU2() / 255.0f; |
296 | break; |
297 | } |
298 | // specular strength ... |
299 | case AI_LWO_SPEC: |
300 | { |
301 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SPEC,2); |
302 | surf.mSpecularValue = GetU2() / 255.0f; |
303 | break; |
304 | } |
305 | // luminosity ... |
306 | case AI_LWO_LUMI: |
307 | { |
308 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,LUMI,2); |
309 | surf.mLuminosity = GetU2() / 255.0f; |
310 | break; |
311 | } |
312 | // transparency |
313 | case AI_LWO_TRAN: |
314 | { |
315 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TRAN,2); |
316 | surf.mTransparency = GetU2() / 255.0f; |
317 | break; |
318 | } |
319 | // surface flags |
320 | case AI_LWO_FLAG: |
321 | { |
322 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,FLAG,2); |
323 | uint16_t flag = GetU2(); |
324 | if (flag & 0x4 ) surf.mMaximumSmoothAngle = 1.56207f; |
325 | if (flag & 0x8 ) surf.mColorHighlights = 1.f; |
326 | if (flag & 0x100) surf.bDoubleSided = true; |
327 | break; |
328 | } |
329 | // maximum smoothing angle |
330 | case AI_LWO_SMAN: |
331 | { |
332 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SMAN,4); |
333 | surf.mMaximumSmoothAngle = std::fabs( GetF4() ); |
334 | break; |
335 | } |
336 | // glossiness |
337 | case AI_LWO_GLOS: |
338 | { |
339 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,GLOS,2); |
340 | surf.mGlossiness = (float)GetU2(); |
341 | break; |
342 | } |
343 | // color texture |
344 | case AI_LWO_CTEX: |
345 | { |
346 | pTex = SetupNewTextureLWOB(surf.mColorTextures, |
347 | head.length); |
348 | break; |
349 | } |
350 | // diffuse texture |
351 | case AI_LWO_DTEX: |
352 | { |
353 | pTex = SetupNewTextureLWOB(surf.mDiffuseTextures, |
354 | head.length); |
355 | break; |
356 | } |
357 | // specular texture |
358 | case AI_LWO_STEX: |
359 | { |
360 | pTex = SetupNewTextureLWOB(surf.mSpecularTextures, |
361 | head.length); |
362 | break; |
363 | } |
364 | // bump texture |
365 | case AI_LWO_BTEX: |
366 | { |
367 | pTex = SetupNewTextureLWOB(surf.mBumpTextures, |
368 | head.length); |
369 | break; |
370 | } |
371 | // transparency texture |
372 | case AI_LWO_TTEX: |
373 | { |
374 | pTex = SetupNewTextureLWOB(surf.mOpacityTextures, |
375 | head.length); |
376 | break; |
377 | } |
378 | // texture path |
379 | case AI_LWO_TIMG: |
380 | { |
381 | if (pTex) { |
382 | GetS0(pTex->mFileName,head.length); |
383 | } |
384 | else DefaultLogger::get()->warn("LWOB: Unexpected TIMG chunk" ); |
385 | break; |
386 | } |
387 | // texture strength |
388 | case AI_LWO_TVAL: |
389 | { |
390 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TVAL,1); |
391 | if (pTex) { |
392 | pTex->mStrength = (float)GetU1()/ 255.f; |
393 | } |
394 | else DefaultLogger::get()->warn("LWOB: Unexpected TVAL chunk" ); |
395 | break; |
396 | } |
397 | // texture flags |
398 | case AI_LWO_TFLG: |
399 | { |
400 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TFLG,2); |
401 | |
402 | if (pTex) |
403 | { |
404 | const uint16_t s = GetU2(); |
405 | if (s & 1) |
406 | pTex->majorAxis = LWO::Texture::AXIS_X; |
407 | else if (s & 2) |
408 | pTex->majorAxis = LWO::Texture::AXIS_Y; |
409 | else if (s & 4) |
410 | pTex->majorAxis = LWO::Texture::AXIS_Z; |
411 | |
412 | if (s & 16) |
413 | DefaultLogger::get()->warn("LWOB: Ignoring \'negate\' flag on texture" ); |
414 | } |
415 | else DefaultLogger::get()->warn("LWOB: Unexpected TFLG chunk" ); |
416 | break; |
417 | } |
418 | } |
419 | mFileBuffer = next; |
420 | } |
421 | } |
422 | |
423 | #endif // !! ASSIMP_BUILD_NO_LWO_IMPORTER |
424 | |