/src/assimp/code/AssetLib/COB/COBLoader.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 | | |
42 | | /** @file COBLoader.cpp |
43 | | * @brief Implementation of the TrueSpace COB/SCN importer class. |
44 | | */ |
45 | | |
46 | | #ifndef ASSIMP_BUILD_NO_COB_IMPORTER |
47 | | |
48 | | #include "COBLoader.h" |
49 | | #include "COBScene.h" |
50 | | #include "PostProcessing/ConvertToLHProcess.h" |
51 | | |
52 | | #include <assimp/LineSplitter.h> |
53 | | #include <assimp/ParsingUtils.h> |
54 | | #include <assimp/StreamReader.h> |
55 | | #include <assimp/TinyFormatter.h> |
56 | | #include <assimp/fast_atof.h> |
57 | | #include <assimp/importerdesc.h> |
58 | | #include <assimp/scene.h> |
59 | | #include <assimp/DefaultLogger.hpp> |
60 | | #include <assimp/IOSystem.hpp> |
61 | | |
62 | | #include <memory> |
63 | | |
64 | | namespace Assimp { |
65 | | |
66 | | using namespace Assimp::COB; |
67 | | using namespace Assimp::Formatter; |
68 | | |
69 | | static constexpr float units[] = { |
70 | | 1000.f, |
71 | | 100.f, |
72 | | 1.f, |
73 | | 0.001f, |
74 | | 1.f / 0.0254f, |
75 | | 1.f / 0.3048f, |
76 | | 1.f / 0.9144f, |
77 | | 1.f / 1609.344f |
78 | | }; |
79 | | |
80 | | static constexpr aiImporterDesc desc = { |
81 | | "TrueSpace Object Importer", |
82 | | "", |
83 | | "", |
84 | | "little-endian files only", |
85 | | aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour, |
86 | | 0, |
87 | | 0, |
88 | | 0, |
89 | | 0, |
90 | | "cob scn" |
91 | | }; |
92 | | |
93 | | // ------------------------------------------------------------------------------------------------ |
94 | | // Returns whether the class can handle the format of the given file. |
95 | 0 | bool COBImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { |
96 | 0 | static const char *tokens[] = { "Caligary" }; |
97 | 0 | return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); |
98 | 0 | } |
99 | | |
100 | | // ------------------------------------------------------------------------------------------------ |
101 | | // Loader meta information |
102 | 38.0k | const aiImporterDesc *COBImporter::GetInfo() const { |
103 | 38.0k | return &desc; |
104 | 38.0k | } |
105 | | |
106 | | // ------------------------------------------------------------------------------------------------ |
107 | | // Setup configuration properties for the loader |
108 | 0 | void COBImporter::SetupProperties(const Importer * /*pImp*/) { |
109 | | // nothing to be done for the moment |
110 | 0 | } |
111 | | |
112 | | // ------------------------------------------------------------------------------------------------ |
113 | 0 | AI_WONT_RETURN void COBImporter::ThrowException(const std::string &msg) { |
114 | 0 | throw DeadlyImportError("COB: ", msg); |
115 | 0 | } |
116 | | |
117 | | // ------------------------------------------------------------------------------------------------ |
118 | 0 | static bool isValidASCIIHeader(const char *head) { |
119 | 0 | ai_assert(head != nullptr); |
120 | |
|
121 | 0 | if (strncmp(head, "Caligari ", 9) != 0) { |
122 | 0 | COBImporter::ThrowException("Could not found magic id: `Caligari`"); |
123 | 0 | } |
124 | | |
125 | 0 | if (strncmp(&head[9], "V00.", 4) != 0) { |
126 | 0 | COBImporter::ThrowException("Could not found Version tag: `V00.`"); |
127 | 0 | } |
128 | 0 | ASSIMP_LOG_INFO("File format tag: ", std::string(head + 9, 6)); |
129 | 0 | if (head[16] != 'L') { |
130 | 0 | COBImporter::ThrowException("File is big-endian, which is not supported"); |
131 | 0 | } |
132 | | |
133 | 0 | return true; |
134 | 0 | } |
135 | | |
136 | | // ------------------------------------------------------------------------------------------------ |
137 | | // Imports the given file into the given scene structure. |
138 | 0 | void COBImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { |
139 | 0 | COB::Scene scene; |
140 | |
|
141 | 0 | auto file = pIOHandler->Open(pFile, "rb"); |
142 | 0 | if (!file) { |
143 | 0 | ThrowException("Could not open " + pFile); |
144 | 0 | } |
145 | | |
146 | 0 | std::unique_ptr<StreamReaderLE> stream(new StreamReaderLE(file)); |
147 | | |
148 | | // check header |
149 | 0 | static constexpr size_t HeaderSize = 32u; |
150 | 0 | char head[HeaderSize] = {}; |
151 | 0 | stream->CopyAndAdvance(head, HeaderSize); |
152 | | |
153 | | // load data into intermediate structures |
154 | 0 | if (head[15] == 'A') { |
155 | 0 | if (!isValidASCIIHeader(head)) { |
156 | 0 | ThrowException("Invalid ASCII file header"); |
157 | 0 | } |
158 | 0 | ReadAsciiFile(scene, stream.get()); |
159 | 0 | } else { |
160 | 0 | ReadBinaryFile(scene, stream.get()); |
161 | 0 | } |
162 | 0 | if (scene.nodes.empty()) { |
163 | 0 | ThrowException("No nodes loaded"); |
164 | 0 | } |
165 | | |
166 | | // sort faces by material indices |
167 | 0 | for (std::shared_ptr<Node> &n : scene.nodes) { |
168 | 0 | if (n->type == Node::TYPE_MESH) { |
169 | 0 | Mesh &mesh = (Mesh &)(*n); |
170 | 0 | for (Face &f : mesh.faces) { |
171 | 0 | mesh.temp_map[f.material].push_back(&f); |
172 | 0 | } |
173 | 0 | } |
174 | 0 | } |
175 | | |
176 | | // count meshes |
177 | 0 | for (std::shared_ptr<Node> &n : scene.nodes) { |
178 | 0 | if (n->type == Node::TYPE_MESH) { |
179 | 0 | Mesh &mesh = (Mesh &)(*n); |
180 | 0 | if (mesh.vertex_positions.size() && mesh.texture_coords.size()) { |
181 | 0 | pScene->mNumMeshes += static_cast<unsigned int>(mesh.temp_map.size()); |
182 | 0 | } |
183 | 0 | } |
184 | 0 | } |
185 | 0 | pScene->mMeshes = new aiMesh *[pScene->mNumMeshes](); |
186 | 0 | pScene->mMaterials = new aiMaterial *[pScene->mNumMeshes](); |
187 | 0 | pScene->mNumMeshes = 0; |
188 | | |
189 | | // count lights and cameras |
190 | 0 | for (std::shared_ptr<Node> &n : scene.nodes) { |
191 | 0 | if (n->type == Node::TYPE_LIGHT) { |
192 | 0 | ++pScene->mNumLights; |
193 | 0 | } else if (n->type == Node::TYPE_CAMERA) { |
194 | 0 | ++pScene->mNumCameras; |
195 | 0 | } |
196 | 0 | } |
197 | |
|
198 | 0 | if (pScene->mNumLights) { |
199 | 0 | pScene->mLights = new aiLight *[pScene->mNumLights](); |
200 | 0 | } |
201 | 0 | if (pScene->mNumCameras) { |
202 | 0 | pScene->mCameras = new aiCamera *[pScene->mNumCameras](); |
203 | 0 | } |
204 | 0 | pScene->mNumLights = pScene->mNumCameras = 0; |
205 | | |
206 | | // resolve parents by their IDs and build the output graph |
207 | 0 | std::unique_ptr<Node> root(new Group()); |
208 | 0 | for (size_t n = 0; n < scene.nodes.size(); ++n) { |
209 | 0 | const Node &nn = *scene.nodes[n].get(); |
210 | 0 | if (nn.parent_id == 0) { |
211 | 0 | root->temp_children.push_back(&nn); |
212 | 0 | } |
213 | |
|
214 | 0 | for (size_t m = n; m < scene.nodes.size(); ++m) { |
215 | 0 | const Node &mm = *scene.nodes[m].get(); |
216 | 0 | if (mm.parent_id == nn.id) { |
217 | 0 | nn.temp_children.push_back(&mm); |
218 | 0 | } |
219 | 0 | } |
220 | 0 | } |
221 | |
|
222 | 0 | pScene->mRootNode = BuildNodes(*root, scene, pScene); |
223 | | //flip normals after import |
224 | 0 | FlipWindingOrderProcess flip; |
225 | 0 | flip.Execute(pScene); |
226 | 0 | } |
227 | | |
228 | | // ------------------------------------------------------------------------------------------------ |
229 | 0 | void ConvertTexture(const std::shared_ptr<Texture> &tex, aiMaterial *out, aiTextureType type) { |
230 | 0 | const aiString path(tex->path); |
231 | 0 | out->AddProperty(&path, AI_MATKEY_TEXTURE(type, 0)); |
232 | 0 | out->AddProperty(&tex->transform, 1, AI_MATKEY_UVTRANSFORM(type, 0)); |
233 | 0 | } |
234 | | |
235 | | // ------------------------------------------------------------------------------------------------ |
236 | 0 | aiNode *COBImporter::BuildNodes(const Node &root, const Scene &scin, aiScene *fill) { |
237 | 0 | aiNode *nd = new aiNode(); |
238 | 0 | nd->mName.Set(root.name); |
239 | 0 | nd->mTransformation = root.transform; |
240 | | |
241 | | // Note to everybody believing Voodoo is appropriate here: |
242 | | // I know polymorphism, run as fast as you can ;-) |
243 | 0 | if (Node::TYPE_MESH == root.type) { |
244 | 0 | const Mesh &ndmesh = (const Mesh &)(root); |
245 | 0 | if (ndmesh.vertex_positions.size() && ndmesh.texture_coords.size()) { |
246 | |
|
247 | 0 | using Entry = std::pair<const unsigned int, Mesh::FaceRefList>; |
248 | 0 | for (const Entry &reflist : ndmesh.temp_map) { |
249 | 0 | { // create mesh |
250 | 0 | size_t n = 0; |
251 | 0 | for (Face *f : reflist.second) { |
252 | 0 | n += f->indices.size(); |
253 | 0 | } |
254 | 0 | if (!n) { |
255 | 0 | continue; |
256 | 0 | } |
257 | 0 | aiMesh *outmesh = fill->mMeshes[fill->mNumMeshes++] = new aiMesh(); |
258 | 0 | ++nd->mNumMeshes; |
259 | |
|
260 | 0 | outmesh->mVertices = new aiVector3D[n]; |
261 | 0 | outmesh->mTextureCoords[0] = new aiVector3D[n]; |
262 | |
|
263 | 0 | outmesh->mFaces = new aiFace[reflist.second.size()](); |
264 | 0 | for (Face *f : reflist.second) { |
265 | 0 | if (f->indices.empty()) { |
266 | 0 | continue; |
267 | 0 | } |
268 | | |
269 | 0 | aiFace &fout = outmesh->mFaces[outmesh->mNumFaces++]; |
270 | 0 | fout.mIndices = new unsigned int[f->indices.size()]; |
271 | |
|
272 | 0 | for (VertexIndex &v : f->indices) { |
273 | 0 | if (v.pos_idx >= ndmesh.vertex_positions.size()) { |
274 | 0 | ThrowException("Position index out of range"); |
275 | 0 | } |
276 | 0 | if (v.uv_idx >= ndmesh.texture_coords.size()) { |
277 | 0 | ThrowException("UV index out of range"); |
278 | 0 | } |
279 | 0 | outmesh->mVertices[outmesh->mNumVertices] = ndmesh.vertex_positions[v.pos_idx]; |
280 | 0 | outmesh->mTextureCoords[0][outmesh->mNumVertices] = aiVector3D( |
281 | 0 | ndmesh.texture_coords[v.uv_idx].x, |
282 | 0 | ndmesh.texture_coords[v.uv_idx].y, |
283 | 0 | 0.f); |
284 | |
|
285 | 0 | fout.mIndices[fout.mNumIndices++] = outmesh->mNumVertices++; |
286 | 0 | } |
287 | 0 | } |
288 | 0 | outmesh->mMaterialIndex = fill->mNumMaterials; |
289 | 0 | } |
290 | 0 | { // create material |
291 | 0 | const Material *min = nullptr; |
292 | 0 | for (const Material &m : scin.materials) { |
293 | 0 | if (m.parent_id == ndmesh.id && m.matnum == reflist.first) { |
294 | 0 | min = &m; |
295 | 0 | break; |
296 | 0 | } |
297 | 0 | } |
298 | 0 | std::unique_ptr<const Material> defmat; |
299 | 0 | if (!min) { |
300 | 0 | ASSIMP_LOG_VERBOSE_DEBUG("Could not resolve material index ", reflist.first, " - creating default material for this slot"); |
301 | |
|
302 | 0 | defmat.reset(min = new Material()); |
303 | 0 | } |
304 | |
|
305 | 0 | aiMaterial *mat = new aiMaterial(); |
306 | 0 | fill->mMaterials[fill->mNumMaterials++] = mat; |
307 | |
|
308 | 0 | const aiString s(format("#mat_") << fill->mNumMeshes << "_" << min->matnum); |
309 | 0 | mat->AddProperty(&s, AI_MATKEY_NAME); |
310 | |
|
311 | 0 | if (int tmp = ndmesh.draw_flags & Mesh::WIRED ? 1 : 0) { |
312 | 0 | mat->AddProperty(&tmp, 1, AI_MATKEY_ENABLE_WIREFRAME); |
313 | 0 | } |
314 | |
|
315 | 0 | { |
316 | 0 | int shader; |
317 | 0 | switch (min->shader) { |
318 | 0 | case Material::FLAT: |
319 | 0 | shader = aiShadingMode_Gouraud; |
320 | 0 | break; |
321 | | |
322 | 0 | case Material::PHONG: |
323 | 0 | shader = aiShadingMode_Phong; |
324 | 0 | break; |
325 | | |
326 | 0 | case Material::METAL: |
327 | 0 | shader = aiShadingMode_CookTorrance; |
328 | 0 | break; |
329 | | |
330 | 0 | default: |
331 | 0 | ASSIMP_LOG_ERROR("Unknown option."); |
332 | 0 | ai_assert(false); // shouldn't be here |
333 | 0 | break; |
334 | 0 | } |
335 | 0 | mat->AddProperty(&shader, 1, AI_MATKEY_SHADING_MODEL); |
336 | 0 | if (shader != aiShadingMode_Gouraud) { |
337 | 0 | mat->AddProperty(&min->exp, 1, AI_MATKEY_SHININESS); |
338 | 0 | } |
339 | 0 | } |
340 | | |
341 | 0 | mat->AddProperty(&min->ior, 1, AI_MATKEY_REFRACTI); |
342 | 0 | mat->AddProperty(&min->rgb, 1, AI_MATKEY_COLOR_DIFFUSE); |
343 | |
|
344 | 0 | aiColor3D c = aiColor3D(min->rgb) * min->ks; |
345 | 0 | mat->AddProperty(&c, 1, AI_MATKEY_COLOR_SPECULAR); |
346 | |
|
347 | 0 | c = aiColor3D(min->rgb) * min->ka; |
348 | 0 | mat->AddProperty(&c, 1, AI_MATKEY_COLOR_AMBIENT); |
349 | | |
350 | | // convert textures if some exist. |
351 | 0 | if (min->tex_color) { |
352 | 0 | ConvertTexture(min->tex_color, mat, aiTextureType_DIFFUSE); |
353 | 0 | } |
354 | 0 | if (min->tex_env) { |
355 | 0 | ConvertTexture(min->tex_env, mat, aiTextureType_UNKNOWN); |
356 | 0 | } |
357 | 0 | if (min->tex_bump) { |
358 | 0 | ConvertTexture(min->tex_bump, mat, aiTextureType_HEIGHT); |
359 | 0 | } |
360 | 0 | } |
361 | 0 | } |
362 | 0 | } |
363 | 0 | } else if (Node::TYPE_LIGHT == root.type) { |
364 | 0 | const Light &ndlight = (const Light &)(root); |
365 | 0 | aiLight *outlight = fill->mLights[fill->mNumLights++] = new aiLight(); |
366 | |
|
367 | 0 | outlight->mName.Set(ndlight.name); |
368 | 0 | outlight->mColorDiffuse = outlight->mColorAmbient = outlight->mColorSpecular = ndlight.color; |
369 | |
|
370 | 0 | outlight->mAngleOuterCone = AI_DEG_TO_RAD(ndlight.angle); |
371 | 0 | outlight->mAngleInnerCone = AI_DEG_TO_RAD(ndlight.inner_angle); |
372 | | |
373 | | // XXX |
374 | 0 | outlight->mType = ndlight.ltype == Light::SPOT ? aiLightSource_SPOT : aiLightSource_DIRECTIONAL; |
375 | 0 | } else if (Node::TYPE_CAMERA == root.type) { |
376 | 0 | const Camera &ndcam = (const Camera &)(root); |
377 | 0 | aiCamera *outcam = fill->mCameras[fill->mNumCameras++] = new aiCamera(); |
378 | |
|
379 | 0 | outcam->mName.Set(ndcam.name); |
380 | 0 | } |
381 | | |
382 | | // add meshes |
383 | 0 | if (nd->mNumMeshes) { // mMeshes must be nullptr if count is 0 |
384 | 0 | nd->mMeshes = new unsigned int[nd->mNumMeshes]; |
385 | 0 | for (unsigned int i = 0; i < nd->mNumMeshes; ++i) { |
386 | 0 | nd->mMeshes[i] = fill->mNumMeshes - i - 1; |
387 | 0 | } |
388 | 0 | } |
389 | | |
390 | | // add children recursively |
391 | 0 | if (!root.temp_children.empty()) { |
392 | 0 | nd->mChildren = new aiNode *[root.temp_children.size()](); |
393 | 0 | for (const Node *n : root.temp_children) { |
394 | 0 | (nd->mChildren[nd->mNumChildren++] = BuildNodes(*n, scin, fill))->mParent = nd; |
395 | 0 | } |
396 | 0 | } |
397 | |
|
398 | 0 | return nd; |
399 | 0 | } |
400 | | |
401 | | // ------------------------------------------------------------------------------------------------ |
402 | | // Read an ASCII file into the given scene data structure |
403 | 0 | void COBImporter::ReadAsciiFile(Scene &out, StreamReaderLE *stream) { |
404 | 0 | ChunkInfo ci; |
405 | 0 | for (LineSplitter splitter(*stream); splitter; ++splitter) { |
406 | | |
407 | | // add all chunks to be recognized here. /else ../ omitted intentionally. |
408 | 0 | if (splitter.match_start("PolH ")) { |
409 | 0 | ReadChunkInfo_Ascii(ci, splitter); |
410 | 0 | ReadPolH_Ascii(out, splitter, ci); |
411 | 0 | } |
412 | 0 | if (splitter.match_start("BitM ")) { |
413 | 0 | ReadChunkInfo_Ascii(ci, splitter); |
414 | 0 | ReadBitM_Ascii(out, splitter, ci); |
415 | 0 | } |
416 | 0 | if (splitter.match_start("Mat1 ")) { |
417 | 0 | ReadChunkInfo_Ascii(ci, splitter); |
418 | 0 | ReadMat1_Ascii(out, splitter, ci); |
419 | 0 | } |
420 | 0 | if (splitter.match_start("Grou ")) { |
421 | 0 | ReadChunkInfo_Ascii(ci, splitter); |
422 | 0 | ReadGrou_Ascii(out, splitter, ci); |
423 | 0 | } |
424 | 0 | if (splitter.match_start("Lght ")) { |
425 | 0 | ReadChunkInfo_Ascii(ci, splitter); |
426 | 0 | ReadLght_Ascii(out, splitter, ci); |
427 | 0 | } |
428 | 0 | if (splitter.match_start("Came ")) { |
429 | 0 | ReadChunkInfo_Ascii(ci, splitter); |
430 | 0 | ReadCame_Ascii(out, splitter, ci); |
431 | 0 | } |
432 | 0 | if (splitter.match_start("Bone ")) { |
433 | 0 | ReadChunkInfo_Ascii(ci, splitter); |
434 | 0 | ReadBone_Ascii(out, splitter, ci); |
435 | 0 | } |
436 | 0 | if (splitter.match_start("Chan ")) { |
437 | 0 | ReadChunkInfo_Ascii(ci, splitter); |
438 | 0 | ReadChan_Ascii(out, splitter, ci); |
439 | 0 | } |
440 | 0 | if (splitter.match_start("Unit ")) { |
441 | 0 | ReadChunkInfo_Ascii(ci, splitter); |
442 | 0 | ReadUnit_Ascii(out, splitter, ci); |
443 | 0 | } |
444 | 0 | if (splitter.match_start("END ")) { |
445 | | // we don't need this, but I guess there is a reason this |
446 | | // chunk has been implemented into COB for. |
447 | 0 | return; |
448 | 0 | } |
449 | 0 | } |
450 | 0 | } |
451 | | |
452 | | // ------------------------------------------------------------------------------------------------ |
453 | 0 | void COBImporter::ReadChunkInfo_Ascii(ChunkInfo &out, const LineSplitter &splitter) { |
454 | 0 | const char *all_tokens[8]; |
455 | 0 | splitter.get_tokens(all_tokens); |
456 | |
|
457 | 0 | out.version = (all_tokens[1][1] - '0') * 100 + (all_tokens[1][3] - '0') * 10 + (all_tokens[1][4] - '0'); |
458 | 0 | out.id = strtoul10(all_tokens[3]); |
459 | 0 | out.parent_id = strtoul10(all_tokens[5]); |
460 | 0 | out.size = strtol10(all_tokens[7]); |
461 | 0 | } |
462 | | |
463 | | // ------------------------------------------------------------------------------------------------ |
464 | 0 | void COBImporter::UnsupportedChunk_Ascii(LineSplitter &splitter, const ChunkInfo &nfo, const char *name) { |
465 | 0 | const std::string error = format("Encountered unsupported chunk: ") << name << " [version: " << nfo.version << ", size: " << nfo.size << "]"; |
466 | | |
467 | | // we can recover if the chunk size was specified. |
468 | 0 | if (nfo.size != static_cast<unsigned int>(-1)) { |
469 | 0 | ASSIMP_LOG_ERROR(error); |
470 | | |
471 | | // (HACK) - our current position in the stream is the beginning of the |
472 | | // head line of the next chunk. That's fine, but the caller is going |
473 | | // to call ++ on `splitter`, which we need to swallow to avoid |
474 | | // missing the next line. |
475 | 0 | splitter.get_stream().IncPtr(nfo.size); |
476 | 0 | splitter.swallow_next_increment(); |
477 | 0 | } else { |
478 | 0 | ThrowException(error); |
479 | 0 | } |
480 | 0 | } |
481 | | |
482 | | // ------------------------------------------------------------------------------------------------ |
483 | 0 | void COBImporter::ReadBasicNodeInfo_Ascii(Node &msh, LineSplitter &splitter, const ChunkInfo & /*nfo*/) { |
484 | 0 | for (; splitter; ++splitter) { |
485 | 0 | if (splitter.match_start("Name")) { |
486 | 0 | msh.name = std::string(splitter[1]); |
487 | | |
488 | | // make nice names by merging the dupe count |
489 | 0 | std::replace(msh.name.begin(), msh.name.end(), |
490 | 0 | ',', '_'); |
491 | 0 | } else if (splitter.match_start("Transform")) { |
492 | 0 | for (unsigned int y = 0; y < 4 && ++splitter; ++y) { |
493 | 0 | const char *s = splitter->c_str(); |
494 | 0 | const char *end = s + splitter->size(); |
495 | 0 | for (unsigned int x = 0; x < 4; ++x) { |
496 | 0 | SkipSpaces(&s, end); |
497 | 0 | msh.transform[y][x] = fast_atof(&s); |
498 | 0 | } |
499 | 0 | } |
500 | | // we need the transform chunk, so we won't return until we have it. |
501 | 0 | return; |
502 | 0 | } |
503 | 0 | } |
504 | 0 | } |
505 | | |
506 | | // ------------------------------------------------------------------------------------------------ |
507 | | template <typename T> |
508 | 0 | void COBImporter::ReadFloat3Tuple_Ascii(T &fill, const char **in, const char *end) { |
509 | 0 | const char *rgb = *in; |
510 | 0 | for (unsigned int i = 0; i < 3; ++i) { |
511 | 0 | SkipSpaces(&rgb, end); |
512 | 0 | if (*rgb == ',') ++rgb; |
513 | 0 | SkipSpaces(&rgb, end); |
514 | |
|
515 | 0 | fill[i] = fast_atof(&rgb); |
516 | 0 | } |
517 | 0 | *in = rgb; |
518 | 0 | } |
519 | | |
520 | | // ------------------------------------------------------------------------------------------------ |
521 | 0 | void COBImporter::ReadMat1_Ascii(Scene &out, LineSplitter &splitter, const ChunkInfo &nfo) { |
522 | 0 | if (nfo.version > 8) { |
523 | 0 | return UnsupportedChunk_Ascii(splitter, nfo, "Mat1"); |
524 | 0 | } |
525 | | |
526 | 0 | ++splitter; |
527 | 0 | if (!splitter.match_start("mat# ")) { |
528 | 0 | ASSIMP_LOG_WARN("Expected `mat#` line in `Mat1` chunk ", nfo.id); |
529 | 0 | return; |
530 | 0 | } |
531 | | |
532 | 0 | out.materials.emplace_back(); |
533 | 0 | Material &mat = out.materials.back(); |
534 | 0 | mat = nfo; |
535 | |
|
536 | 0 | mat.matnum = strtoul10(splitter[1]); |
537 | 0 | ++splitter; |
538 | |
|
539 | 0 | if (!splitter.match_start("shader: ")) { |
540 | 0 | ASSIMP_LOG_WARN("Expected `mat#` line in `Mat1` chunk ", nfo.id); |
541 | 0 | return; |
542 | 0 | } |
543 | 0 | std::string shader = std::string(splitter[1]); |
544 | 0 | shader = shader.substr(0, shader.find_first_of(" \t")); |
545 | |
|
546 | 0 | if (shader == "metal") { |
547 | 0 | mat.shader = Material::METAL; |
548 | 0 | } else if (shader == "phong") { |
549 | 0 | mat.shader = Material::PHONG; |
550 | 0 | } else if (shader != "flat") { |
551 | 0 | ASSIMP_LOG_WARN("Unknown value for `shader` in `Mat1` chunk ", nfo.id); |
552 | 0 | } |
553 | |
|
554 | 0 | ++splitter; |
555 | 0 | if (!splitter.match_start("rgb ")) { |
556 | 0 | ASSIMP_LOG_WARN("Expected `rgb` line in `Mat1` chunk ", nfo.id); |
557 | 0 | } |
558 | |
|
559 | 0 | const char *rgb = splitter[1]; |
560 | 0 | ReadFloat3Tuple_Ascii(mat.rgb, &rgb, splitter.getEnd()); |
561 | |
|
562 | 0 | ++splitter; |
563 | 0 | if (!splitter.match_start("alpha ")) { |
564 | 0 | ASSIMP_LOG_WARN("Expected `alpha` line in `Mat1` chunk ", nfo.id); |
565 | 0 | } |
566 | |
|
567 | 0 | const char *tokens[10]; |
568 | 0 | splitter.get_tokens(tokens); |
569 | |
|
570 | 0 | mat.alpha = fast_atof(tokens[1]); |
571 | 0 | mat.ka = fast_atof(tokens[3]); |
572 | 0 | mat.ks = fast_atof(tokens[5]); |
573 | 0 | mat.exp = fast_atof(tokens[7]); |
574 | 0 | mat.ior = fast_atof(tokens[9]); |
575 | 0 | } |
576 | | |
577 | | // ------------------------------------------------------------------------------------------------ |
578 | 0 | void COBImporter::ReadUnit_Ascii(Scene &out, LineSplitter &splitter, const ChunkInfo &nfo) { |
579 | 0 | if (nfo.version > 1) { |
580 | 0 | return UnsupportedChunk_Ascii(splitter, nfo, "Unit"); |
581 | 0 | } |
582 | 0 | ++splitter; |
583 | 0 | if (!splitter.match_start("Units ")) { |
584 | 0 | ASSIMP_LOG_WARN("Expected `Units` line in `Unit` chunk ", nfo.id); |
585 | 0 | return; |
586 | 0 | } |
587 | | |
588 | | // parent chunks preceede their children, so we should have the |
589 | | // corresponding chunk already. |
590 | 0 | for (std::shared_ptr<Node> &nd : out.nodes) { |
591 | 0 | if (nd->id == nfo.parent_id) { |
592 | 0 | const unsigned int t = strtoul10(splitter[1]); |
593 | |
|
594 | 0 | nd->unit_scale = t >= sizeof(units) / sizeof(units[0]) ? ( |
595 | 0 | ASSIMP_LOG_WARN(t, " is not a valid value for `Units` attribute in `Unit chunk` ", nfo.id), 1.f) : |
596 | 0 | units[t]; |
597 | 0 | return; |
598 | 0 | } |
599 | 0 | } |
600 | 0 | ASSIMP_LOG_WARN("`Unit` chunk ", nfo.id, " is a child of ", nfo.parent_id, " which does not exist"); |
601 | 0 | } |
602 | | |
603 | | // ------------------------------------------------------------------------------------------------ |
604 | 0 | void COBImporter::ReadChan_Ascii(Scene & /*out*/, LineSplitter &splitter, const ChunkInfo &nfo) { |
605 | 0 | if (nfo.version > 8) { |
606 | 0 | return UnsupportedChunk_Ascii(splitter, nfo, "Chan"); |
607 | 0 | } |
608 | 0 | } |
609 | | |
610 | | // ------------------------------------------------------------------------------------------------ |
611 | 0 | void COBImporter::ReadLght_Ascii(Scene &out, LineSplitter &splitter, const ChunkInfo &nfo) { |
612 | 0 | if (nfo.version > 8) { |
613 | 0 | return UnsupportedChunk_Ascii(splitter, nfo, "Lght"); |
614 | 0 | } |
615 | | |
616 | 0 | out.nodes.push_back(std::shared_ptr<Light>(new Light())); |
617 | 0 | Light &msh = (Light &)(*out.nodes.back().get()); |
618 | 0 | msh = nfo; |
619 | |
|
620 | 0 | ReadBasicNodeInfo_Ascii(msh, ++splitter, nfo); |
621 | |
|
622 | 0 | if (splitter.match_start("Infinite ")) { |
623 | 0 | msh.ltype = Light::INFINITE; |
624 | 0 | } else if (splitter.match_start("Local ")) { |
625 | 0 | msh.ltype = Light::LOCAL; |
626 | 0 | } else if (splitter.match_start("Spot ")) { |
627 | 0 | msh.ltype = Light::SPOT; |
628 | 0 | } else { |
629 | 0 | ASSIMP_LOG_WARN("Unknown kind of light source in `Lght` chunk ", nfo.id, " : ", *splitter); |
630 | 0 | msh.ltype = Light::SPOT; |
631 | 0 | } |
632 | |
|
633 | 0 | ++splitter; |
634 | 0 | if (!splitter.match_start("color ")) { |
635 | 0 | ASSIMP_LOG_WARN("Expected `color` line in `Lght` chunk ", nfo.id); |
636 | 0 | } |
637 | |
|
638 | 0 | const char *rgb = splitter[1]; |
639 | 0 | const char *end = splitter.getEnd(); |
640 | 0 | ReadFloat3Tuple_Ascii(msh.color, &rgb, end); |
641 | |
|
642 | 0 | SkipSpaces(&rgb, end); |
643 | 0 | if (strncmp(rgb, "cone angle", 10) != 0) { |
644 | 0 | ASSIMP_LOG_WARN("Expected `cone angle` entity in `color` line in `Lght` chunk ", nfo.id); |
645 | 0 | } |
646 | 0 | SkipSpaces(rgb + 10, &rgb, end); |
647 | 0 | msh.angle = fast_atof(&rgb); |
648 | |
|
649 | 0 | SkipSpaces(&rgb, end); |
650 | 0 | if (strncmp(rgb, "inner angle", 11) != 0) { |
651 | 0 | ASSIMP_LOG_WARN("Expected `inner angle` entity in `color` line in `Lght` chunk ", nfo.id); |
652 | 0 | } |
653 | 0 | SkipSpaces(rgb + 11, &rgb, end); |
654 | 0 | msh.inner_angle = fast_atof(&rgb); |
655 | | |
656 | | // skip the rest for we can't handle this kind of physically-based lighting information. |
657 | 0 | } |
658 | | |
659 | | // ------------------------------------------------------------------------------------------------ |
660 | 0 | void COBImporter::ReadCame_Ascii(Scene &out, LineSplitter &splitter, const ChunkInfo &nfo) { |
661 | 0 | if (nfo.version > 2) { |
662 | 0 | return UnsupportedChunk_Ascii(splitter, nfo, "Came"); |
663 | 0 | } |
664 | | |
665 | 0 | out.nodes.push_back(std::shared_ptr<Camera>(new Camera())); |
666 | 0 | Camera &msh = (Camera &)(*out.nodes.back().get()); |
667 | 0 | msh = nfo; |
668 | |
|
669 | 0 | ReadBasicNodeInfo_Ascii(msh, ++splitter, nfo); |
670 | | |
671 | | // skip the next line, we don't know this differentiation between a |
672 | | // standard camera and a panoramic camera. |
673 | 0 | ++splitter; |
674 | 0 | } |
675 | | |
676 | | // ------------------------------------------------------------------------------------------------ |
677 | 0 | void COBImporter::ReadBone_Ascii(Scene &out, LineSplitter &splitter, const ChunkInfo &nfo) { |
678 | 0 | if (nfo.version > 5) { |
679 | 0 | return UnsupportedChunk_Ascii(splitter, nfo, "Bone"); |
680 | 0 | } |
681 | | |
682 | 0 | out.nodes.push_back(std::shared_ptr<Bone>(new Bone())); |
683 | 0 | Bone &msh = (Bone &)(*out.nodes.back().get()); |
684 | 0 | msh = nfo; |
685 | |
|
686 | 0 | ReadBasicNodeInfo_Ascii(msh, ++splitter, nfo); |
687 | | |
688 | | // TODO |
689 | 0 | } |
690 | | |
691 | | // ------------------------------------------------------------------------------------------------ |
692 | 0 | void COBImporter::ReadGrou_Ascii(Scene &out, LineSplitter &splitter, const ChunkInfo &nfo) { |
693 | 0 | if (nfo.version > 1) { |
694 | 0 | return UnsupportedChunk_Ascii(splitter, nfo, "Grou"); |
695 | 0 | } |
696 | | |
697 | 0 | out.nodes.push_back(std::shared_ptr<Group>(new Group())); |
698 | 0 | Group &msh = (Group &)(*out.nodes.back().get()); |
699 | 0 | msh = nfo; |
700 | |
|
701 | 0 | ReadBasicNodeInfo_Ascii(msh, ++splitter, nfo); |
702 | 0 | } |
703 | | |
704 | | // ------------------------------------------------------------------------------------------------ |
705 | 0 | void COBImporter::ReadPolH_Ascii(Scene &out, LineSplitter &splitter, const ChunkInfo &nfo) { |
706 | 0 | if (nfo.version > 8) { |
707 | 0 | return UnsupportedChunk_Ascii(splitter, nfo, "PolH"); |
708 | 0 | } |
709 | | |
710 | 0 | out.nodes.push_back(std::shared_ptr<Mesh>(new Mesh())); |
711 | 0 | Mesh &msh = (Mesh &)(*out.nodes.back().get()); |
712 | 0 | msh = nfo; |
713 | |
|
714 | 0 | ReadBasicNodeInfo_Ascii(msh, ++splitter, nfo); |
715 | | |
716 | | // the chunk has a fixed order of components, but some are not interesting of us so |
717 | | // we're just looking for keywords in arbitrary order. The end of the chunk is |
718 | | // either the last `Face` or the `DrawFlags` attribute, depending on the format ver. |
719 | 0 | for (; splitter; ++splitter) { |
720 | 0 | if (splitter.match_start("World Vertices")) { |
721 | 0 | const unsigned int cnt = strtoul10(splitter[2]); |
722 | 0 | msh.vertex_positions.resize(cnt); |
723 | |
|
724 | 0 | for (unsigned int cur = 0; cur < cnt && ++splitter; ++cur) { |
725 | 0 | const char *s = splitter->c_str(); |
726 | 0 | const char *end = splitter.getEnd(); |
727 | 0 | aiVector3D &v = msh.vertex_positions[cur]; |
728 | |
|
729 | 0 | SkipSpaces(&s, end); |
730 | 0 | v.x = fast_atof(&s); |
731 | 0 | SkipSpaces(&s, end); |
732 | 0 | v.y = fast_atof(&s); |
733 | 0 | SkipSpaces(&s, end); |
734 | 0 | v.z = fast_atof(&s); |
735 | 0 | } |
736 | 0 | } else if (splitter.match_start("Texture Vertices")) { |
737 | 0 | const unsigned int cnt = strtoul10(splitter[2]); |
738 | 0 | msh.texture_coords.resize(cnt); |
739 | |
|
740 | 0 | for (unsigned int cur = 0; cur < cnt && ++splitter; ++cur) { |
741 | 0 | const char *s = splitter->c_str(); |
742 | 0 | const char *end = splitter.getEnd(); |
743 | |
|
744 | 0 | aiVector2D &v = msh.texture_coords[cur]; |
745 | |
|
746 | 0 | SkipSpaces(&s, end); |
747 | 0 | v.x = fast_atof(&s); |
748 | 0 | SkipSpaces(&s, end); |
749 | 0 | v.y = fast_atof(&s); |
750 | 0 | } |
751 | 0 | } else if (splitter.match_start("Faces")) { |
752 | 0 | const unsigned int cnt = strtoul10(splitter[1]); |
753 | 0 | msh.faces.reserve(cnt); |
754 | |
|
755 | 0 | for (unsigned int cur = 0; cur < cnt && ++splitter; ++cur) { |
756 | 0 | if (splitter.match_start("Hole")) { |
757 | 0 | ASSIMP_LOG_WARN("Skipping unsupported `Hole` line"); |
758 | 0 | continue; |
759 | 0 | } |
760 | | |
761 | 0 | if (!splitter.match_start("Face")) { |
762 | 0 | ThrowException("Expected Face line"); |
763 | 0 | } |
764 | | |
765 | 0 | msh.faces.emplace_back(); |
766 | 0 | Face &face = msh.faces.back(); |
767 | |
|
768 | 0 | face.indices.resize(strtoul10(splitter[2])); |
769 | 0 | face.flags = strtoul10(splitter[4]); |
770 | 0 | face.material = strtoul10(splitter[6]); |
771 | |
|
772 | 0 | const char *s = (++splitter)->c_str(); |
773 | 0 | const char *end = splitter.getEnd(); |
774 | 0 | for (size_t i = 0; i < face.indices.size(); ++i) { |
775 | 0 | if (!SkipSpaces(&s, end)) { |
776 | 0 | ThrowException("Expected EOL token in Face entry"); |
777 | 0 | } |
778 | 0 | if ('<' != *s++) { |
779 | 0 | ThrowException("Expected < token in Face entry"); |
780 | 0 | } |
781 | 0 | face.indices[i].pos_idx = strtoul10(s, &s); |
782 | 0 | if (',' != *s++) { |
783 | 0 | ThrowException("Expected , token in Face entry"); |
784 | 0 | } |
785 | 0 | face.indices[i].uv_idx = strtoul10(s, &s); |
786 | 0 | if ('>' != *s++) { |
787 | 0 | ThrowException("Expected < token in Face entry"); |
788 | 0 | } |
789 | 0 | } |
790 | 0 | } |
791 | 0 | if (nfo.version <= 4) { |
792 | 0 | break; |
793 | 0 | } |
794 | 0 | } else if (splitter.match_start("DrawFlags")) { |
795 | 0 | msh.draw_flags = strtoul10(splitter[1]); |
796 | 0 | break; |
797 | 0 | } |
798 | 0 | } |
799 | 0 | } |
800 | | |
801 | | // ------------------------------------------------------------------------------------------------ |
802 | 0 | void COBImporter::ReadBitM_Ascii(Scene & /*out*/, LineSplitter &splitter, const ChunkInfo &nfo) { |
803 | 0 | if (nfo.version > 1) { |
804 | 0 | return UnsupportedChunk_Ascii(splitter, nfo, "BitM"); |
805 | 0 | } |
806 | | |
807 | 0 | const unsigned int head = strtoul10((++splitter)[1]); |
808 | 0 | if (head != sizeof(Bitmap::BitmapHeader)) { |
809 | 0 | ASSIMP_LOG_WARN("Unexpected ThumbNailHdrSize, skipping this chunk"); |
810 | 0 | return; |
811 | 0 | } |
812 | 0 | } |
813 | | |
814 | | // ------------------------------------------------------------------------------------------------ |
815 | 0 | void COBImporter::ReadString_Binary(std::string &out, StreamReaderLE &reader) { |
816 | 0 | out.resize(reader.GetI2()); |
817 | 0 | for (char &c : out) { |
818 | 0 | c = reader.GetI1(); |
819 | 0 | } |
820 | 0 | } |
821 | | |
822 | | // ------------------------------------------------------------------------------------------------ |
823 | 0 | void COBImporter::ReadBasicNodeInfo_Binary(Node &msh, StreamReaderLE &reader, const ChunkInfo & /*nfo*/) { |
824 | 0 | const unsigned int dupes = reader.GetI2(); |
825 | 0 | ReadString_Binary(msh.name, reader); |
826 | |
|
827 | 0 | msh.name = format(msh.name) << '_' << dupes; |
828 | | |
829 | | // skip local axes for the moment |
830 | 0 | reader.IncPtr(48); |
831 | |
|
832 | 0 | msh.transform = aiMatrix4x4(); |
833 | 0 | for (unsigned int y = 0; y < 3; ++y) { |
834 | 0 | for (unsigned int x = 0; x < 4; ++x) { |
835 | 0 | msh.transform[y][x] = reader.GetF4(); |
836 | 0 | } |
837 | 0 | } |
838 | 0 | } |
839 | | |
840 | | // ------------------------------------------------------------------------------------------------ |
841 | 0 | void COBImporter::UnsupportedChunk_Binary(StreamReaderLE &reader, const ChunkInfo &nfo, const char *name) { |
842 | 0 | const std::string error = format("Encountered unsupported chunk: ") << name << " [version: " << nfo.version << ", size: " << nfo.size << "]"; |
843 | | |
844 | | // we can recover if the chunk size was specified. |
845 | 0 | if (nfo.size != static_cast<unsigned int>(-1)) { |
846 | 0 | ASSIMP_LOG_ERROR(error); |
847 | 0 | reader.IncPtr(nfo.size); |
848 | 0 | } else |
849 | 0 | ThrowException(error); |
850 | 0 | } |
851 | | |
852 | | // ------------------------------------------------------------------------------------------------ |
853 | | // tiny utility guard to aid me at staying within chunk boundaries. |
854 | | class chunk_guard { |
855 | | public: |
856 | | chunk_guard(const COB::ChunkInfo &nfo, StreamReaderLE &reader) : |
857 | 0 | nfo(nfo), reader(reader), cur(reader.GetCurrentPos()) { |
858 | | // empty |
859 | 0 | } |
860 | | |
861 | 0 | ~chunk_guard() { |
862 | | // don't do anything if the size is not given |
863 | 0 | if (nfo.size != static_cast<unsigned int>(-1)) { |
864 | 0 | try { |
865 | 0 | reader.IncPtr(static_cast<int>(nfo.size) - reader.GetCurrentPos() + cur); |
866 | 0 | } catch (const DeadlyImportError &) { |
867 | | // out of limit so correct the value |
868 | 0 | reader.IncPtr(reader.GetReadLimit()); |
869 | 0 | } |
870 | 0 | } |
871 | 0 | } |
872 | | |
873 | | private: |
874 | | const COB::ChunkInfo &nfo; |
875 | | StreamReaderLE &reader; |
876 | | long cur; |
877 | | }; |
878 | | |
879 | | // ------------------------------------------------------------------------------------------------ |
880 | 0 | void COBImporter::ReadBinaryFile(Scene &out, StreamReaderLE *reader) { |
881 | 0 | if (nullptr == reader) { |
882 | 0 | return; |
883 | 0 | } |
884 | | |
885 | 0 | while (true) { |
886 | 0 | std::string type; |
887 | 0 | type += reader->GetI1(); |
888 | 0 | type += reader->GetI1(); |
889 | 0 | type += reader->GetI1(); |
890 | 0 | type += reader->GetI1(); |
891 | |
|
892 | 0 | ChunkInfo nfo; |
893 | 0 | nfo.version = reader->GetI2() * 10; |
894 | 0 | nfo.version += reader->GetI2(); |
895 | |
|
896 | 0 | nfo.id = reader->GetI4(); |
897 | 0 | nfo.parent_id = reader->GetI4(); |
898 | 0 | nfo.size = reader->GetI4(); |
899 | |
|
900 | 0 | if (type == "PolH") { |
901 | 0 | ReadPolH_Binary(out, *reader, nfo); |
902 | 0 | } else if (type == "BitM") { |
903 | 0 | ReadBitM_Binary(out, *reader, nfo); |
904 | 0 | } else if (type == "Grou") { |
905 | 0 | ReadGrou_Binary(out, *reader, nfo); |
906 | 0 | } else if (type == "Lght") { |
907 | 0 | ReadLght_Binary(out, *reader, nfo); |
908 | 0 | } else if (type == "Came") { |
909 | 0 | ReadCame_Binary(out, *reader, nfo); |
910 | 0 | } else if (type == "Mat1") { |
911 | 0 | ReadMat1_Binary(out, *reader, nfo); |
912 | 0 | } else if (type == "Unit") { |
913 | 0 | ReadUnit_Binary(out, *reader, nfo); |
914 | 0 | } else if (type == "OLay") { |
915 | | // ignore layer index silently. |
916 | 0 | if (nfo.size != static_cast<unsigned int>(-1)) { |
917 | 0 | reader->IncPtr(nfo.size); |
918 | 0 | } else |
919 | 0 | return UnsupportedChunk_Binary(*reader, nfo, type.c_str()); |
920 | 0 | } else if (type == "END ") { |
921 | 0 | return; |
922 | 0 | } else { |
923 | 0 | UnsupportedChunk_Binary(*reader, nfo, type.c_str()); |
924 | 0 | } |
925 | 0 | } |
926 | 0 | } |
927 | | |
928 | | // ------------------------------------------------------------------------------------------------ |
929 | 0 | void COBImporter::ReadPolH_Binary(COB::Scene &out, StreamReaderLE &reader, const ChunkInfo &nfo) { |
930 | 0 | if (nfo.version > 8) { |
931 | 0 | return UnsupportedChunk_Binary(reader, nfo, "PolH"); |
932 | 0 | } |
933 | 0 | const chunk_guard cn(nfo, reader); |
934 | |
|
935 | 0 | out.nodes.push_back(std::shared_ptr<Mesh>(new Mesh())); |
936 | 0 | Mesh &msh = (Mesh &)(*out.nodes.back().get()); |
937 | 0 | msh = nfo; |
938 | |
|
939 | 0 | ReadBasicNodeInfo_Binary(msh, reader, nfo); |
940 | |
|
941 | 0 | msh.vertex_positions.resize(reader.GetI4()); |
942 | 0 | for (aiVector3D &v : msh.vertex_positions) { |
943 | 0 | v.x = reader.GetF4(); |
944 | 0 | v.y = reader.GetF4(); |
945 | 0 | v.z = reader.GetF4(); |
946 | 0 | } |
947 | |
|
948 | 0 | msh.texture_coords.resize(reader.GetI4()); |
949 | 0 | for (aiVector2D &v : msh.texture_coords) { |
950 | 0 | v.x = reader.GetF4(); |
951 | 0 | v.y = reader.GetF4(); |
952 | 0 | } |
953 | |
|
954 | 0 | const size_t numf = reader.GetI4(); |
955 | 0 | msh.faces.reserve(numf); |
956 | 0 | for (size_t i = 0; i < numf; ++i) { |
957 | | // XXX backface culling flag is 0x10 in flags |
958 | | |
959 | | // hole? |
960 | 0 | bool hole = (reader.GetI1() & 0x08) != 0; |
961 | 0 | if (hole) { |
962 | | // XXX Basically this should just work fine - then triangulator |
963 | | // should output properly triangulated data even for polygons |
964 | | // with holes. Test data specific to COB is needed to confirm it. |
965 | 0 | if (msh.faces.empty()) { |
966 | 0 | ThrowException(format("A hole is the first entity in the `PolH` chunk with id ") << nfo.id); |
967 | 0 | } |
968 | 0 | } else |
969 | 0 | msh.faces.emplace_back(); |
970 | 0 | Face &f = msh.faces.back(); |
971 | |
|
972 | 0 | const size_t num = reader.GetI2(); |
973 | 0 | f.indices.reserve(f.indices.size() + num); |
974 | |
|
975 | 0 | if (!hole) { |
976 | 0 | f.material = reader.GetI2(); |
977 | 0 | f.flags = 0; |
978 | 0 | } |
979 | |
|
980 | 0 | for (size_t x = 0; x < num; ++x) { |
981 | 0 | f.indices.emplace_back(); |
982 | |
|
983 | 0 | VertexIndex &v = f.indices.back(); |
984 | 0 | v.pos_idx = reader.GetI4(); |
985 | 0 | v.uv_idx = reader.GetI4(); |
986 | 0 | } |
987 | |
|
988 | 0 | if (hole) { |
989 | 0 | std::reverse(f.indices.rbegin(), f.indices.rbegin() + num); |
990 | 0 | } |
991 | 0 | } |
992 | 0 | if (nfo.version > 4) { |
993 | 0 | msh.draw_flags = reader.GetI4(); |
994 | 0 | } |
995 | 0 | nfo.version > 5 && nfo.version < 8 ? reader.GetI4() : 0; |
996 | 0 | } |
997 | | |
998 | | // ------------------------------------------------------------------------------------------------ |
999 | 0 | void COBImporter::ReadBitM_Binary(COB::Scene & /*out*/, StreamReaderLE &reader, const ChunkInfo &nfo) { |
1000 | 0 | if (nfo.version > 1) { |
1001 | 0 | return UnsupportedChunk_Binary(reader, nfo, "BitM"); |
1002 | 0 | } |
1003 | | |
1004 | 0 | const chunk_guard cn(nfo, reader); |
1005 | |
|
1006 | 0 | const uint32_t len = reader.GetI4(); |
1007 | 0 | reader.IncPtr(len); |
1008 | |
|
1009 | 0 | reader.GetI4(); |
1010 | 0 | reader.IncPtr(reader.GetI4()); |
1011 | 0 | } |
1012 | | |
1013 | | // ------------------------------------------------------------------------------------------------ |
1014 | 0 | void COBImporter::ReadMat1_Binary(COB::Scene &out, StreamReaderLE &reader, const ChunkInfo &nfo) { |
1015 | 0 | if (nfo.version > 8) { |
1016 | 0 | return UnsupportedChunk_Binary(reader, nfo, "Mat1"); |
1017 | 0 | } |
1018 | | |
1019 | 0 | const chunk_guard cn(nfo, reader); |
1020 | |
|
1021 | 0 | out.materials.emplace_back(); |
1022 | 0 | Material &mat = out.materials.back(); |
1023 | 0 | mat = nfo; |
1024 | |
|
1025 | 0 | mat.matnum = reader.GetI2(); |
1026 | 0 | switch (reader.GetI1()) { |
1027 | 0 | case 'f': |
1028 | 0 | mat.type = Material::FLAT; |
1029 | 0 | break; |
1030 | 0 | case 'p': |
1031 | 0 | mat.type = Material::PHONG; |
1032 | 0 | break; |
1033 | 0 | case 'm': |
1034 | 0 | mat.type = Material::METAL; |
1035 | 0 | break; |
1036 | 0 | default: |
1037 | 0 | ASSIMP_LOG_ERROR("Unrecognized shader type in `Mat1` chunk with id ", nfo.id); |
1038 | 0 | mat.type = Material::FLAT; |
1039 | 0 | } |
1040 | | |
1041 | 0 | switch (reader.GetI1()) { |
1042 | 0 | case 'f': |
1043 | 0 | mat.autofacet = Material::FACETED; |
1044 | 0 | break; |
1045 | 0 | case 'a': |
1046 | 0 | mat.autofacet = Material::AUTOFACETED; |
1047 | 0 | break; |
1048 | 0 | case 's': |
1049 | 0 | mat.autofacet = Material::SMOOTH; |
1050 | 0 | break; |
1051 | 0 | default: |
1052 | 0 | ASSIMP_LOG_ERROR("Unrecognized faceting mode in `Mat1` chunk with id ", nfo.id); |
1053 | 0 | mat.autofacet = Material::FACETED; |
1054 | 0 | } |
1055 | 0 | mat.autofacet_angle = static_cast<float>(reader.GetI1()); |
1056 | |
|
1057 | 0 | mat.rgb.r = reader.GetF4(); |
1058 | 0 | mat.rgb.g = reader.GetF4(); |
1059 | 0 | mat.rgb.b = reader.GetF4(); |
1060 | |
|
1061 | 0 | mat.alpha = reader.GetF4(); |
1062 | 0 | mat.ka = reader.GetF4(); |
1063 | 0 | mat.ks = reader.GetF4(); |
1064 | 0 | mat.exp = reader.GetF4(); |
1065 | 0 | mat.ior = reader.GetF4(); |
1066 | |
|
1067 | 0 | char id[2]; |
1068 | 0 | id[0] = reader.GetI1(), id[1] = reader.GetI1(); |
1069 | |
|
1070 | 0 | if (id[0] == 'e' && id[1] == ':') { |
1071 | 0 | mat.tex_env = std::make_shared<Texture>(); |
1072 | |
|
1073 | 0 | reader.GetI1(); |
1074 | 0 | ReadString_Binary(mat.tex_env->path, reader); |
1075 | | |
1076 | | // advance to next texture-id |
1077 | 0 | id[0] = reader.GetI1(), id[1] = reader.GetI1(); |
1078 | 0 | } |
1079 | |
|
1080 | 0 | if (id[0] == 't' && id[1] == ':') { |
1081 | 0 | mat.tex_color = std::make_shared<Texture>(); |
1082 | |
|
1083 | 0 | reader.GetI1(); |
1084 | 0 | ReadString_Binary(mat.tex_color->path, reader); |
1085 | |
|
1086 | 0 | mat.tex_color->transform.mTranslation.x = reader.GetF4(); |
1087 | 0 | mat.tex_color->transform.mTranslation.y = reader.GetF4(); |
1088 | |
|
1089 | 0 | mat.tex_color->transform.mScaling.x = reader.GetF4(); |
1090 | 0 | mat.tex_color->transform.mScaling.y = reader.GetF4(); |
1091 | | |
1092 | | // advance to next texture-id |
1093 | 0 | id[0] = reader.GetI1(), id[1] = reader.GetI1(); |
1094 | 0 | } |
1095 | |
|
1096 | 0 | if (id[0] == 'b' && id[1] == ':') { |
1097 | 0 | mat.tex_bump = std::make_shared<Texture>(); |
1098 | |
|
1099 | 0 | reader.GetI1(); |
1100 | 0 | ReadString_Binary(mat.tex_bump->path, reader); |
1101 | |
|
1102 | 0 | mat.tex_bump->transform.mTranslation.x = reader.GetF4(); |
1103 | 0 | mat.tex_bump->transform.mTranslation.y = reader.GetF4(); |
1104 | |
|
1105 | 0 | mat.tex_bump->transform.mScaling.x = reader.GetF4(); |
1106 | 0 | mat.tex_bump->transform.mScaling.y = reader.GetF4(); |
1107 | | |
1108 | | // skip amplitude for I don't know its purpose. |
1109 | 0 | reader.GetF4(); |
1110 | 0 | } |
1111 | 0 | reader.IncPtr(-2); |
1112 | 0 | } |
1113 | | |
1114 | | // ------------------------------------------------------------------------------------------------ |
1115 | 0 | void COBImporter::ReadCame_Binary(COB::Scene &out, StreamReaderLE &reader, const ChunkInfo &nfo) { |
1116 | 0 | if (nfo.version > 2) { |
1117 | 0 | return UnsupportedChunk_Binary(reader, nfo, "Came"); |
1118 | 0 | } |
1119 | | |
1120 | 0 | const chunk_guard cn(nfo, reader); |
1121 | |
|
1122 | 0 | out.nodes.push_back(std::shared_ptr<Camera>(new Camera())); |
1123 | 0 | Camera &msh = (Camera &)(*out.nodes.back().get()); |
1124 | 0 | msh = nfo; |
1125 | |
|
1126 | 0 | ReadBasicNodeInfo_Binary(msh, reader, nfo); |
1127 | | |
1128 | | // the rest is not interesting for us, so we skip over it. |
1129 | 0 | if (nfo.version > 1) { |
1130 | 0 | if (reader.GetI2() == 512) { |
1131 | 0 | reader.IncPtr(42); |
1132 | 0 | } |
1133 | 0 | } |
1134 | 0 | } |
1135 | | |
1136 | | // ------------------------------------------------------------------------------------------------ |
1137 | 0 | void COBImporter::ReadLght_Binary(COB::Scene &out, StreamReaderLE &reader, const ChunkInfo &nfo) { |
1138 | 0 | if (nfo.version > 2) { |
1139 | 0 | return UnsupportedChunk_Binary(reader, nfo, "Lght"); |
1140 | 0 | } |
1141 | | |
1142 | 0 | const chunk_guard cn(nfo, reader); |
1143 | |
|
1144 | 0 | out.nodes.push_back(std::shared_ptr<Light>(new Light())); |
1145 | 0 | Light &msh = (Light &)(*out.nodes.back().get()); |
1146 | 0 | msh = nfo; |
1147 | |
|
1148 | 0 | ReadBasicNodeInfo_Binary(msh, reader, nfo); |
1149 | 0 | } |
1150 | | |
1151 | | // ------------------------------------------------------------------------------------------------ |
1152 | 0 | void COBImporter::ReadGrou_Binary(COB::Scene &out, StreamReaderLE &reader, const ChunkInfo &nfo) { |
1153 | 0 | if (nfo.version > 2) { |
1154 | 0 | return UnsupportedChunk_Binary(reader, nfo, "Grou"); |
1155 | 0 | } |
1156 | | |
1157 | 0 | const chunk_guard cn(nfo, reader); |
1158 | |
|
1159 | 0 | out.nodes.push_back(std::make_shared<Group>()); |
1160 | 0 | Group &msh = (Group &)(*out.nodes.back().get()); |
1161 | 0 | msh = nfo; |
1162 | |
|
1163 | 0 | ReadBasicNodeInfo_Binary(msh, reader, nfo); |
1164 | 0 | } |
1165 | | |
1166 | | // ------------------------------------------------------------------------------------------------ |
1167 | 0 | void COBImporter::ReadUnit_Binary(COB::Scene &out, StreamReaderLE &reader, const ChunkInfo &nfo) { |
1168 | 0 | if (nfo.version > 1) { |
1169 | 0 | return UnsupportedChunk_Binary(reader, nfo, "Unit"); |
1170 | 0 | } |
1171 | | |
1172 | 0 | const chunk_guard cn(nfo, reader); |
1173 | | |
1174 | | // parent chunks preceede their children, so we should have the |
1175 | | // corresponding chunk already. |
1176 | 0 | for (std::shared_ptr<Node> &nd : out.nodes) { |
1177 | 0 | if (nd->id == nfo.parent_id) { |
1178 | 0 | const unsigned int t = reader.GetI2(); |
1179 | 0 | nd->unit_scale = t >= sizeof(units) / sizeof(units[0]) ? ( |
1180 | 0 | ASSIMP_LOG_WARN(t, " is not a valid value for `Units` attribute in `Unit chunk` ", nfo.id), 1.f) : |
1181 | 0 | units[t]; |
1182 | |
|
1183 | 0 | return; |
1184 | 0 | } |
1185 | 0 | } |
1186 | 0 | ASSIMP_LOG_WARN("`Unit` chunk ", nfo.id, " is a child of ", nfo.parent_id, " which does not exist"); |
1187 | 0 | } |
1188 | | |
1189 | | } // namespace Assimp |
1190 | | |
1191 | | #endif // ASSIMP_BUILD_NO_COB_IMPORTER |