/src/assimp/code/AssetLib/LWO/LWOBLoader.cpp
Line | Count | Source |
1 | | /* |
2 | | --------------------------------------------------------------------------- |
3 | | Open Asset Import Library (assimp) |
4 | | --------------------------------------------------------------------------- |
5 | | |
6 | | Copyright (c) 2006-2026, assimp team |
7 | | |
8 | | |
9 | | |
10 | | All rights reserved. |
11 | | |
12 | | Redistribution and use of this software in source and binary forms, |
13 | | with or without modification, are permitted provided that the following |
14 | | conditions are met: |
15 | | |
16 | | * Redistributions of source code must retain the above |
17 | | copyright notice, this list of conditions and the |
18 | | following disclaimer. |
19 | | |
20 | | * Redistributions in binary form must reproduce the above |
21 | | copyright notice, this list of conditions and the |
22 | | following disclaimer in the documentation and/or other |
23 | | materials provided with the distribution. |
24 | | |
25 | | * Neither the name of the assimp team, nor the names of its |
26 | | contributors may be used to endorse or promote products |
27 | | derived from this software without specific prior |
28 | | written permission of the assimp team. |
29 | | |
30 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
31 | | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
32 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
33 | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
34 | | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
35 | | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
36 | | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
37 | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
38 | | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
39 | | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
40 | | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
41 | | --------------------------------------------------------------------------- |
42 | | */ |
43 | | |
44 | | /** @file Implementation of the LWO importer class for the older LWOB |
45 | | file formats, including materials */ |
46 | | |
47 | | |
48 | | #ifndef ASSIMP_BUILD_NO_LWO_IMPORTER |
49 | | |
50 | | // Internal headers |
51 | | #include "LWOLoader.h" |
52 | | using namespace Assimp; |
53 | | |
54 | | // ------------------------------------------------------------------------------------------------ |
55 | 607 | void LWOImporter::LoadLWOBFile() { |
56 | 607 | LE_NCONST uint8_t* const end = mFileBuffer + fileSize; |
57 | 607 | bool running = true; |
58 | 2.83k | while (running) { |
59 | 2.83k | if (mFileBuffer + sizeof(IFF::ChunkHeader) > end) |
60 | 431 | break; |
61 | 2.40k | const IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer); |
62 | | |
63 | 2.40k | if (mFileBuffer + head.length > end) { |
64 | 133 | throw DeadlyImportError("LWOB: Invalid chunk length"); |
65 | 133 | } |
66 | 2.27k | uint8_t* const next = mFileBuffer+head.length; |
67 | 2.27k | switch (head.type) { |
68 | | // vertex list |
69 | 537 | case AI_LWO_PNTS: { |
70 | 537 | if (!mCurLayer->mTempPoints.empty()) |
71 | 537 | ASSIMP_LOG_WARN("LWO: PNTS chunk encountered twice"); |
72 | 530 | else |
73 | 530 | LoadLWOPoints(head.length); |
74 | 537 | } break; |
75 | 519 | case AI_LWO_POLS: { // face list |
76 | 519 | if (!mCurLayer->mFaces.empty()) |
77 | 519 | ASSIMP_LOG_WARN("LWO: POLS chunk encountered twice"); |
78 | 514 | else |
79 | 514 | LoadLWOBPolygons(head.length); |
80 | 519 | } break; |
81 | | |
82 | 586 | case AI_LWO_SRFS: // list of tags |
83 | 586 | { |
84 | 586 | if (!mTags->empty()) |
85 | 586 | ASSIMP_LOG_WARN("LWO: SRFS chunk encountered twice"); |
86 | 579 | else |
87 | 579 | LoadLWOTags(head.length); |
88 | 586 | } break; |
89 | | |
90 | 446 | case AI_LWO_SURF: // surface chunk |
91 | 446 | { |
92 | 446 | LoadLWOBSurface(head.length); |
93 | 446 | } break; |
94 | | |
95 | 185 | default: |
96 | 185 | break; |
97 | 2.27k | } |
98 | 2.23k | mFileBuffer = next; |
99 | 2.23k | } |
100 | 607 | } |
101 | | |
102 | | // ------------------------------------------------------------------------------------------------ |
103 | 514 | void LWOImporter::LoadLWOBPolygons(unsigned int length) { |
104 | | // first find out how many faces and vertices we'll finally need |
105 | 514 | LE_NCONST uint16_t* const end = (LE_NCONST uint16_t*)(mFileBuffer+length); |
106 | 514 | LE_NCONST uint16_t* cursor = (LE_NCONST uint16_t*)mFileBuffer; |
107 | | |
108 | | // perform endianness conversions |
109 | 514 | #ifndef AI_BUILD_BIG_ENDIAN |
110 | 77.6k | while (cursor < end)ByteSwap::Swap2(cursor++); |
111 | 514 | cursor = (LE_NCONST uint16_t*)mFileBuffer; |
112 | 514 | #endif |
113 | | |
114 | 514 | unsigned int iNumFaces = 0,iNumVertices = 0; |
115 | 514 | CountVertsAndFacesLWOB(iNumVertices,iNumFaces,cursor,end); |
116 | | |
117 | | // allocate the output array and copy face indices |
118 | 514 | if (iNumFaces) { |
119 | 487 | cursor = (LE_NCONST uint16_t*)mFileBuffer; |
120 | | |
121 | 487 | mCurLayer->mFaces.resize(iNumFaces); |
122 | 487 | FaceList::iterator it = mCurLayer->mFaces.begin(); |
123 | 487 | CopyFaceIndicesLWOB(it,cursor,end); |
124 | 487 | } |
125 | 514 | } |
126 | | |
127 | | // ------------------------------------------------------------------------------------------------ |
128 | | void LWOImporter::CountVertsAndFacesLWOB(unsigned int& verts, unsigned int& faces, |
129 | 539 | LE_NCONST uint16_t*& cursor, const uint16_t* const end, unsigned int max) { |
130 | 11.3k | while (cursor < end && max--) { |
131 | 10.8k | uint16_t numIndices; |
132 | | // must have 2 shorts left for numIndices and surface |
133 | 10.8k | if (end - cursor < 2) { |
134 | 3 | throw DeadlyImportError("LWOB: Unexpected end of file"); |
135 | 3 | } |
136 | 10.8k | ::memcpy(&numIndices, cursor++, 2); |
137 | | // must have enough left for indices and surface |
138 | 10.8k | if (end - cursor < (1 + numIndices)) { |
139 | 22 | throw DeadlyImportError("LWOB: Unexpected end of file"); |
140 | 22 | } |
141 | 10.8k | verts += numIndices; |
142 | 10.8k | faces++; |
143 | 10.8k | cursor += numIndices; |
144 | 10.8k | int16_t surface; |
145 | 10.8k | ::memcpy(&surface, cursor++, 2); |
146 | 10.8k | if (surface < 0) { |
147 | | // there are detail polygons |
148 | 25 | ::memcpy(&numIndices, cursor++, 2); |
149 | 25 | CountVertsAndFacesLWOB(verts,faces,cursor,end,numIndices); |
150 | 25 | } |
151 | 10.8k | } |
152 | 539 | } |
153 | | |
154 | | // ------------------------------------------------------------------------------------------------ |
155 | | void LWOImporter::CopyFaceIndicesLWOB(FaceList::iterator &it, |
156 | | LE_NCONST uint16_t*& cursor, |
157 | | const uint16_t* const end, |
158 | 502 | unsigned int max) { |
159 | 10.1k | while (cursor < end && max--) { |
160 | 9.67k | LWO::Face& face = *it;++it; |
161 | 9.67k | uint16_t numIndices; |
162 | 9.67k | ::memcpy(&numIndices, cursor++, 2); |
163 | 9.67k | face.mNumIndices = numIndices; |
164 | 9.67k | if(face.mNumIndices) { |
165 | 9.54k | if (cursor + face.mNumIndices >= end) { |
166 | 0 | break; |
167 | 0 | } |
168 | 9.54k | face.mIndices = new unsigned int[face.mNumIndices]; |
169 | 56.8k | for (unsigned int i = 0; i < face.mNumIndices;++i) { |
170 | 47.3k | unsigned int & mi = face.mIndices[i]; |
171 | 47.3k | uint16_t index; |
172 | 47.3k | ::memcpy(&index, cursor++, 2); |
173 | 47.3k | mi = index; |
174 | 47.3k | if (mi >= mCurLayer->mTempPoints.size()) { |
175 | 1.20k | ASSIMP_LOG_WARN("LWOB: face index is out of range"); |
176 | 1.20k | mi = mCurLayer->mTempPoints.empty() ? 0 : (unsigned int)(mCurLayer->mTempPoints.size() - 1); |
177 | 1.20k | } |
178 | 47.3k | } |
179 | 9.54k | } else { |
180 | 130 | ASSIMP_LOG_WARN("LWOB: Face has 0 indices"); |
181 | 130 | } |
182 | 9.67k | int16_t surface; |
183 | 9.67k | ::memcpy(&surface, cursor++, 2); |
184 | 9.67k | if (surface < 0) { |
185 | 19 | surface = -surface; |
186 | | |
187 | | // there are detail polygons. |
188 | 19 | uint16_t numPolygons; |
189 | 19 | ::memcpy(&numPolygons, cursor++, 2); |
190 | 19 | if (cursor < end) { |
191 | 15 | CopyFaceIndicesLWOB(it,cursor,end,numPolygons); |
192 | 15 | } |
193 | 19 | } |
194 | 9.67k | face.surfaceIndex = surface-1; |
195 | 9.67k | } |
196 | 502 | } |
197 | | |
198 | | // ------------------------------------------------------------------------------------------------ |
199 | 85 | LWO::Texture* LWOImporter::SetupNewTextureLWOB(LWO::TextureList& list,unsigned int size) { |
200 | 85 | list.emplace_back(); |
201 | 85 | LWO::Texture* tex = &list.back(); |
202 | | |
203 | 85 | std::string type; |
204 | 85 | GetS0(type,size); |
205 | 85 | const char* s = type.c_str(); |
206 | | |
207 | 85 | if(strstr(s, "Image Map")) { |
208 | | // Determine mapping type |
209 | 74 | if(strstr(s, "Planar")) |
210 | 0 | tex->mapMode = LWO::Texture::Planar; |
211 | 74 | else if(strstr(s, "Cylindrical")) |
212 | 69 | tex->mapMode = LWO::Texture::Cylindrical; |
213 | 5 | else if(strstr(s, "Spherical")) |
214 | 0 | tex->mapMode = LWO::Texture::Spherical; |
215 | 5 | else if(strstr(s, "Cubic")) |
216 | 0 | tex->mapMode = LWO::Texture::Cubic; |
217 | 5 | else if(strstr(s, "Front")) |
218 | 0 | tex->mapMode = LWO::Texture::FrontProjection; |
219 | 74 | } else { |
220 | | // procedural or gradient, not supported |
221 | 11 | ASSIMP_LOG_ERROR("LWOB: Unsupported legacy texture: ", type); |
222 | 11 | } |
223 | | |
224 | 85 | return tex; |
225 | 85 | } |
226 | | |
227 | | // ------------------------------------------------------------------------------------------------ |
228 | 446 | void LWOImporter::LoadLWOBSurface(unsigned int size) { |
229 | 446 | LE_NCONST uint8_t* const end = mFileBuffer + size; |
230 | | |
231 | 446 | mSurfaces->push_back( LWO::Surface () ); |
232 | 446 | LWO::Surface& surf = mSurfaces->back(); |
233 | 446 | LWO::Texture *pTex = nullptr; |
234 | | |
235 | 446 | GetS0(surf.mName,size); |
236 | 446 | bool running = true; |
237 | 6.28k | while (running) { |
238 | 6.28k | if (mFileBuffer + 6 >= end) |
239 | 430 | break; |
240 | | |
241 | 5.85k | IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer); |
242 | | |
243 | | /* A single test file (sonycam.lwo) seems to have invalid surface chunks. |
244 | | * I'm assuming it's the fault of a single, unknown exporter so there are |
245 | | * probably THOUSANDS of them. Here's a dirty workaround: |
246 | | * |
247 | | * We don't break if the chunk limit is exceeded. Instead, we're computing |
248 | | * how much storage is actually left and work with this value from now on. |
249 | | */ |
250 | 5.85k | if (mFileBuffer + head.length > end) { |
251 | 294 | ASSIMP_LOG_ERROR("LWOB: Invalid surface chunk length. Trying to continue."); |
252 | 294 | head.length = (uint16_t) (end - mFileBuffer); |
253 | 294 | } |
254 | | |
255 | 5.85k | uint8_t* const next = mFileBuffer+head.length; |
256 | 5.85k | switch (head.type) { |
257 | | // diffuse color |
258 | 419 | case AI_LWO_COLR: |
259 | 419 | { |
260 | 419 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,COLR,3); |
261 | 417 | surf.mColor.r = GetU1() / 255.0f; |
262 | 417 | surf.mColor.g = GetU1() / 255.0f; |
263 | 417 | surf.mColor.b = GetU1() / 255.0f; |
264 | 417 | break; |
265 | 419 | } |
266 | | // diffuse strength ... |
267 | 340 | case AI_LWO_DIFF: |
268 | 340 | { |
269 | 340 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,DIFF,2); |
270 | 338 | surf.mDiffuseValue = GetU2() / 255.0f; |
271 | 338 | break; |
272 | 340 | } |
273 | | // specular strength ... |
274 | 330 | case AI_LWO_SPEC: |
275 | 330 | { |
276 | 330 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SPEC,2); |
277 | 328 | surf.mSpecularValue = GetU2() / 255.0f; |
278 | 328 | break; |
279 | 330 | } |
280 | | // luminosity ... |
281 | 322 | case AI_LWO_LUMI: |
282 | 322 | { |
283 | 322 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,LUMI,2); |
284 | 321 | surf.mLuminosity = GetU2() / 255.0f; |
285 | 321 | break; |
286 | 322 | } |
287 | | // transparency |
288 | 314 | case AI_LWO_TRAN: |
289 | 314 | { |
290 | 314 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TRAN,2); |
291 | 312 | surf.mTransparency = GetU2() / 255.0f; |
292 | 312 | break; |
293 | 314 | } |
294 | | // surface flags |
295 | 401 | case AI_LWO_FLAG: |
296 | 401 | { |
297 | 401 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,FLAG,2); |
298 | 399 | uint16_t flag = GetU2(); |
299 | 399 | if (flag & 0x4 ) surf.mMaximumSmoothAngle = 1.56207f; |
300 | 399 | if (flag & 0x8 ) surf.mColorHighlights = 1.f; |
301 | 399 | if (flag & 0x100) surf.bDoubleSided = true; |
302 | 399 | break; |
303 | 401 | } |
304 | | // maximum smoothing angle |
305 | 103 | case AI_LWO_SMAN: |
306 | 103 | { |
307 | 103 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SMAN,4); |
308 | 102 | surf.mMaximumSmoothAngle = std::fabs( GetF4() ); |
309 | 102 | break; |
310 | 103 | } |
311 | | // glossiness |
312 | 218 | case AI_LWO_GLOS: |
313 | 218 | { |
314 | 218 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,GLOS,2); |
315 | 216 | surf.mGlossiness = (float)GetU2(); |
316 | 216 | break; |
317 | 218 | } |
318 | | // color texture |
319 | 75 | case AI_LWO_CTEX: |
320 | 75 | { |
321 | 75 | pTex = SetupNewTextureLWOB(surf.mColorTextures, |
322 | 75 | head.length); |
323 | 75 | break; |
324 | 218 | } |
325 | | // diffuse texture |
326 | 2 | case AI_LWO_DTEX: |
327 | 2 | { |
328 | 2 | pTex = SetupNewTextureLWOB(surf.mDiffuseTextures, |
329 | 2 | head.length); |
330 | 2 | break; |
331 | 218 | } |
332 | | // specular texture |
333 | 2 | case AI_LWO_STEX: |
334 | 2 | { |
335 | 2 | pTex = SetupNewTextureLWOB(surf.mSpecularTextures, |
336 | 2 | head.length); |
337 | 2 | break; |
338 | 218 | } |
339 | | // bump texture |
340 | 2 | case AI_LWO_BTEX: |
341 | 2 | { |
342 | 2 | pTex = SetupNewTextureLWOB(surf.mBumpTextures, |
343 | 2 | head.length); |
344 | 2 | break; |
345 | 218 | } |
346 | | // transparency texture |
347 | 4 | case AI_LWO_TTEX: |
348 | 4 | { |
349 | 4 | pTex = SetupNewTextureLWOB(surf.mOpacityTextures, |
350 | 4 | head.length); |
351 | 4 | break; |
352 | 218 | } |
353 | | // texture path |
354 | 47 | case AI_LWO_TIMG: |
355 | 47 | { |
356 | 47 | if (pTex) { |
357 | 46 | GetS0(pTex->mFileName,head.length); |
358 | 46 | } else { |
359 | 1 | ASSIMP_LOG_WARN("LWOB: Unexpected TIMG chunk"); |
360 | 1 | } |
361 | 47 | break; |
362 | 218 | } |
363 | | // texture strength |
364 | 1 | case AI_LWO_TVAL: |
365 | 1 | { |
366 | 1 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TVAL,1); |
367 | 1 | if (pTex) { |
368 | 1 | pTex->mStrength = (float)GetU1()/ 255.f; |
369 | 1 | } else { |
370 | 0 | ASSIMP_LOG_ERROR("LWOB: Unexpected TVAL chunk"); |
371 | 0 | } |
372 | 1 | break; |
373 | 1 | } |
374 | | // texture flags |
375 | 71 | case AI_LWO_TFLG: |
376 | 71 | { |
377 | 71 | AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TFLG,2); |
378 | | |
379 | 69 | if (nullptr != pTex) { |
380 | 67 | const uint16_t s = GetU2(); |
381 | 67 | if (s & 1) |
382 | 3 | pTex->majorAxis = LWO::Texture::AXIS_X; |
383 | 64 | else if (s & 2) |
384 | 2 | pTex->majorAxis = LWO::Texture::AXIS_Y; |
385 | 62 | else if (s & 4) |
386 | 60 | pTex->majorAxis = LWO::Texture::AXIS_Z; |
387 | | |
388 | 67 | if (s & 16) { |
389 | 2 | ASSIMP_LOG_WARN("LWOB: Ignoring \'negate\' flag on texture"); |
390 | 2 | } |
391 | 67 | } |
392 | 2 | else { |
393 | 2 | ASSIMP_LOG_WARN("LWOB: Unexpected TFLG chunk"); |
394 | 2 | } |
395 | 69 | break; |
396 | 71 | } |
397 | 5.85k | } |
398 | 5.83k | mFileBuffer = next; |
399 | 5.83k | } |
400 | 446 | } |
401 | | |
402 | | #endif // !! ASSIMP_BUILD_NO_LWO_IMPORTER |