/src/assimp/code/AssetLib/Blender/BlenderLoader.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 BlenderLoader.cpp |
43 | | * @brief Implementation of the Blender3D importer class. |
44 | | */ |
45 | | |
46 | | //#define ASSIMP_BUILD_NO_COMPRESSED_BLEND |
47 | | // Uncomment this to disable support for (gzip)compressed .BLEND files |
48 | | |
49 | | #ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER |
50 | | |
51 | | #include "BlenderBMesh.h" |
52 | | #include "BlenderCustomData.h" |
53 | | #include "BlenderIntermediate.h" |
54 | | #include "BlenderModifier.h" |
55 | | #include <assimp/StringUtils.h> |
56 | | #include <assimp/importerdesc.h> |
57 | | #include <assimp/scene.h> |
58 | | |
59 | | #include <assimp/MemoryIOWrapper.h> |
60 | | #include <assimp/StreamReader.h> |
61 | | #include <assimp/StringComparison.h> |
62 | | |
63 | | #include <cctype> |
64 | | #include <memory> |
65 | | #include <utility> |
66 | | |
67 | | // zlib is needed for compressed blend files |
68 | | #ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND |
69 | | #include "Common/Compression.h" |
70 | | #endif |
71 | | |
72 | | namespace Assimp { |
73 | | |
74 | | template <> |
75 | 0 | const char *LogFunctions<BlenderImporter>::Prefix() { |
76 | 0 | return "BLEND: "; |
77 | 0 | } |
78 | | |
79 | | } // namespace Assimp |
80 | | |
81 | | using namespace Assimp; |
82 | | using namespace Assimp::Blender; |
83 | | using namespace Assimp::Formatter; |
84 | | |
85 | | static constexpr aiImporterDesc blenderDesc = { |
86 | | "Blender 3D Importer (http://www.blender3d.org)", |
87 | | "", |
88 | | "", |
89 | | "No animation support yet", |
90 | | aiImporterFlags_SupportBinaryFlavour, |
91 | | 0, |
92 | | 0, |
93 | | 2, |
94 | | 50, |
95 | | "blend" |
96 | | }; |
97 | | |
98 | | // ------------------------------------------------------------------------------------------------ |
99 | | // Constructor to be privately used by Importer |
100 | | BlenderImporter::BlenderImporter() : |
101 | 31.7k | modifier_cache(new BlenderModifierShowcase()) { |
102 | | // empty |
103 | 31.7k | } |
104 | | |
105 | | // ------------------------------------------------------------------------------------------------ |
106 | | // Destructor, private as well |
107 | 31.7k | BlenderImporter::~BlenderImporter() { |
108 | 31.7k | delete modifier_cache; |
109 | 31.7k | } |
110 | | |
111 | | static constexpr char Token[] = "BLENDER"; |
112 | | |
113 | | // ------------------------------------------------------------------------------------------------ |
114 | | // Returns whether the class can handle the format of the given file. |
115 | 186 | bool BlenderImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { |
116 | 186 | return ParseMagicToken(pFile, pIOHandler).error.empty(); |
117 | 186 | } |
118 | | |
119 | | // ------------------------------------------------------------------------------------------------ |
120 | | // Loader registry entry |
121 | 31.9k | const aiImporterDesc *BlenderImporter::GetInfo() const { |
122 | 31.9k | return &blenderDesc; |
123 | 31.9k | } |
124 | | |
125 | | // ------------------------------------------------------------------------------------------------ |
126 | | // Setup configuration properties for the loader |
127 | 26 | void BlenderImporter::SetupProperties(const Importer * /*pImp*/) { |
128 | | // nothing to be done for the moment |
129 | 26 | } |
130 | | |
131 | | // ------------------------------------------------------------------------------------------------ |
132 | | // Imports the given file into the given scene structure. |
133 | | void BlenderImporter::InternReadFile(const std::string &pFile, |
134 | 26 | aiScene *pScene, IOSystem *pIOHandler) { |
135 | 26 | FileDatabase file; |
136 | 26 | StreamOrError streamOrError = ParseMagicToken(pFile, pIOHandler); |
137 | 26 | if (!streamOrError.error.empty()) { |
138 | 0 | ThrowException(streamOrError.error); |
139 | 0 | } |
140 | 26 | std::shared_ptr<IOStream> stream = std::move(streamOrError.stream); |
141 | | |
142 | 26 | char version[4] = { 0 }; |
143 | 26 | file.i64bit = (stream->Read(version, 1, 1), version[0] == '-'); |
144 | 26 | file.little = (stream->Read(version, 1, 1), version[0] == 'v'); |
145 | | |
146 | 26 | stream->Read(version, 3, 1); |
147 | 26 | version[3] = '\0'; |
148 | | |
149 | 26 | LogInfo("Blender version is ", version[0], ".", version + 1, |
150 | 26 | " (64bit: ", file.i64bit ? "true" : "false", |
151 | 26 | ", little endian: ", file.little ? "true" : "false", ")"); |
152 | | |
153 | 26 | ParseBlendFile(file, std::move(stream)); |
154 | | |
155 | 26 | Scene scene; |
156 | 26 | ExtractScene(scene, file); |
157 | | |
158 | 26 | ConvertBlendFile(pScene, scene, file); |
159 | 26 | } |
160 | | |
161 | | // ------------------------------------------------------------------------------------------------ |
162 | 26 | void BlenderImporter::ParseBlendFile(FileDatabase &out, std::shared_ptr<IOStream> stream) { |
163 | 26 | out.reader = std::make_shared<StreamReaderAny>(stream, out.little); |
164 | | |
165 | 26 | DNAParser dna_reader(out); |
166 | 26 | const DNA *dna = nullptr; |
167 | | |
168 | 26 | out.entries.reserve(128); |
169 | 26 | { // even small BLEND files tend to consist of many file blocks |
170 | 26 | SectionParser parser(*out.reader, out.i64bit); |
171 | | |
172 | | // first parse the file in search for the DNA and insert all other sections into the database |
173 | 17.1k | while ((parser.Next(), 1)) { |
174 | 17.1k | const FileBlockHead &head = parser.GetCurrent(); |
175 | | |
176 | 17.1k | if (head.id == "ENDB") { |
177 | 20 | break; // only valid end of the file |
178 | 17.1k | } else if (head.id == "DNA1") { |
179 | 20 | dna_reader.Parse(); |
180 | 20 | dna = &dna_reader.GetDNA(); |
181 | 20 | continue; |
182 | 20 | } |
183 | | |
184 | 17.0k | out.entries.push_back(head); |
185 | 17.0k | } |
186 | 26 | } |
187 | 26 | if (!dna) { |
188 | 0 | ThrowException("SDNA not found"); |
189 | 0 | } |
190 | | |
191 | 26 | std::sort(out.entries.begin(), out.entries.end()); |
192 | 26 | } |
193 | | |
194 | | // ------------------------------------------------------------------------------------------------ |
195 | 20 | void BlenderImporter::ExtractScene(Scene &out, const FileDatabase &file) { |
196 | 20 | const FileBlockHead *block = nullptr; |
197 | 20 | std::map<std::string, size_t>::const_iterator it = file.dna.indices.find("Scene"); |
198 | 20 | if (it == file.dna.indices.end()) { |
199 | 0 | ThrowException("There is no `Scene` structure record"); |
200 | 0 | } |
201 | | |
202 | 20 | const Structure &ss = file.dna.structures[(*it).second]; |
203 | | |
204 | | // we need a scene somewhere to start with. |
205 | 11.1k | for (const FileBlockHead &bl : file.entries) { |
206 | | |
207 | | // Fix: using the DNA index is more reliable to locate scenes |
208 | | //if (bl.id == "SC") { |
209 | | |
210 | 11.1k | if (bl.dna_index == (*it).second) { |
211 | 20 | block = &bl; |
212 | 20 | break; |
213 | 20 | } |
214 | 11.1k | } |
215 | | |
216 | 20 | if (!block) { |
217 | 0 | ThrowException("There is not a single `Scene` record to load"); |
218 | 0 | } |
219 | | |
220 | 20 | file.reader->SetCurrentPos(block->start); |
221 | 20 | ss.Convert(out, file); |
222 | | |
223 | 20 | #ifndef ASSIMP_BUILD_BLENDER_NO_STATS |
224 | 20 | ASSIMP_LOG_INFO( |
225 | 20 | "(Stats) Fields read: ", file.stats().fields_read, |
226 | 20 | ", pointers resolved: ", file.stats().pointers_resolved, |
227 | 20 | ", cache hits: ", file.stats().cache_hits, |
228 | 20 | ", cached objects: ", file.stats().cached_objects); |
229 | 20 | #endif |
230 | 20 | } |
231 | | |
232 | | // ------------------------------------------------------------------------------------------------ |
233 | 2 | void BlenderImporter::ParseSubCollection(const Blender::Scene &in, aiNode *root, const std::shared_ptr<Collection>& collection, ConversionData &conv_data) { |
234 | | |
235 | 2 | std::deque<Object *> root_objects; |
236 | | // Count number of objects |
237 | 5 | for (std::shared_ptr<CollectionObject> cur = std::static_pointer_cast<CollectionObject>(collection->gobject.first); cur; cur = cur->next) { |
238 | 3 | if (cur->ob) { |
239 | 3 | root_objects.push_back(cur->ob); |
240 | 3 | } |
241 | 3 | } |
242 | 2 | std::deque<Collection *> root_children; |
243 | | // Count number of child nodes |
244 | 3 | for (std::shared_ptr<CollectionChild> cur = std::static_pointer_cast<CollectionChild>(collection->children.first); cur; cur = cur->next) { |
245 | 1 | if (cur->collection) { |
246 | 1 | root_children.push_back(cur->collection.get()); |
247 | 1 | } |
248 | 1 | } |
249 | 2 | root->mNumChildren = static_cast<unsigned int>(root_objects.size() + root_children.size()); |
250 | 2 | root->mChildren = new aiNode *[root->mNumChildren](); |
251 | | |
252 | 5 | for (unsigned int i = 0; i < static_cast<unsigned int>(root_objects.size()); ++i) { |
253 | 3 | root->mChildren[i] = ConvertNode(in, root_objects[i], conv_data, aiMatrix4x4()); |
254 | 3 | root->mChildren[i]->mParent = root; |
255 | 3 | } |
256 | | |
257 | | // For each subcollection create a new node to represent it |
258 | 2 | unsigned int iterator = static_cast<unsigned int>(root_objects.size()); |
259 | 3 | for (std::shared_ptr<CollectionChild> cur = std::static_pointer_cast<CollectionChild>(collection->children.first); cur; cur = cur->next) { |
260 | 1 | if (cur->collection) { |
261 | 1 | root->mChildren[iterator] = new aiNode(cur->collection->id.name + 2); // skip over the name prefix 'OB' |
262 | 1 | root->mChildren[iterator]->mParent = root; |
263 | 1 | ParseSubCollection(in, root->mChildren[iterator], cur->collection, conv_data); |
264 | 1 | } |
265 | 1 | iterator += 1; |
266 | 1 | } |
267 | 2 | } |
268 | | |
269 | | // ------------------------------------------------------------------------------------------------ |
270 | 19 | void BlenderImporter::ConvertBlendFile(aiScene *out, const Scene &in, const FileDatabase &file) { |
271 | 19 | ConversionData conv(file); |
272 | | |
273 | 19 | aiNode *root = out->mRootNode = new aiNode("<BlenderRoot>"); |
274 | | // Iterate over all objects directly under master_collection, |
275 | | // If in.master_collection == null, then we're parsing something older. |
276 | 19 | if (in.master_collection) { |
277 | 1 | ParseSubCollection(in, root, in.master_collection, conv); |
278 | 18 | } else { |
279 | 18 | std::deque<const Object *> no_parents; |
280 | 95 | for (std::shared_ptr<Base> cur = std::static_pointer_cast<Base>(in.base.first); cur; cur = cur->next) { |
281 | 77 | if (cur->object) { |
282 | 77 | if (!cur->object->parent) { |
283 | 68 | no_parents.push_back(cur->object.get()); |
284 | 68 | } else { |
285 | 9 | conv.objects.insert(cur->object.get()); |
286 | 9 | } |
287 | 77 | } |
288 | 77 | } |
289 | 83 | for (std::shared_ptr<Base> cur = in.basact; cur; cur = cur->next) { |
290 | 65 | if (cur->object) { |
291 | 65 | if (cur->object->parent) { |
292 | 9 | conv.objects.insert(cur->object.get()); |
293 | 9 | } |
294 | 65 | } |
295 | 65 | } |
296 | | |
297 | 18 | if (no_parents.empty()) { |
298 | 0 | ThrowException("Expected at least one object with no parent"); |
299 | 0 | } |
300 | | |
301 | 18 | root->mNumChildren = static_cast<unsigned int>(no_parents.size()); |
302 | 18 | root->mChildren = new aiNode *[root->mNumChildren](); |
303 | 86 | for (unsigned int i = 0; i < root->mNumChildren; ++i) { |
304 | 68 | root->mChildren[i] = ConvertNode(in, no_parents[i], conv, aiMatrix4x4()); |
305 | 68 | root->mChildren[i]->mParent = root; |
306 | 68 | } |
307 | 18 | } |
308 | | |
309 | 19 | BuildMaterials(conv); |
310 | | |
311 | 19 | if (conv.meshes->size()) { |
312 | 19 | out->mMeshes = new aiMesh *[out->mNumMeshes = static_cast<unsigned int>(conv.meshes->size())]; |
313 | 19 | std::copy(conv.meshes->begin(), conv.meshes->end(), out->mMeshes); |
314 | 19 | conv.meshes.dismiss(); |
315 | 19 | } |
316 | | |
317 | 19 | if (conv.lights->size()) { |
318 | 19 | out->mLights = new aiLight *[out->mNumLights = static_cast<unsigned int>(conv.lights->size())]; |
319 | 19 | std::copy(conv.lights->begin(), conv.lights->end(), out->mLights); |
320 | 19 | conv.lights.dismiss(); |
321 | 19 | } |
322 | | |
323 | 19 | if (conv.cameras->size()) { |
324 | 19 | out->mCameras = new aiCamera *[out->mNumCameras = static_cast<unsigned int>(conv.cameras->size())]; |
325 | 19 | std::copy(conv.cameras->begin(), conv.cameras->end(), out->mCameras); |
326 | 19 | conv.cameras.dismiss(); |
327 | 19 | } |
328 | | |
329 | 19 | if (conv.materials->size()) { |
330 | 19 | out->mMaterials = new aiMaterial *[out->mNumMaterials = static_cast<unsigned int>(conv.materials->size())]; |
331 | 19 | std::copy(conv.materials->begin(), conv.materials->end(), out->mMaterials); |
332 | 19 | conv.materials.dismiss(); |
333 | 19 | } |
334 | | |
335 | 19 | if (conv.textures->size()) { |
336 | 1 | out->mTextures = new aiTexture *[out->mNumTextures = static_cast<unsigned int>(conv.textures->size())]; |
337 | 1 | std::copy(conv.textures->begin(), conv.textures->end(), out->mTextures); |
338 | 1 | conv.textures.dismiss(); |
339 | 1 | } |
340 | | |
341 | | // acknowledge that the scene might come out incomplete |
342 | | // by Assimp's definition of `complete`: blender scenes |
343 | | // can consist of thousands of cameras or lights with |
344 | | // not a single mesh between them. |
345 | 19 | if (!out->mNumMeshes) { |
346 | 0 | out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; |
347 | 0 | } |
348 | 19 | } |
349 | | |
350 | | // ------------------------------------------------------------------------------------------------ |
351 | 1 | void BlenderImporter::ResolveImage(aiMaterial *out, const Material *mat, const MTex *tex, const Image *img, ConversionData &conv_data) { |
352 | 1 | (void)mat; |
353 | 1 | (void)tex; |
354 | 1 | (void)conv_data; |
355 | 1 | aiString name; |
356 | | |
357 | | // check if the file contents are bundled with the BLEND file |
358 | 1 | if (img->packedfile) { |
359 | 1 | name.data[0] = '*'; |
360 | 1 | name.length = 1 + ASSIMP_itoa10(name.data + 1, static_cast<unsigned int>(AI_MAXLEN - 1), static_cast<int32_t>(conv_data.textures->size())); |
361 | | |
362 | 1 | conv_data.textures->push_back(new aiTexture()); |
363 | 1 | aiTexture *curTex = conv_data.textures->back(); |
364 | | |
365 | | // usually 'img->name' will be the original file name of the embedded textures, |
366 | | // so we can extract the file extension from it. |
367 | 1 | const size_t nlen = strlen(img->name); |
368 | 1 | const char *s = img->name + nlen, *e = s; |
369 | 5 | while (s >= img->name && *s != '.') { |
370 | 4 | --s; |
371 | 4 | } |
372 | | |
373 | 1 | curTex->achFormatHint[0] = s + 1 > e ? '\0' : (char)::tolower((unsigned char)s[1]); |
374 | 1 | curTex->achFormatHint[1] = s + 2 > e ? '\0' : (char)::tolower((unsigned char)s[2]); |
375 | 1 | curTex->achFormatHint[2] = s + 3 > e ? '\0' : (char)::tolower((unsigned char)s[3]); |
376 | 1 | curTex->achFormatHint[3] = '\0'; |
377 | | |
378 | | // tex->mHeight = 0; |
379 | 1 | curTex->mWidth = img->packedfile->size; |
380 | 1 | uint8_t *ch = new uint8_t[curTex->mWidth]; |
381 | | |
382 | 1 | conv_data.db.reader->SetCurrentPos(static_cast<size_t>(img->packedfile->data->val)); |
383 | 1 | conv_data.db.reader->CopyAndAdvance(ch, curTex->mWidth); |
384 | | |
385 | 1 | curTex->pcData = reinterpret_cast<aiTexel *>(ch); |
386 | | |
387 | 1 | LogInfo("Reading embedded texture, original file was ", img->name); |
388 | 1 | } else { |
389 | 0 | name = aiString(img->name); |
390 | 0 | } |
391 | | |
392 | 1 | aiTextureType texture_type = aiTextureType_UNKNOWN; |
393 | 1 | MTex::MapType map_type = tex->mapto; |
394 | | |
395 | 1 | if (map_type & MTex::MapType_COL) |
396 | 1 | texture_type = aiTextureType_DIFFUSE; |
397 | 0 | else if (map_type & MTex::MapType_NORM) { |
398 | 0 | if (tex->tex->imaflag & Tex::ImageFlags_NORMALMAP) { |
399 | 0 | texture_type = aiTextureType_NORMALS; |
400 | 0 | } else { |
401 | 0 | texture_type = aiTextureType_HEIGHT; |
402 | 0 | } |
403 | 0 | out->AddProperty(&tex->norfac, 1, AI_MATKEY_BUMPSCALING); |
404 | 0 | } else if (map_type & MTex::MapType_COLSPEC) |
405 | 0 | texture_type = aiTextureType_SPECULAR; |
406 | 0 | else if (map_type & MTex::MapType_COLMIR) |
407 | 0 | texture_type = aiTextureType_REFLECTION; |
408 | | //else if (map_type & MTex::MapType_REF) |
409 | 0 | else if (map_type & MTex::MapType_SPEC) |
410 | 0 | texture_type = aiTextureType_SHININESS; |
411 | 0 | else if (map_type & MTex::MapType_EMIT) |
412 | 0 | texture_type = aiTextureType_EMISSIVE; |
413 | | //else if (map_type & MTex::MapType_ALPHA) |
414 | | //else if (map_type & MTex::MapType_HAR) |
415 | | //else if (map_type & MTex::MapType_RAYMIRR) |
416 | | //else if (map_type & MTex::MapType_TRANSLU) |
417 | 0 | else if (map_type & MTex::MapType_AMB) |
418 | 0 | texture_type = aiTextureType_AMBIENT; |
419 | 0 | else if (map_type & MTex::MapType_DISPLACE) |
420 | 0 | texture_type = aiTextureType_DISPLACEMENT; |
421 | | //else if (map_type & MTex::MapType_WARP) |
422 | | |
423 | 1 | out->AddProperty(&name, AI_MATKEY_TEXTURE(texture_type, |
424 | 1 | conv_data.next_texture[texture_type]++)); |
425 | 1 | } |
426 | | |
427 | | // ------------------------------------------------------------------------------------------------ |
428 | 0 | void BlenderImporter::AddSentinelTexture(aiMaterial *out, const Material *mat, const MTex *tex, ConversionData &conv_data) { |
429 | 0 | (void)mat; |
430 | 0 | (void)tex; |
431 | 0 | (void)conv_data; |
432 | |
|
433 | 0 | aiString name; |
434 | 0 | name.length = ai_snprintf(name.data, AI_MAXLEN, "Procedural,num=%i,type=%s", conv_data.sentinel_cnt++, |
435 | 0 | GetTextureTypeDisplayString(tex->tex->type)); |
436 | 0 | out->AddProperty(&name, AI_MATKEY_TEXTURE_DIFFUSE( |
437 | 0 | conv_data.next_texture[aiTextureType_DIFFUSE]++)); |
438 | 0 | } |
439 | | |
440 | | // ------------------------------------------------------------------------------------------------ |
441 | 18 | void BlenderImporter::ResolveTexture(aiMaterial *out, const Material *mat, const MTex *tex, ConversionData &conv_data) { |
442 | 18 | const Tex *rtex = tex->tex.get(); |
443 | 18 | if (!rtex || !rtex->type) { |
444 | 17 | return; |
445 | 17 | } |
446 | | |
447 | | // We can't support most of the texture types because they're mostly procedural. |
448 | | // These are substituted by a dummy texture. |
449 | 1 | const char *dispnam = ""; |
450 | 1 | switch (rtex->type) { |
451 | | // these are listed in blender's UI |
452 | 0 | case Tex::Type_CLOUDS: |
453 | 0 | case Tex::Type_WOOD: |
454 | 0 | case Tex::Type_MARBLE: |
455 | 0 | case Tex::Type_MAGIC: |
456 | 0 | case Tex::Type_BLEND: |
457 | 0 | case Tex::Type_STUCCI: |
458 | 0 | case Tex::Type_NOISE: |
459 | 0 | case Tex::Type_PLUGIN: |
460 | 0 | case Tex::Type_MUSGRAVE: |
461 | 0 | case Tex::Type_VORONOI: |
462 | 0 | case Tex::Type_DISTNOISE: |
463 | 0 | case Tex::Type_ENVMAP: |
464 | | |
465 | | // these do no appear in the UI, why? |
466 | 0 | case Tex::Type_POINTDENSITY: |
467 | 0 | case Tex::Type_VOXELDATA: |
468 | |
|
469 | 0 | LogWarn("Encountered a texture with an unsupported type: ", dispnam); |
470 | 0 | AddSentinelTexture(out, mat, tex, conv_data); |
471 | 0 | break; |
472 | | |
473 | 1 | case Tex::Type_IMAGE: |
474 | 1 | if (!rtex->ima) { |
475 | 0 | LogError("A texture claims to be an Image, but no image reference is given"); |
476 | 0 | break; |
477 | 0 | } |
478 | 1 | ResolveImage(out, mat, tex, rtex->ima.get(), conv_data); |
479 | 1 | break; |
480 | | |
481 | 0 | default: |
482 | 0 | ai_assert(false); |
483 | 1 | }; |
484 | 1 | } |
485 | | |
486 | | // ------------------------------------------------------------------------------------------------ |
487 | 19 | void BlenderImporter::BuildDefaultMaterial(Blender::ConversionData &conv_data) { |
488 | | // add a default material if necessary |
489 | 19 | unsigned int index = static_cast<unsigned int>(-1); |
490 | 34 | for (aiMesh *mesh : conv_data.meshes.get()) { |
491 | 34 | if (mesh->mMaterialIndex == static_cast<unsigned int>(-1)) { |
492 | | |
493 | 4 | if (index == static_cast<unsigned int>(-1)) { |
494 | | // Setup a default material. |
495 | 4 | std::shared_ptr<Material> p(new Material()); |
496 | 4 | const size_t len = ::strlen(AI_DEFAULT_MATERIAL_NAME); |
497 | 4 | ai_assert(len < sizeof(p->id.name) - 2); |
498 | 4 | memcpy(p->id.name + 2, AI_DEFAULT_MATERIAL_NAME, len); |
499 | | |
500 | | // Note: MSVC11 does not zero-initialize Material here, although it should. |
501 | | // Thus all relevant fields should be explicitly initialized. We cannot add |
502 | | // a default constructor to Material since the DNA codegen does not support |
503 | | // parsing it. |
504 | 4 | p->r = p->g = p->b = 0.6f; |
505 | 4 | p->specr = p->specg = p->specb = 0.6f; |
506 | 4 | p->ambr = p->ambg = p->ambb = 0.0f; |
507 | 4 | p->mirr = p->mirg = p->mirb = 0.0f; |
508 | 4 | p->emit = 0.f; |
509 | 4 | p->alpha = 0.f; |
510 | 4 | p->har = 0; |
511 | | |
512 | 4 | index = static_cast<unsigned int>(conv_data.materials_raw.size()); |
513 | 4 | conv_data.materials_raw.push_back(p); |
514 | 4 | LogInfo("Adding default material"); |
515 | 4 | } |
516 | 4 | mesh->mMaterialIndex = index; |
517 | 4 | } |
518 | 34 | } |
519 | 19 | } |
520 | | |
521 | 24 | void BlenderImporter::AddBlendParams(aiMaterial *result, const Material *source) { |
522 | 24 | aiColor3D diffuseColor(source->r, source->g, source->b); |
523 | 24 | result->AddProperty(&diffuseColor, 1, "$mat.blend.diffuse.color", 0, 0); |
524 | | |
525 | 24 | float diffuseIntensity = source->ref; |
526 | 24 | result->AddProperty(&diffuseIntensity, 1, "$mat.blend.diffuse.intensity", 0, 0); |
527 | | |
528 | 24 | int diffuseShader = source->diff_shader; |
529 | 24 | result->AddProperty(&diffuseShader, 1, "$mat.blend.diffuse.shader", 0, 0); |
530 | | |
531 | 24 | int diffuseRamp = 0; |
532 | 24 | result->AddProperty(&diffuseRamp, 1, "$mat.blend.diffuse.ramp", 0, 0); |
533 | | |
534 | 24 | aiColor3D specularColor(source->specr, source->specg, source->specb); |
535 | 24 | result->AddProperty(&specularColor, 1, "$mat.blend.specular.color", 0, 0); |
536 | | |
537 | 24 | float specularIntensity = source->spec; |
538 | 24 | result->AddProperty(&specularIntensity, 1, "$mat.blend.specular.intensity", 0, 0); |
539 | | |
540 | 24 | int specularShader = source->spec_shader; |
541 | 24 | result->AddProperty(&specularShader, 1, "$mat.blend.specular.shader", 0, 0); |
542 | | |
543 | 24 | int specularRamp = 0; |
544 | 24 | result->AddProperty(&specularRamp, 1, "$mat.blend.specular.ramp", 0, 0); |
545 | | |
546 | 24 | int specularHardness = source->har; |
547 | 24 | result->AddProperty(&specularHardness, 1, "$mat.blend.specular.hardness", 0, 0); |
548 | | |
549 | 24 | int transparencyUse = source->mode & MA_TRANSPARENCY ? 1 : 0; |
550 | 24 | result->AddProperty(&transparencyUse, 1, "$mat.blend.transparency.use", 0, 0); |
551 | | |
552 | 24 | int transparencyMethod = source->mode & MA_RAYTRANSP ? 2 : (source->mode & MA_ZTRANSP ? 1 : 0); |
553 | 24 | result->AddProperty(&transparencyMethod, 1, "$mat.blend.transparency.method", 0, 0); |
554 | | |
555 | 24 | float transparencyAlpha = source->alpha; |
556 | 24 | result->AddProperty(&transparencyAlpha, 1, "$mat.blend.transparency.alpha", 0, 0); |
557 | | |
558 | 24 | float transparencySpecular = source->spectra; |
559 | 24 | result->AddProperty(&transparencySpecular, 1, "$mat.blend.transparency.specular", 0, 0); |
560 | | |
561 | 24 | float transparencyFresnel = source->fresnel_tra; |
562 | 24 | result->AddProperty(&transparencyFresnel, 1, "$mat.blend.transparency.fresnel", 0, 0); |
563 | | |
564 | 24 | float transparencyBlend = source->fresnel_tra_i; |
565 | 24 | result->AddProperty(&transparencyBlend, 1, "$mat.blend.transparency.blend", 0, 0); |
566 | | |
567 | 24 | float transparencyIor = source->ang; |
568 | 24 | result->AddProperty(&transparencyIor, 1, "$mat.blend.transparency.ior", 0, 0); |
569 | | |
570 | 24 | float transparencyFilter = source->filter; |
571 | 24 | result->AddProperty(&transparencyFilter, 1, "$mat.blend.transparency.filter", 0, 0); |
572 | | |
573 | 24 | float transparencyFalloff = source->tx_falloff; |
574 | 24 | result->AddProperty(&transparencyFalloff, 1, "$mat.blend.transparency.falloff", 0, 0); |
575 | | |
576 | 24 | float transparencyLimit = source->tx_limit; |
577 | 24 | result->AddProperty(&transparencyLimit, 1, "$mat.blend.transparency.limit", 0, 0); |
578 | | |
579 | 24 | int transparencyDepth = source->ray_depth_tra; |
580 | 24 | result->AddProperty(&transparencyDepth, 1, "$mat.blend.transparency.depth", 0, 0); |
581 | | |
582 | 24 | float transparencyGlossAmount = source->gloss_tra; |
583 | 24 | result->AddProperty(&transparencyGlossAmount, 1, "$mat.blend.transparency.glossAmount", 0, 0); |
584 | | |
585 | 24 | float transparencyGlossThreshold = source->adapt_thresh_tra; |
586 | 24 | result->AddProperty(&transparencyGlossThreshold, 1, "$mat.blend.transparency.glossThreshold", 0, 0); |
587 | | |
588 | 24 | int transparencyGlossSamples = source->samp_gloss_tra; |
589 | 24 | result->AddProperty(&transparencyGlossSamples, 1, "$mat.blend.transparency.glossSamples", 0, 0); |
590 | | |
591 | 24 | int mirrorUse = source->mode & MA_RAYMIRROR ? 1 : 0; |
592 | 24 | result->AddProperty(&mirrorUse, 1, "$mat.blend.mirror.use", 0, 0); |
593 | | |
594 | 24 | float mirrorReflectivity = source->ray_mirror; |
595 | 24 | result->AddProperty(&mirrorReflectivity, 1, "$mat.blend.mirror.reflectivity", 0, 0); |
596 | | |
597 | 24 | aiColor3D mirrorColor(source->mirr, source->mirg, source->mirb); |
598 | 24 | result->AddProperty(&mirrorColor, 1, "$mat.blend.mirror.color", 0, 0); |
599 | | |
600 | 24 | float mirrorFresnel = source->fresnel_mir; |
601 | 24 | result->AddProperty(&mirrorFresnel, 1, "$mat.blend.mirror.fresnel", 0, 0); |
602 | | |
603 | 24 | float mirrorBlend = source->fresnel_mir_i; |
604 | 24 | result->AddProperty(&mirrorBlend, 1, "$mat.blend.mirror.blend", 0, 0); |
605 | | |
606 | 24 | int mirrorDepth = source->ray_depth; |
607 | 24 | result->AddProperty(&mirrorDepth, 1, "$mat.blend.mirror.depth", 0, 0); |
608 | | |
609 | 24 | float mirrorMaxDist = source->dist_mir; |
610 | 24 | result->AddProperty(&mirrorMaxDist, 1, "$mat.blend.mirror.maxDist", 0, 0); |
611 | | |
612 | 24 | int mirrorFadeTo = source->fadeto_mir; |
613 | 24 | result->AddProperty(&mirrorFadeTo, 1, "$mat.blend.mirror.fadeTo", 0, 0); |
614 | | |
615 | 24 | float mirrorGlossAmount = source->gloss_mir; |
616 | 24 | result->AddProperty(&mirrorGlossAmount, 1, "$mat.blend.mirror.glossAmount", 0, 0); |
617 | | |
618 | 24 | float mirrorGlossThreshold = source->adapt_thresh_mir; |
619 | 24 | result->AddProperty(&mirrorGlossThreshold, 1, "$mat.blend.mirror.glossThreshold", 0, 0); |
620 | | |
621 | 24 | int mirrorGlossSamples = source->samp_gloss_mir; |
622 | 24 | result->AddProperty(&mirrorGlossSamples, 1, "$mat.blend.mirror.glossSamples", 0, 0); |
623 | | |
624 | 24 | float mirrorGlossAnisotropic = source->aniso_gloss_mir; |
625 | 24 | result->AddProperty(&mirrorGlossAnisotropic, 1, "$mat.blend.mirror.glossAnisotropic", 0, 0); |
626 | 24 | } |
627 | | |
628 | 19 | void BlenderImporter::BuildMaterials(ConversionData &conv_data) { |
629 | 19 | conv_data.materials->reserve(conv_data.materials_raw.size()); |
630 | | |
631 | 19 | BuildDefaultMaterial(conv_data); |
632 | | |
633 | 24 | for (const std::shared_ptr<Material> &mat : conv_data.materials_raw) { |
634 | | |
635 | | // reset per material global counters |
636 | 480 | for (size_t i = 0; i < sizeof(conv_data.next_texture) / sizeof(conv_data.next_texture[0]); ++i) { |
637 | 456 | conv_data.next_texture[i] = 0; |
638 | 456 | } |
639 | | |
640 | 24 | aiMaterial *mout = new aiMaterial(); |
641 | 24 | conv_data.materials->push_back(mout); |
642 | | // For any new material field handled here, the default material above must be updated with an appropriate default value. |
643 | | |
644 | | // set material name |
645 | 24 | aiString name = aiString(mat->id.name + 2); // skip over the name prefix 'MA' |
646 | 24 | mout->AddProperty(&name, AI_MATKEY_NAME); |
647 | | |
648 | | // basic material colors |
649 | 24 | aiColor3D col(mat->r, mat->g, mat->b); |
650 | 24 | if (mat->r || mat->g || mat->b) { |
651 | | |
652 | | // Usually, zero diffuse color means no diffuse color at all in the equation. |
653 | | // So we omit this member to express this intent. |
654 | 24 | mout->AddProperty(&col, 1, AI_MATKEY_COLOR_DIFFUSE); |
655 | | |
656 | 24 | if (mat->emit) { |
657 | 0 | aiColor3D emit_col(mat->emit * mat->r, mat->emit * mat->g, mat->emit * mat->b); |
658 | 0 | mout->AddProperty(&emit_col, 1, AI_MATKEY_COLOR_EMISSIVE); |
659 | 0 | } |
660 | 24 | } |
661 | | |
662 | 24 | col = aiColor3D(mat->specr, mat->specg, mat->specb); |
663 | 24 | mout->AddProperty(&col, 1, AI_MATKEY_COLOR_SPECULAR); |
664 | | |
665 | | // is hardness/shininess set? |
666 | 24 | if (mat->har) { |
667 | 19 | const float har = mat->har; |
668 | 19 | mout->AddProperty(&har, 1, AI_MATKEY_SHININESS); |
669 | 19 | } |
670 | | |
671 | 24 | col = aiColor3D(mat->ambr, mat->ambg, mat->ambb); |
672 | 24 | mout->AddProperty(&col, 1, AI_MATKEY_COLOR_AMBIENT); |
673 | | |
674 | | // is mirror enabled? |
675 | 24 | if (mat->mode & MA_RAYMIRROR) { |
676 | 1 | const float ray_mirror = mat->ray_mirror; |
677 | 1 | mout->AddProperty(&ray_mirror, 1, AI_MATKEY_REFLECTIVITY); |
678 | 1 | } |
679 | | |
680 | 24 | col = aiColor3D(mat->mirr, mat->mirg, mat->mirb); |
681 | 24 | mout->AddProperty(&col, 1, AI_MATKEY_COLOR_REFLECTIVE); |
682 | | |
683 | 456 | for (size_t i = 0; i < sizeof(mat->mtex) / sizeof(mat->mtex[0]); ++i) { |
684 | 432 | if (!mat->mtex[i]) { |
685 | 414 | continue; |
686 | 414 | } |
687 | | |
688 | 18 | ResolveTexture(mout, mat.get(), mat->mtex[i].get(), conv_data); |
689 | 18 | } |
690 | | |
691 | 24 | AddBlendParams(mout, mat.get()); |
692 | 24 | } |
693 | 19 | } |
694 | | |
695 | | // ------------------------------------------------------------------------------------------------ |
696 | 77 | void BlenderImporter::CheckActualType(const ElemBase *dt, const char *check) { |
697 | 77 | ai_assert(dt); |
698 | 77 | if (strcmp(dt->dna_type, check)) { |
699 | 0 | ThrowException("Expected object at ", std::hex, dt, " to be of type `", check, |
700 | 0 | "`, but it claims to be a `", dt->dna_type, "`instead"); |
701 | 0 | } |
702 | 77 | } |
703 | | |
704 | | // ------------------------------------------------------------------------------------------------ |
705 | 0 | void BlenderImporter::NotSupportedObjectType(const Object *obj, const char *type) { |
706 | 0 | LogWarn("Object `", obj->id.name, "` - type is unsupported: `", type, "`, skipping"); |
707 | 0 | } |
708 | | |
709 | | // ------------------------------------------------------------------------------------------------ |
710 | | void BlenderImporter::ConvertMesh(const Scene & /*in*/, const Object * /*obj*/, const Mesh *mesh, |
711 | 32 | ConversionData &conv_data, TempArray<std::vector, aiMesh> &temp) { |
712 | | // TODO: Resolve various problems with BMesh triangulation before re-enabling. |
713 | | // See issues #400, #373, #318 #315 and #132. |
714 | | #if defined(TODO_FIX_BMESH_CONVERSION) |
715 | | BlenderBMeshConverter BMeshConverter(mesh); |
716 | | if (BMeshConverter.ContainsBMesh()) { |
717 | | mesh = BMeshConverter.TriangulateBMesh(); |
718 | | } |
719 | | #endif |
720 | | |
721 | 32 | typedef std::pair<const int, size_t> MyPair; |
722 | 32 | if ((!mesh->totface && !mesh->totloop) || !mesh->totvert) { |
723 | 0 | return; |
724 | 0 | } |
725 | | |
726 | | // some sanity checks |
727 | 32 | if (static_cast<size_t>(mesh->totface) > mesh->mface.size()) { |
728 | 0 | ThrowException("Number of faces is larger than the corresponding array"); |
729 | 0 | } |
730 | | |
731 | 32 | if (static_cast<size_t>(mesh->totvert) > mesh->mvert.size()) { |
732 | 0 | ThrowException("Number of vertices is larger than the corresponding array"); |
733 | 0 | } |
734 | | |
735 | 32 | if (static_cast<size_t>(mesh->totloop) > mesh->mloop.size()) { |
736 | 0 | ThrowException("Number of vertices is larger than the corresponding array"); |
737 | 0 | } |
738 | | |
739 | | // collect per-submesh numbers |
740 | 32 | std::map<int, size_t> per_mat; |
741 | 32 | std::map<int, size_t> per_mat_verts; |
742 | 5.07k | for (int i = 0; i < mesh->totface; ++i) { |
743 | | |
744 | 5.04k | const MFace &mf = mesh->mface[i]; |
745 | 5.04k | per_mat[mf.mat_nr]++; |
746 | 5.04k | per_mat_verts[mf.mat_nr] += mf.v4 ? 4 : 3; |
747 | 5.04k | } |
748 | | |
749 | 670 | for (int i = 0; i < mesh->totpoly; ++i) { |
750 | 638 | const MPoly &mp = mesh->mpoly[i]; |
751 | 638 | per_mat[mp.mat_nr]++; |
752 | 638 | per_mat_verts[mp.mat_nr] += mp.totloop; |
753 | 638 | } |
754 | | |
755 | | // ... and allocate the corresponding meshes |
756 | 32 | const size_t old = temp->size(); |
757 | 32 | temp->reserve(temp->size() + per_mat.size()); |
758 | | |
759 | 32 | std::map<size_t, size_t> mat_num_to_mesh_idx; |
760 | 32 | for (MyPair &it : per_mat) { |
761 | | |
762 | 32 | mat_num_to_mesh_idx[it.first] = temp->size(); |
763 | 32 | temp->push_back(new aiMesh()); |
764 | | |
765 | 32 | aiMesh *out = temp->back(); |
766 | 32 | out->mVertices = new aiVector3D[per_mat_verts[it.first]]; |
767 | 32 | out->mNormals = new aiVector3D[per_mat_verts[it.first]]; |
768 | | |
769 | | //out->mNumFaces = 0 |
770 | | //out->mNumVertices = 0 |
771 | 32 | out->mFaces = new aiFace[it.second](); |
772 | | |
773 | | // all sub-meshes created from this mesh are named equally. this allows |
774 | | // curious users to recover the original adjacency. |
775 | 32 | out->mName = aiString(mesh->id.name + 2); |
776 | | // skip over the name prefix 'ME' |
777 | | |
778 | | // resolve the material reference and add this material to the set of |
779 | | // output materials. The (temporary) material index is the index |
780 | | // of the material entry within the list of resolved materials. |
781 | 32 | if (mesh->mat) { |
782 | | |
783 | 28 | if (static_cast<size_t>(it.first) >= mesh->mat.size()) { |
784 | 0 | ThrowException("Material index is out of range"); |
785 | 0 | } |
786 | | |
787 | 28 | std::shared_ptr<Material> mat = mesh->mat[it.first]; |
788 | 28 | const std::deque<std::shared_ptr<Material>>::iterator has = std::find( |
789 | 28 | conv_data.materials_raw.begin(), |
790 | 28 | conv_data.materials_raw.end(), mat); |
791 | | |
792 | 28 | if (has != conv_data.materials_raw.end()) { |
793 | 8 | out->mMaterialIndex = static_cast<unsigned int>(std::distance(conv_data.materials_raw.begin(), has)); |
794 | 20 | } else { |
795 | 20 | out->mMaterialIndex = static_cast<unsigned int>(conv_data.materials_raw.size()); |
796 | 20 | conv_data.materials_raw.push_back(mat); |
797 | 20 | } |
798 | 28 | } else |
799 | 4 | out->mMaterialIndex = static_cast<unsigned int>(-1); |
800 | 32 | } |
801 | | |
802 | 5.07k | for (int i = 0; i < mesh->totface; ++i) { |
803 | | |
804 | 5.04k | const MFace &mf = mesh->mface[i]; |
805 | | |
806 | 5.04k | aiMesh *const out = temp[mat_num_to_mesh_idx[mf.mat_nr]]; |
807 | 5.04k | aiFace &f = out->mFaces[out->mNumFaces++]; |
808 | | |
809 | 5.04k | f.mIndices = new unsigned int[f.mNumIndices = mf.v4 ? 4 : 3]; |
810 | 5.04k | aiVector3D *vo = out->mVertices + out->mNumVertices; |
811 | 5.04k | aiVector3D *vn = out->mNormals + out->mNumVertices; |
812 | | |
813 | | // XXX we can't fold this easily, because we are restricted |
814 | | // to the member names from the BLEND file (v1,v2,v3,v4) |
815 | | // which are assigned by the genblenddna.py script and |
816 | | // cannot be changed without breaking the entire |
817 | | // import process. |
818 | | |
819 | 5.04k | if (mf.v1 >= mesh->totvert) { |
820 | 0 | ThrowException("Vertex index v1 out of range"); |
821 | 0 | } |
822 | 5.04k | const MVert *v = &mesh->mvert[mf.v1]; |
823 | 5.04k | vo->x = v->co[0]; |
824 | 5.04k | vo->y = v->co[1]; |
825 | 5.04k | vo->z = v->co[2]; |
826 | 5.04k | vn->x = v->no[0]; |
827 | 5.04k | vn->y = v->no[1]; |
828 | 5.04k | vn->z = v->no[2]; |
829 | 5.04k | f.mIndices[0] = out->mNumVertices++; |
830 | 5.04k | ++vo; |
831 | 5.04k | ++vn; |
832 | | |
833 | | // if (f.mNumIndices >= 2) { |
834 | 5.04k | if (mf.v2 >= mesh->totvert) { |
835 | 0 | ThrowException("Vertex index v2 out of range"); |
836 | 0 | } |
837 | 5.04k | v = &mesh->mvert[mf.v2]; |
838 | 5.04k | vo->x = v->co[0]; |
839 | 5.04k | vo->y = v->co[1]; |
840 | 5.04k | vo->z = v->co[2]; |
841 | 5.04k | vn->x = v->no[0]; |
842 | 5.04k | vn->y = v->no[1]; |
843 | 5.04k | vn->z = v->no[2]; |
844 | 5.04k | f.mIndices[1] = out->mNumVertices++; |
845 | 5.04k | ++vo; |
846 | 5.04k | ++vn; |
847 | | |
848 | 5.04k | if (mf.v3 >= mesh->totvert) { |
849 | 0 | ThrowException("Vertex index v3 out of range"); |
850 | 0 | } |
851 | | // if (f.mNumIndices >= 3) { |
852 | 5.04k | v = &mesh->mvert[mf.v3]; |
853 | 5.04k | vo->x = v->co[0]; |
854 | 5.04k | vo->y = v->co[1]; |
855 | 5.04k | vo->z = v->co[2]; |
856 | 5.04k | vn->x = v->no[0]; |
857 | 5.04k | vn->y = v->no[1]; |
858 | 5.04k | vn->z = v->no[2]; |
859 | 5.04k | f.mIndices[2] = out->mNumVertices++; |
860 | 5.04k | ++vo; |
861 | 5.04k | ++vn; |
862 | | |
863 | 5.04k | if (mf.v4 >= mesh->totvert) { |
864 | 0 | ThrowException("Vertex index v4 out of range"); |
865 | 0 | } |
866 | | // if (f.mNumIndices >= 4) { |
867 | 5.04k | if (mf.v4) { |
868 | 4.88k | v = &mesh->mvert[mf.v4]; |
869 | 4.88k | vo->x = v->co[0]; |
870 | 4.88k | vo->y = v->co[1]; |
871 | 4.88k | vo->z = v->co[2]; |
872 | 4.88k | vn->x = v->no[0]; |
873 | 4.88k | vn->y = v->no[1]; |
874 | 4.88k | vn->z = v->no[2]; |
875 | 4.88k | f.mIndices[3] = out->mNumVertices++; |
876 | 4.88k | ++vo; |
877 | 4.88k | ++vn; |
878 | | |
879 | 4.88k | out->mPrimitiveTypes |= aiPrimitiveType_POLYGON; |
880 | 4.88k | } else |
881 | 161 | out->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; |
882 | | |
883 | | // } |
884 | | // } |
885 | | // } |
886 | 5.04k | } |
887 | | |
888 | 670 | for (int i = 0; i < mesh->totpoly; ++i) { |
889 | | |
890 | 638 | const MPoly &mf = mesh->mpoly[i]; |
891 | | |
892 | 638 | aiMesh *const out = temp[mat_num_to_mesh_idx[mf.mat_nr]]; |
893 | 638 | aiFace &f = out->mFaces[out->mNumFaces++]; |
894 | | |
895 | 638 | f.mIndices = new unsigned int[f.mNumIndices = mf.totloop]; |
896 | 638 | aiVector3D *vo = out->mVertices + out->mNumVertices; |
897 | 638 | aiVector3D *vn = out->mNormals + out->mNumVertices; |
898 | | |
899 | | // XXX we can't fold this easily, because we are restricted |
900 | | // to the member names from the BLEND file (v1,v2,v3,v4) |
901 | | // which are assigned by the genblenddna.py script and |
902 | | // cannot be changed without breaking the entire |
903 | | // import process. |
904 | 3.15k | for (int j = 0; j < mf.totloop; ++j) { |
905 | 2.52k | const MLoop &loop = mesh->mloop[mf.loopstart + j]; |
906 | | |
907 | 2.52k | if (loop.v >= mesh->totvert) { |
908 | 0 | ThrowException("Vertex index out of range"); |
909 | 0 | } |
910 | | |
911 | 2.52k | const MVert &v = mesh->mvert[loop.v]; |
912 | | |
913 | 2.52k | vo->x = v.co[0]; |
914 | 2.52k | vo->y = v.co[1]; |
915 | 2.52k | vo->z = v.co[2]; |
916 | 2.52k | vn->x = v.no[0]; |
917 | 2.52k | vn->y = v.no[1]; |
918 | 2.52k | vn->z = v.no[2]; |
919 | 2.52k | f.mIndices[j] = out->mNumVertices++; |
920 | | |
921 | 2.52k | ++vo; |
922 | 2.52k | ++vn; |
923 | 2.52k | } |
924 | 638 | if (mf.totloop == 3) { |
925 | 32 | out->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; |
926 | 606 | } else { |
927 | 606 | out->mPrimitiveTypes |= aiPrimitiveType_POLYGON; |
928 | 606 | } |
929 | 638 | } |
930 | | |
931 | | // TODO should we create the TextureUVMapping map in Convert<Material> to prevent redundant processing? |
932 | | |
933 | | // create texture <-> uvname mapping for all materials |
934 | | // key is texture number, value is data * |
935 | 32 | typedef std::map<uint32_t, const MLoopUV *> TextureUVMapping; |
936 | | // key is material number, value is the TextureUVMapping for the material |
937 | 32 | typedef std::map<uint32_t, TextureUVMapping> MaterialTextureUVMappings; |
938 | 32 | MaterialTextureUVMappings matTexUvMappings; |
939 | 32 | const uint32_t maxMat = static_cast<uint32_t>(mesh->mat.size()); |
940 | 60 | for (uint32_t m = 0; m < maxMat; ++m) { |
941 | | // get material by index |
942 | 28 | const std::shared_ptr<Material> pMat = mesh->mat[m]; |
943 | 28 | TextureUVMapping texuv; |
944 | 28 | const uint32_t maxTex = sizeof(pMat->mtex) / sizeof(pMat->mtex[0]); |
945 | 532 | for (uint32_t t = 0; t < maxTex; ++t) { |
946 | 504 | if (pMat->mtex[t] && pMat->mtex[t]->uvname[0]) { |
947 | | // get the CustomData layer for given uvname and correct type |
948 | 0 | const ElemBase *pLoop = getCustomDataLayerData(mesh->ldata, CD_MLOOPUV, pMat->mtex[t]->uvname); |
949 | 0 | if (pLoop) { |
950 | 0 | texuv.insert(std::make_pair(t, dynamic_cast<const MLoopUV *>(pLoop))); |
951 | 0 | } |
952 | 0 | } |
953 | 504 | } |
954 | 28 | if (texuv.size()) { |
955 | 0 | matTexUvMappings.insert(std::make_pair(m, texuv)); |
956 | 0 | } |
957 | 28 | } |
958 | | |
959 | | // collect texture coordinates, they're stored in a separate per-face buffer |
960 | 32 | if (mesh->mtface || mesh->mloopuv) { |
961 | 3 | if (mesh->totface > static_cast<int>(mesh->mtface.size())) { |
962 | 0 | ThrowException("Number of UV faces is larger than the corresponding UV face array (#1)"); |
963 | 0 | } |
964 | 6 | for (std::vector<aiMesh *>::iterator it = temp->begin() + old; it != temp->end(); ++it) { |
965 | 3 | ai_assert(0 != (*it)->mNumVertices); |
966 | 3 | ai_assert(0 != (*it)->mNumFaces); |
967 | 3 | const auto itMatTexUvMapping = matTexUvMappings.find((*it)->mMaterialIndex); |
968 | 3 | if (itMatTexUvMapping == matTexUvMappings.end()) { |
969 | | // default behaviour like before |
970 | 3 | (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices]; |
971 | 3 | } else { |
972 | | // create texture coords for every mapped tex |
973 | 0 | for (uint32_t i = 0; i < itMatTexUvMapping->second.size(); ++i) { |
974 | 0 | (*it)->mTextureCoords[i] = new aiVector3D[(*it)->mNumVertices]; |
975 | 0 | } |
976 | 0 | } |
977 | 3 | (*it)->mNumFaces = (*it)->mNumVertices = 0; |
978 | 3 | } |
979 | | |
980 | 10 | for (int i = 0; i < mesh->totface; ++i) { |
981 | 7 | const MTFace *v = &mesh->mtface[i]; |
982 | | |
983 | 7 | aiMesh *const out = temp[mat_num_to_mesh_idx[mesh->mface[i].mat_nr]]; |
984 | 7 | const aiFace &f = out->mFaces[out->mNumFaces++]; |
985 | | |
986 | 7 | aiVector3D *vo = &out->mTextureCoords[0][out->mNumVertices]; |
987 | 35 | for (unsigned int j = 0; j < f.mNumIndices; ++j, ++vo, ++out->mNumVertices) { |
988 | 28 | vo->x = v->uv[j][0]; |
989 | 28 | vo->y = v->uv[j][1]; |
990 | 28 | } |
991 | 7 | } |
992 | | |
993 | 9 | for (int i = 0; i < mesh->totpoly; ++i) { |
994 | 6 | const MPoly &v = mesh->mpoly[i]; |
995 | 6 | aiMesh *const out = temp[mat_num_to_mesh_idx[v.mat_nr]]; |
996 | 6 | const aiFace &f = out->mFaces[out->mNumFaces++]; |
997 | | |
998 | 6 | const auto itMatTexUvMapping = matTexUvMappings.find(v.mat_nr); |
999 | 6 | if (itMatTexUvMapping == matTexUvMappings.end()) { |
1000 | | // old behavior |
1001 | 6 | aiVector3D *vo = &out->mTextureCoords[0][out->mNumVertices]; |
1002 | 30 | for (unsigned int j = 0; j < f.mNumIndices; ++j, ++vo, ++out->mNumVertices) { |
1003 | 24 | const MLoopUV &uv = mesh->mloopuv[v.loopstart + j]; |
1004 | 24 | vo->x = uv.uv[0]; |
1005 | 24 | vo->y = uv.uv[1]; |
1006 | 24 | } |
1007 | 6 | } else { |
1008 | | // create textureCoords for every mapped tex |
1009 | 0 | for (uint32_t m = 0; m < itMatTexUvMapping->second.size(); ++m) { |
1010 | 0 | const MLoopUV *tm = itMatTexUvMapping->second[m]; |
1011 | 0 | aiVector3D *vo = &out->mTextureCoords[m][out->mNumVertices]; |
1012 | 0 | uint32_t j = 0; |
1013 | 0 | for (; j < f.mNumIndices; ++j, ++vo) { |
1014 | 0 | const MLoopUV &uv = tm[v.loopstart + j]; |
1015 | 0 | vo->x = uv.uv[0]; |
1016 | 0 | vo->y = uv.uv[1]; |
1017 | 0 | } |
1018 | | // only update written mNumVertices in last loop |
1019 | | // TODO why must the numVertices be incremented here? |
1020 | 0 | if (m == itMatTexUvMapping->second.size() - 1) { |
1021 | 0 | out->mNumVertices += j; |
1022 | 0 | } |
1023 | 0 | } |
1024 | 0 | } |
1025 | 6 | } |
1026 | 3 | } |
1027 | | |
1028 | | // collect texture coordinates, old-style (marked as deprecated in current blender sources) |
1029 | 32 | if (mesh->tface) { |
1030 | 0 | if (mesh->totface > static_cast<int>(mesh->tface.size())) { |
1031 | 0 | ThrowException("Number of faces is larger than the corresponding UV face array (#2)"); |
1032 | 0 | } |
1033 | 0 | for (std::vector<aiMesh *>::iterator it = temp->begin() + old; it != temp->end(); ++it) { |
1034 | 0 | ai_assert(0 != (*it)->mNumVertices); |
1035 | 0 | ai_assert(0 != (*it)->mNumFaces); |
1036 | |
|
1037 | 0 | (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices]; |
1038 | 0 | (*it)->mNumFaces = (*it)->mNumVertices = 0; |
1039 | 0 | } |
1040 | |
|
1041 | 0 | for (int i = 0; i < mesh->totface; ++i) { |
1042 | 0 | const TFace *v = &mesh->tface[i]; |
1043 | |
|
1044 | 0 | aiMesh *const out = temp[mat_num_to_mesh_idx[mesh->mface[i].mat_nr]]; |
1045 | 0 | const aiFace &f = out->mFaces[out->mNumFaces++]; |
1046 | |
|
1047 | 0 | aiVector3D *vo = &out->mTextureCoords[0][out->mNumVertices]; |
1048 | 0 | for (unsigned int j = 0; j < f.mNumIndices; ++j, ++vo, ++out->mNumVertices) { |
1049 | 0 | vo->x = v->uv[j][0]; |
1050 | 0 | vo->y = v->uv[j][1]; |
1051 | 0 | } |
1052 | 0 | } |
1053 | 0 | } |
1054 | | |
1055 | | // collect vertex colors, stored separately as well |
1056 | 32 | if (mesh->mcol || mesh->mloopcol) { |
1057 | 1 | if (mesh->totface > static_cast<int>((mesh->mcol.size() / 4))) { |
1058 | 0 | ThrowException("Number of faces is larger than the corresponding color face array"); |
1059 | 0 | } |
1060 | 2 | for (std::vector<aiMesh *>::iterator it = temp->begin() + old; it != temp->end(); ++it) { |
1061 | 1 | ai_assert(0 != (*it)->mNumVertices); |
1062 | 1 | ai_assert(0 != (*it)->mNumFaces); |
1063 | | |
1064 | 1 | (*it)->mColors[0] = new aiColor4D[(*it)->mNumVertices]; |
1065 | 1 | (*it)->mNumFaces = (*it)->mNumVertices = 0; |
1066 | 1 | } |
1067 | | |
1068 | 7 | for (int i = 0; i < mesh->totface; ++i) { |
1069 | | |
1070 | 6 | aiMesh *const out = temp[mat_num_to_mesh_idx[mesh->mface[i].mat_nr]]; |
1071 | 6 | const aiFace &f = out->mFaces[out->mNumFaces++]; |
1072 | | |
1073 | 6 | aiColor4D *vo = &out->mColors[0][out->mNumVertices]; |
1074 | 30 | for (unsigned int n = 0; n < f.mNumIndices; ++n, ++vo, ++out->mNumVertices) { |
1075 | 24 | const MCol *col = &mesh->mcol[(i << 2) + n]; |
1076 | | |
1077 | 24 | vo->r = col->r; |
1078 | 24 | vo->g = col->g; |
1079 | 24 | vo->b = col->b; |
1080 | 24 | vo->a = col->a; |
1081 | 24 | } |
1082 | 6 | for (unsigned int n = f.mNumIndices; n < 4; ++n) |
1083 | 0 | ; |
1084 | 6 | } |
1085 | | |
1086 | 1 | for (int i = 0; i < mesh->totpoly; ++i) { |
1087 | 0 | const MPoly &v = mesh->mpoly[i]; |
1088 | 0 | aiMesh *const out = temp[mat_num_to_mesh_idx[v.mat_nr]]; |
1089 | 0 | const aiFace &f = out->mFaces[out->mNumFaces++]; |
1090 | |
|
1091 | 0 | aiColor4D *vo = &out->mColors[0][out->mNumVertices]; |
1092 | 0 | const ai_real scaleZeroToOne = 1.f / 255.f; |
1093 | 0 | for (unsigned int j = 0; j < f.mNumIndices; ++j, ++vo, ++out->mNumVertices) { |
1094 | 0 | const MLoopCol &col = mesh->mloopcol[v.loopstart + j]; |
1095 | 0 | vo->r = ai_real(col.r) * scaleZeroToOne; |
1096 | 0 | vo->g = ai_real(col.g) * scaleZeroToOne; |
1097 | 0 | vo->b = ai_real(col.b) * scaleZeroToOne; |
1098 | 0 | vo->a = ai_real(col.a) * scaleZeroToOne; |
1099 | 0 | } |
1100 | 0 | } |
1101 | 1 | } |
1102 | | |
1103 | 32 | return; |
1104 | 32 | } |
1105 | | |
1106 | | // ------------------------------------------------------------------------------------------------ |
1107 | 20 | aiCamera *BlenderImporter::ConvertCamera(const Scene & /*in*/, const Object *obj, const Camera *cam, ConversionData & /*conv_data*/) { |
1108 | 20 | std::unique_ptr<aiCamera> out(new aiCamera()); |
1109 | 20 | out->mName = obj->id.name + 2; |
1110 | 20 | out->mPosition = aiVector3D(0.f, 0.f, 0.f); |
1111 | 20 | out->mUp = aiVector3D(0.f, 1.f, 0.f); |
1112 | 20 | out->mLookAt = aiVector3D(0.f, 0.f, -1.f); |
1113 | 20 | if (cam->sensor_x && cam->lens) { |
1114 | 8 | out->mHorizontalFOV = 2.f * std::atan2(cam->sensor_x, 2.f * cam->lens); |
1115 | 8 | } |
1116 | 20 | out->mClipPlaneNear = cam->clipsta; |
1117 | 20 | out->mClipPlaneFar = cam->clipend; |
1118 | | |
1119 | 20 | return out.release(); |
1120 | 20 | } |
1121 | | |
1122 | | // ------------------------------------------------------------------------------------------------ |
1123 | 25 | aiLight *BlenderImporter::ConvertLight(const Scene & /*in*/, const Object *obj, const Lamp *lamp, ConversionData & /*conv_data*/) { |
1124 | 25 | std::unique_ptr<aiLight> out(new aiLight()); |
1125 | 25 | out->mName = obj->id.name + 2; |
1126 | | |
1127 | 25 | switch (lamp->type) { |
1128 | 23 | case Lamp::Type_Local: |
1129 | 23 | out->mType = aiLightSource_POINT; |
1130 | 23 | break; |
1131 | 0 | case Lamp::Type_Spot: |
1132 | 0 | out->mType = aiLightSource_SPOT; |
1133 | | |
1134 | | // blender orients directional lights as facing toward -z |
1135 | 0 | out->mDirection = aiVector3D(0.f, 0.f, -1.f); |
1136 | 0 | out->mUp = aiVector3D(0.f, 1.f, 0.f); |
1137 | |
|
1138 | 0 | out->mAngleInnerCone = lamp->spotsize * (1.0f - lamp->spotblend); |
1139 | 0 | out->mAngleOuterCone = lamp->spotsize; |
1140 | 0 | break; |
1141 | 0 | case Lamp::Type_Sun: |
1142 | 0 | out->mType = aiLightSource_DIRECTIONAL; |
1143 | | |
1144 | | // blender orients directional lights as facing toward -z |
1145 | 0 | out->mDirection = aiVector3D(0.f, 0.f, -1.f); |
1146 | 0 | out->mUp = aiVector3D(0.f, 1.f, 0.f); |
1147 | 0 | break; |
1148 | | |
1149 | 2 | case Lamp::Type_Area: |
1150 | 2 | out->mType = aiLightSource_AREA; |
1151 | | |
1152 | 2 | if (lamp->area_shape == 0) { |
1153 | 1 | out->mSize = aiVector2D(lamp->area_size, lamp->area_size); |
1154 | 1 | } else { |
1155 | 1 | out->mSize = aiVector2D(lamp->area_size, lamp->area_sizey); |
1156 | 1 | } |
1157 | | |
1158 | | // blender orients directional lights as facing toward -z |
1159 | 2 | out->mDirection = aiVector3D(0.f, 0.f, -1.f); |
1160 | 2 | out->mUp = aiVector3D(0.f, 1.f, 0.f); |
1161 | 2 | break; |
1162 | | |
1163 | 0 | default: |
1164 | 0 | break; |
1165 | 25 | } |
1166 | | |
1167 | 25 | out->mColorAmbient = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy; |
1168 | 25 | out->mColorSpecular = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy; |
1169 | 25 | out->mColorDiffuse = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy; |
1170 | | |
1171 | | // If default values are supplied, compute the coefficients from light's max distance |
1172 | | // Read this: https://imdoingitwrong.wordpress.com/2011/01/31/light-attenuation/ |
1173 | | // |
1174 | 25 | if (lamp->constant_coefficient == 1.0f && lamp->linear_coefficient == 0.0f && lamp->quadratic_coefficient == 0.0f && lamp->dist > 0.0f) { |
1175 | 0 | out->mAttenuationConstant = 1.0f; |
1176 | 0 | out->mAttenuationLinear = 2.0f / lamp->dist; |
1177 | 0 | out->mAttenuationQuadratic = 1.0f / (lamp->dist * lamp->dist); |
1178 | 25 | } else { |
1179 | 25 | out->mAttenuationConstant = lamp->constant_coefficient; |
1180 | 25 | out->mAttenuationLinear = lamp->linear_coefficient; |
1181 | 25 | out->mAttenuationQuadratic = lamp->quadratic_coefficient; |
1182 | 25 | } |
1183 | | |
1184 | 25 | return out.release(); |
1185 | 25 | } |
1186 | | |
1187 | | // ------------------------------------------------------------------------------------------------ |
1188 | 77 | aiNode *BlenderImporter::ConvertNode(const Scene &in, const Object *obj, ConversionData &conv_data, const aiMatrix4x4 &parentTransform) { |
1189 | 77 | std::deque<const Object *> children; |
1190 | 120 | for (ObjectSet::iterator it = conv_data.objects.begin(); it != conv_data.objects.end();) { |
1191 | 43 | const Object *object = *it; |
1192 | 43 | if (object->parent == obj) { |
1193 | 6 | children.push_back(object); |
1194 | | |
1195 | 6 | conv_data.objects.erase(it++); |
1196 | 6 | continue; |
1197 | 6 | } |
1198 | 37 | ++it; |
1199 | 37 | } |
1200 | | |
1201 | 77 | std::unique_ptr<aiNode> node(new aiNode(obj->id.name + 2)); // skip over the name prefix 'OB' |
1202 | 77 | if (obj->data) { |
1203 | 77 | switch (obj->type) { |
1204 | 0 | case Object ::Type_EMPTY: |
1205 | 0 | break; // do nothing |
1206 | | |
1207 | | // supported object types |
1208 | 32 | case Object ::Type_MESH: { |
1209 | 32 | const size_t old = conv_data.meshes->size(); |
1210 | | |
1211 | 32 | CheckActualType(obj->data.get(), "Mesh"); |
1212 | 32 | ConvertMesh(in, obj, static_cast<const Mesh *>(obj->data.get()), conv_data, conv_data.meshes); |
1213 | | |
1214 | 32 | if (conv_data.meshes->size() > old) { |
1215 | 32 | node->mMeshes = new unsigned int[node->mNumMeshes = static_cast<unsigned int>(conv_data.meshes->size() - old)]; |
1216 | 64 | for (unsigned int i = 0; i < node->mNumMeshes; ++i) { |
1217 | 32 | node->mMeshes[i] = static_cast<unsigned int>(i + old); |
1218 | 32 | } |
1219 | 32 | } |
1220 | 32 | } break; |
1221 | 25 | case Object ::Type_LAMP: { |
1222 | 25 | CheckActualType(obj->data.get(), "Lamp"); |
1223 | 25 | aiLight *mesh = ConvertLight(in, obj, static_cast<const Lamp *>(obj->data.get()), conv_data); |
1224 | | |
1225 | 25 | if (mesh) { |
1226 | 25 | conv_data.lights->push_back(mesh); |
1227 | 25 | } |
1228 | 25 | } break; |
1229 | 20 | case Object ::Type_CAMERA: { |
1230 | 20 | CheckActualType(obj->data.get(), "Camera"); |
1231 | 20 | aiCamera *mesh = ConvertCamera(in, obj, static_cast<const Camera *>(obj->data.get()), conv_data); |
1232 | | |
1233 | 20 | if (mesh) { |
1234 | 20 | conv_data.cameras->push_back(mesh); |
1235 | 20 | } |
1236 | 20 | } break; |
1237 | | |
1238 | | // unsupported object types / log, but do not break |
1239 | 0 | case Object ::Type_CURVE: |
1240 | 0 | NotSupportedObjectType(obj, "Curve"); |
1241 | 0 | break; |
1242 | 0 | case Object ::Type_SURF: |
1243 | 0 | NotSupportedObjectType(obj, "Surface"); |
1244 | 0 | break; |
1245 | 0 | case Object ::Type_FONT: |
1246 | 0 | NotSupportedObjectType(obj, "Font"); |
1247 | 0 | break; |
1248 | 0 | case Object ::Type_MBALL: |
1249 | 0 | NotSupportedObjectType(obj, "MetaBall"); |
1250 | 0 | break; |
1251 | 0 | case Object ::Type_WAVE: |
1252 | 0 | NotSupportedObjectType(obj, "Wave"); |
1253 | 0 | break; |
1254 | 0 | case Object ::Type_LATTICE: |
1255 | 0 | NotSupportedObjectType(obj, "Lattice"); |
1256 | 0 | break; |
1257 | | |
1258 | | // invalid or unknown type |
1259 | 0 | default: |
1260 | 0 | break; |
1261 | 77 | } |
1262 | 77 | } |
1263 | | |
1264 | 385 | for (unsigned int x = 0; x < 4; ++x) { |
1265 | 1.54k | for (unsigned int y = 0; y < 4; ++y) { |
1266 | 1.23k | node->mTransformation[y][x] = obj->obmat[x][y]; |
1267 | 1.23k | } |
1268 | 308 | } |
1269 | | |
1270 | 77 | aiMatrix4x4 m = parentTransform; |
1271 | 77 | m = m.Inverse(); |
1272 | | |
1273 | 77 | node->mTransformation = m * node->mTransformation; |
1274 | | |
1275 | 77 | if (children.size()) { |
1276 | 4 | node->mNumChildren = static_cast<unsigned int>(children.size()); |
1277 | 4 | aiNode **nd = node->mChildren = new aiNode *[node->mNumChildren](); |
1278 | 6 | for (const Object *nobj : children) { |
1279 | 6 | *nd = ConvertNode(in, nobj, conv_data, node->mTransformation * parentTransform); |
1280 | 6 | (*nd++)->mParent = node.get(); |
1281 | 6 | } |
1282 | 4 | } |
1283 | | |
1284 | | // apply modifiers |
1285 | 77 | modifier_cache->ApplyModifiers(*node, conv_data, in, *obj); |
1286 | | |
1287 | 77 | return node.release(); |
1288 | 77 | } |
1289 | | |
1290 | 212 | BlenderImporter::StreamOrError BlenderImporter::ParseMagicToken(const std::string &pFile, IOSystem *pIOHandler) const { |
1291 | 212 | std::shared_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb")); |
1292 | 212 | if (stream == nullptr) { |
1293 | 0 | return {{}, {}, "Could not open file for reading"}; |
1294 | 0 | } |
1295 | | |
1296 | 212 | char magic[8] = { 0 }; |
1297 | 212 | stream->Read(magic, 7, 1); |
1298 | 212 | if (strcmp(magic, Token) == 0) { |
1299 | 44 | return {stream, {}, {}}; |
1300 | 44 | } |
1301 | | |
1302 | | // Check for presence of the gzip header. If yes, assume it is a |
1303 | | // compressed blend file and try uncompressing it, else fail. This is to |
1304 | | // avoid uncompressing random files which our loader might end up with. |
1305 | | #ifdef ASSIMP_BUILD_NO_COMPRESSED_BLEND |
1306 | | return {{}, {}, "BLENDER magic bytes are missing, is this file compressed (Assimp was built without decompression support)?"}; |
1307 | | #else |
1308 | 168 | if (magic[0] != 0x1f || static_cast<uint8_t>(magic[1]) != 0x8b) { |
1309 | 134 | return {{}, {}, "BLENDER magic bytes are missing, couldn't find GZIP header either"}; |
1310 | 134 | } |
1311 | | |
1312 | 34 | LogDebug("Found no BLENDER magic word but a GZIP header, might be a compressed file"); |
1313 | 34 | if (magic[2] != 8) { |
1314 | 0 | return {{}, {}, "Unsupported GZIP compression method"}; |
1315 | 0 | } |
1316 | | |
1317 | | // http://www.gzip.org/zlib/rfc-gzip.html#header-trailer |
1318 | 34 | stream->Seek(0L, aiOrigin_SET); |
1319 | 34 | std::shared_ptr<StreamReaderLE> reader = std::shared_ptr<StreamReaderLE>(new StreamReaderLE(stream)); |
1320 | | |
1321 | 34 | size_t total = 0; |
1322 | 34 | Compression compression; |
1323 | 34 | auto uncompressed = std::make_shared<std::vector<char>>(); |
1324 | 34 | if (compression.open(Compression::Format::Binary, Compression::FlushMode::NoFlush, 16 + Compression::MaxWBits)) { |
1325 | 34 | total = compression.decompress((unsigned char *)reader->GetPtr(), reader->GetRemainingSize(), *uncompressed); |
1326 | 34 | compression.close(); |
1327 | 34 | } |
1328 | | |
1329 | | // replace the input stream with a memory stream |
1330 | 34 | stream = std::make_shared<MemoryIOStream>(reinterpret_cast<uint8_t *>(uncompressed->data()), total); |
1331 | | |
1332 | | // .. and retry |
1333 | 34 | stream->Read(magic, 7, 1); |
1334 | 34 | if (strcmp(magic, Token) == 0) { |
1335 | 8 | return {stream, uncompressed, {}}; |
1336 | 8 | } |
1337 | 26 | return {{}, {}, "Found no BLENDER magic word in decompressed GZIP file"}; |
1338 | 34 | #endif |
1339 | 34 | } |
1340 | | |
1341 | | #endif // ASSIMP_BUILD_NO_BLEND_IMPORTER |