/src/assimp/code/AssetLib/AC/ACLoader.cpp
Line | Count | Source |
1 | | /* |
2 | | --------------------------------------------------------------------------- |
3 | | Open Asset Import Library (assimp) |
4 | | --------------------------------------------------------------------------- |
5 | | |
6 | | Copyright (c) 2006-2026, assimp team |
7 | | |
8 | | All rights reserved. |
9 | | |
10 | | Redistribution and use of this software in source and binary forms, |
11 | | with or without modification, are permitted provided that the following |
12 | | conditions are met: |
13 | | |
14 | | * Redistributions of source code must retain the above |
15 | | copyright notice, this list of conditions and the |
16 | | following disclaimer. |
17 | | |
18 | | * Redistributions in binary form must reproduce the above |
19 | | copyright notice, this list of conditions and the |
20 | | following disclaimer in the documentation and/or other |
21 | | materials provided with the distribution. |
22 | | |
23 | | * Neither the name of the assimp team, nor the names of its |
24 | | contributors may be used to endorse or promote products |
25 | | derived from this software without specific prior |
26 | | written permission of the assimp team. |
27 | | |
28 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
29 | | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
30 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
31 | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
32 | | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
33 | | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
34 | | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
35 | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
36 | | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
37 | | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
38 | | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
39 | | --------------------------------------------------------------------------- |
40 | | */ |
41 | | |
42 | | /** @file Implementation of the AC3D importer class */ |
43 | | |
44 | | #ifndef ASSIMP_BUILD_NO_AC_IMPORTER |
45 | | |
46 | | // internal headers |
47 | | #include "ACLoader.h" |
48 | | #include "Common/Importer.h" |
49 | | #include <assimp/BaseImporter.h> |
50 | | #include <assimp/ParsingUtils.h> |
51 | | #include <assimp/Subdivision.h> |
52 | | #include <assimp/config.h> |
53 | | #include <assimp/fast_atof.h> |
54 | | #include <assimp/importerdesc.h> |
55 | | #include <assimp/light.h> |
56 | | #include <assimp/material.h> |
57 | | #include <assimp/scene.h> |
58 | | #include <assimp/DefaultLogger.hpp> |
59 | | #include <assimp/IOSystem.hpp> |
60 | | #include <assimp/Importer.hpp> |
61 | | #include <memory> |
62 | | |
63 | | namespace Assimp { |
64 | | |
65 | | static constexpr aiImporterDesc desc = { |
66 | | "AC3D Importer", |
67 | | "", |
68 | | "", |
69 | | "", |
70 | | aiImporterFlags_SupportTextFlavour, |
71 | | 0, |
72 | | 0, |
73 | | 0, |
74 | | 0, |
75 | | "ac acc ac3d" |
76 | | }; |
77 | | |
78 | | static constexpr auto ACDoubleSidedFlag = 0x20; |
79 | | |
80 | | // ------------------------------------------------------------------------------------------------ |
81 | | // skip to the next token |
82 | 81.0k | inline const char *AcSkipToNextToken(const char *buffer, const char *end) { |
83 | 81.0k | if (!SkipSpaces(&buffer, end)) { |
84 | 0 | ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL"); |
85 | 0 | } |
86 | 81.0k | return buffer; |
87 | 81.0k | } |
88 | | |
89 | | // ------------------------------------------------------------------------------------------------ |
90 | | // read a string (may be enclosed in double quotation marks). buffer must point to " |
91 | 19 | inline const char *AcGetString(const char *buffer, const char *end, std::string &out) { |
92 | 19 | if (*buffer == '\0') { |
93 | 0 | throw DeadlyImportError("AC3D: Unexpected EOF in string"); |
94 | 0 | } |
95 | 19 | ++buffer; |
96 | 19 | const char *sz = buffer; |
97 | 248 | while ('\"' != *buffer && buffer != end) { |
98 | 232 | if (IsLineEnd(*buffer)) { |
99 | 3 | ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL in string"); |
100 | 3 | out = "ERROR"; |
101 | 3 | break; |
102 | 3 | } |
103 | 229 | ++buffer; |
104 | 229 | } |
105 | 19 | if (IsLineEnd(*buffer)) { |
106 | 3 | return buffer; |
107 | 3 | } |
108 | 16 | out = std::string(sz, (unsigned int)(buffer - sz)); |
109 | 16 | ++buffer; |
110 | | |
111 | 16 | return buffer; |
112 | 19 | } |
113 | | |
114 | | // ------------------------------------------------------------------------------------------------ |
115 | | // read 1 to n floats prefixed with an optional predefined identifier |
116 | | template <class T> |
117 | 24.7k | inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *end, const char *name, size_t name_length, size_t num, T *out) { |
118 | 24.7k | buffer = AcSkipToNextToken(buffer, end); |
119 | 24.7k | if (0 != name_length) { |
120 | 30 | if (0 != strncmp(buffer, name, name_length) || !IsSpace(buffer[name_length])) { |
121 | 0 | ASSIMP_LOG_ERROR("AC3D: Unexpected token. ", name, " was expected."); |
122 | 0 | return buffer; |
123 | 0 | } |
124 | 30 | buffer += name_length + 1; |
125 | 30 | } |
126 | 81.0k | for (unsigned int _i = 0; _i < num; ++_i) { |
127 | 56.3k | buffer = AcSkipToNextToken(buffer, end); |
128 | 56.3k | buffer = fast_atoreal_move(buffer, ((float *)out)[_i]); |
129 | 56.3k | } |
130 | | |
131 | 24.7k | return buffer; |
132 | 24.7k | } char const* Assimp::TAcCheckedLoadFloatArray<aiVector2t<float> >(char const*, char const*, char const*, unsigned long, unsigned long, aiVector2t<float>*) Line | Count | Source | 117 | 17.9k | inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *end, const char *name, size_t name_length, size_t num, T *out) { | 118 | 17.9k | buffer = AcSkipToNextToken(buffer, end); | 119 | 17.9k | if (0 != name_length) { | 120 | 0 | if (0 != strncmp(buffer, name, name_length) || !IsSpace(buffer[name_length])) { | 121 | 0 | ASSIMP_LOG_ERROR("AC3D: Unexpected token. ", name, " was expected."); | 122 | 0 | return buffer; | 123 | 0 | } | 124 | 0 | buffer += name_length + 1; | 125 | 0 | } | 126 | 53.8k | for (unsigned int _i = 0; _i < num; ++_i) { | 127 | 35.8k | buffer = AcSkipToNextToken(buffer, end); | 128 | 35.8k | buffer = fast_atoreal_move(buffer, ((float *)out)[_i]); | 129 | 35.8k | } | 130 | | | 131 | 17.9k | return buffer; | 132 | 17.9k | } |
Unexecuted instantiation: char const* Assimp::TAcCheckedLoadFloatArray<aiMatrix3x3t<float> >(char const*, char const*, char const*, unsigned long, unsigned long, aiMatrix3x3t<float>*) char const* Assimp::TAcCheckedLoadFloatArray<aiVector3t<float> >(char const*, char const*, char const*, unsigned long, unsigned long, aiVector3t<float>*) Line | Count | Source | 117 | 5 | inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *end, const char *name, size_t name_length, size_t num, T *out) { | 118 | 5 | buffer = AcSkipToNextToken(buffer, end); | 119 | 5 | if (0 != name_length) { | 120 | 0 | if (0 != strncmp(buffer, name, name_length) || !IsSpace(buffer[name_length])) { | 121 | 0 | ASSIMP_LOG_ERROR("AC3D: Unexpected token. ", name, " was expected."); | 122 | 0 | return buffer; | 123 | 0 | } | 124 | 0 | buffer += name_length + 1; | 125 | 0 | } | 126 | 20 | for (unsigned int _i = 0; _i < num; ++_i) { | 127 | 15 | buffer = AcSkipToNextToken(buffer, end); | 128 | 15 | buffer = fast_atoreal_move(buffer, ((float *)out)[_i]); | 129 | 15 | } | 130 | | | 131 | 5 | return buffer; | 132 | 5 | } |
char const* Assimp::TAcCheckedLoadFloatArray<float>(char const*, char const*, char const*, unsigned long, unsigned long, float*) Line | Count | Source | 117 | 6.79k | inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *end, const char *name, size_t name_length, size_t num, T *out) { | 118 | 6.79k | buffer = AcSkipToNextToken(buffer, end); | 119 | 6.79k | if (0 != name_length) { | 120 | 10 | if (0 != strncmp(buffer, name, name_length) || !IsSpace(buffer[name_length])) { | 121 | 0 | ASSIMP_LOG_ERROR("AC3D: Unexpected token. ", name, " was expected."); | 122 | 0 | return buffer; | 123 | 0 | } | 124 | 10 | buffer += name_length + 1; | 125 | 10 | } | 126 | 27.1k | for (unsigned int _i = 0; _i < num; ++_i) { | 127 | 20.3k | buffer = AcSkipToNextToken(buffer, end); | 128 | 20.3k | buffer = fast_atoreal_move(buffer, ((float *)out)[_i]); | 129 | 20.3k | } | 130 | | | 131 | 6.79k | return buffer; | 132 | 6.79k | } |
char const* Assimp::TAcCheckedLoadFloatArray<aiColor3D>(char const*, char const*, char const*, unsigned long, unsigned long, aiColor3D*) Line | Count | Source | 117 | 20 | inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *end, const char *name, size_t name_length, size_t num, T *out) { | 118 | 20 | buffer = AcSkipToNextToken(buffer, end); | 119 | 20 | if (0 != name_length) { | 120 | 20 | if (0 != strncmp(buffer, name, name_length) || !IsSpace(buffer[name_length])) { | 121 | 0 | ASSIMP_LOG_ERROR("AC3D: Unexpected token. ", name, " was expected."); | 122 | 0 | return buffer; | 123 | 0 | } | 124 | 20 | buffer += name_length + 1; | 125 | 20 | } | 126 | 80 | for (unsigned int _i = 0; _i < num; ++_i) { | 127 | 60 | buffer = AcSkipToNextToken(buffer, end); | 128 | 60 | buffer = fast_atoreal_move(buffer, ((float *)out)[_i]); | 129 | 60 | } | 130 | | | 131 | 20 | return buffer; | 132 | 20 | } |
|
133 | | |
134 | | // ------------------------------------------------------------------------------------------------ |
135 | | // Reverses vertex indices in a face. |
136 | 4.63k | static void flipWindingOrder(aiFace &f) { |
137 | 4.63k | std::reverse(f.mIndices, f.mIndices + f.mNumIndices); |
138 | 4.63k | } |
139 | | |
140 | | // ------------------------------------------------------------------------------------------------ |
141 | | // Duplicates a face and inverts it. Also duplicates all vertices (so the new face gets its own |
142 | | // set of normals and isn’t smoothed against the original). |
143 | | static void buildBacksideOfFace(const aiFace &origFace, aiFace *&outFaces, aiVector3D *&outVertices, const aiVector3D *allVertices, |
144 | 4.63k | aiVector3D *&outUV, const aiVector3D *allUV, unsigned &curIdx) { |
145 | 4.63k | auto &newFace = *outFaces++; |
146 | 4.63k | newFace = origFace; |
147 | 4.63k | flipWindingOrder(newFace); |
148 | 18.5k | for (unsigned f = 0; f < newFace.mNumIndices; ++f) { |
149 | 13.8k | *outVertices++ = allVertices[newFace.mIndices[f]]; |
150 | 13.8k | if (outUV) { |
151 | 0 | *outUV = allUV[newFace.mIndices[f]]; |
152 | 0 | outUV++; |
153 | 0 | } |
154 | 13.8k | newFace.mIndices[f] = curIdx++; |
155 | 13.8k | } |
156 | 4.63k | } |
157 | | |
158 | | // ------------------------------------------------------------------------------------------------ |
159 | | // Constructor to be privately used by Importer |
160 | | AC3DImporter::AC3DImporter() : |
161 | 40.0k | mBuffer(), |
162 | | configSplitBFCull(), |
163 | | configEvalSubdivision(), |
164 | | mNumMeshes(), |
165 | | mLights(), |
166 | 40.0k | mLightsCounter(0), |
167 | 40.0k | mGroupsCounter(0), |
168 | 40.0k | mPolysCounter(0), |
169 | 40.0k | mWorldsCounter(0) { |
170 | | // nothing to be done here |
171 | 40.0k | } |
172 | | |
173 | | // ------------------------------------------------------------------------------------------------ |
174 | | // Returns whether the class can handle the format of the given file. |
175 | 530 | bool AC3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { |
176 | 530 | static constexpr uint32_t tokens[] = { AI_MAKE_MAGIC("AC3D") }; |
177 | 530 | return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); |
178 | 530 | } |
179 | | |
180 | | // ------------------------------------------------------------------------------------------------ |
181 | | // Loader meta information |
182 | 40.0k | const aiImporterDesc *AC3DImporter::GetInfo() const { |
183 | 40.0k | return &desc; |
184 | 40.0k | } |
185 | | |
186 | | // ------------------------------------------------------------------------------------------------ |
187 | | // Get a pointer to the next line from the file |
188 | 42.3k | bool AC3DImporter::GetNextLine() { |
189 | 42.3k | SkipLine(&mBuffer.data, mBuffer.end); |
190 | 42.3k | return SkipSpaces(&mBuffer.data, mBuffer.end); |
191 | 42.3k | } |
192 | | |
193 | | // ------------------------------------------------------------------------------------------------ |
194 | | // Parse an object section in an AC file |
195 | 13 | bool AC3DImporter::LoadObjectSection(std::vector<Object> &objects) { |
196 | 13 | if (!TokenMatch(mBuffer.data, "OBJECT", 6)) { |
197 | 0 | return false; |
198 | 0 | } |
199 | | |
200 | 13 | SkipSpaces(&mBuffer.data, mBuffer.end); |
201 | | |
202 | 13 | ++mNumMeshes; |
203 | | |
204 | 13 | objects.emplace_back(); |
205 | 13 | Object &obj = objects.back(); |
206 | | |
207 | 13 | aiLight *light = nullptr; |
208 | 13 | if (!ASSIMP_strincmp(mBuffer.data, "light", 5)) { |
209 | | // This is a light source. Add it to the list |
210 | 3 | mLights->push_back(light = new aiLight()); |
211 | | |
212 | | // Return a point light with no attenuation |
213 | 3 | light->mType = aiLightSource_POINT; |
214 | 3 | light->mColorDiffuse = light->mColorSpecular = aiColor3D(1.f, 1.f, 1.f); |
215 | 3 | light->mAttenuationConstant = 1.f; |
216 | | |
217 | | // Generate a default name for both the light source and the node |
218 | 3 | light->mName.length = ::ai_snprintf(light->mName.data, AI_MAXLEN, "ACLight_%i", static_cast<unsigned int>(mLights->size()) - 1); |
219 | 3 | obj.name = std::string(light->mName.data); |
220 | | |
221 | 3 | ASSIMP_LOG_VERBOSE_DEBUG("AC3D: Light source encountered"); |
222 | 3 | obj.type = Object::Light; |
223 | 10 | } else if (!ASSIMP_strincmp(mBuffer.data, "group", 5)) { |
224 | 0 | obj.type = Object::Group; |
225 | 10 | } else if (!ASSIMP_strincmp(mBuffer.data, "world", 5)) { |
226 | 5 | obj.type = Object::World; |
227 | 5 | } else { |
228 | 5 | obj.type = Object::Poly; |
229 | 5 | } |
230 | | |
231 | 51 | while (GetNextLine()) { |
232 | 51 | if (TokenMatch(mBuffer.data, "kids", 4)) { |
233 | 13 | SkipSpaces(&mBuffer.data, mBuffer.end); |
234 | 13 | unsigned int num = strtoul10(mBuffer.data, &mBuffer.data); |
235 | 13 | GetNextLine(); |
236 | 13 | if (num) { |
237 | | // load the children of this object recursively |
238 | 5 | obj.children.reserve(num); |
239 | 13 | for (unsigned int i = 0; i < num; ++i) { |
240 | 8 | if (!LoadObjectSection(obj.children)) { |
241 | 0 | ASSIMP_LOG_WARN("AC3D: wrong number of kids"); |
242 | 0 | break; |
243 | 0 | } |
244 | 8 | } |
245 | 5 | } |
246 | 13 | return true; |
247 | 38 | } else if (TokenMatch(mBuffer.data, "name", 4)) { |
248 | 8 | SkipSpaces(&mBuffer.data, mBuffer.data); |
249 | 8 | mBuffer.data = AcGetString(mBuffer.data, mBuffer.end, obj.name); |
250 | | |
251 | | // If this is a light source, we'll also need to store |
252 | | // the name of the node in it. |
253 | 8 | if (light) { |
254 | 3 | light->mName.Set(obj.name); |
255 | 3 | } |
256 | 30 | } else if (TokenMatch(mBuffer.data, "texture", 7)) { |
257 | 6 | SkipSpaces(&mBuffer.data, mBuffer.end); |
258 | 6 | std::string texture; |
259 | 6 | mBuffer.data = AcGetString(mBuffer.data, mBuffer.end, texture); |
260 | 6 | obj.textures.push_back(texture); |
261 | 24 | } else if (TokenMatch(mBuffer.data, "texrep", 6)) { |
262 | 1 | SkipSpaces(&mBuffer.data, mBuffer.end); |
263 | 1 | mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 2, &obj.texRepeat); |
264 | 1 | if (!obj.texRepeat.x || !obj.texRepeat.y) |
265 | 0 | obj.texRepeat = aiVector2D(1.f, 1.f); |
266 | 23 | } else if (TokenMatch(mBuffer.data, "texoff", 6)) { |
267 | 1 | SkipSpaces(&mBuffer.data, mBuffer.end); |
268 | 1 | mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 2, &obj.texOffset); |
269 | 22 | } else if (TokenMatch(mBuffer.data, "rot", 3)) { |
270 | 0 | SkipSpaces(&mBuffer.data, mBuffer.end); |
271 | 0 | mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 9, &obj.rotation); |
272 | 22 | } else if (TokenMatch(mBuffer.data, "loc", 3)) { |
273 | 5 | SkipSpaces(&mBuffer.data, mBuffer.end); |
274 | 5 | mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 3, &obj.translation); |
275 | 17 | } else if (TokenMatch(mBuffer.data, "subdiv", 6)) { |
276 | 2 | SkipSpaces(&mBuffer.data, mBuffer.end); |
277 | 2 | obj.subDiv = strtoul10(mBuffer.data, &mBuffer.data); |
278 | 15 | } else if (TokenMatch(mBuffer.data, "crease", 6)) { |
279 | 3 | SkipSpaces(&mBuffer.data, mBuffer.end); |
280 | 3 | obj.crease = fast_atof(mBuffer.data); |
281 | 12 | } else if (TokenMatch(mBuffer.data, "numvert", 7)) { |
282 | 5 | SkipSpaces(&mBuffer.data, mBuffer.end); |
283 | | |
284 | 5 | unsigned int t = strtoul10(mBuffer.data, &mBuffer.data); |
285 | 5 | if (t >= AI_MAX_ALLOC(aiVector3D)) { |
286 | 0 | throw DeadlyImportError("AC3D: Too many vertices, would run out of memory"); |
287 | 0 | } |
288 | 5 | obj.vertices.reserve(t); |
289 | 6.79k | for (unsigned int i = 0; i < t; ++i) { |
290 | 6.79k | if (!GetNextLine()) { |
291 | 0 | ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: not all vertices have been parsed yet"); |
292 | 0 | break; |
293 | 6.79k | } else if (!IsNumeric(*mBuffer.data)) { |
294 | 1 | ASSIMP_LOG_ERROR("AC3D: Unexpected token: not all vertices have been parsed yet"); |
295 | 1 | --mBuffer.data; // make sure the line is processed a second time |
296 | 1 | break; |
297 | 1 | } |
298 | 6.78k | obj.vertices.emplace_back(); |
299 | 6.78k | aiVector3D &v = obj.vertices.back(); |
300 | 6.78k | mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 3, &v.x); |
301 | 6.78k | } |
302 | 7 | } else if (TokenMatch(mBuffer.data, "numsurf", 7)) { |
303 | 5 | SkipSpaces(&mBuffer.data, mBuffer.end); |
304 | | |
305 | 5 | bool Q3DWorkAround = false; |
306 | | |
307 | 5 | const unsigned int t = strtoul10(mBuffer.data, &mBuffer.data); |
308 | 5 | obj.surfaces.reserve(t); |
309 | 4.38k | for (unsigned int i = 0; i < t; ++i) { |
310 | 4.37k | GetNextLine(); |
311 | 4.37k | if (!TokenMatch(mBuffer.data, "SURF", 4)) { |
312 | | // FIX: this can occur for some files - Quick 3D for |
313 | | // example writes no surf chunks |
314 | 0 | if (!Q3DWorkAround) { |
315 | 0 | ASSIMP_LOG_WARN("AC3D: SURF token was expected"); |
316 | 0 | ASSIMP_LOG_VERBOSE_DEBUG("Continuing with Quick3D Workaround enabled"); |
317 | 0 | } |
318 | 0 | --mBuffer.data; // make sure the line is processed a second time |
319 | | // break; --- see fix notes above |
320 | |
|
321 | 0 | Q3DWorkAround = true; |
322 | 0 | } |
323 | 4.37k | SkipSpaces(&mBuffer.data, mBuffer.end); |
324 | 4.37k | obj.surfaces.emplace_back(); |
325 | 4.37k | Surface &surf = obj.surfaces.back(); |
326 | 4.37k | surf.flags = strtoul_cppstyle(mBuffer.data); |
327 | | |
328 | 13.1k | while (true) { |
329 | 13.1k | if (!GetNextLine()) { |
330 | 0 | throw DeadlyImportError("AC3D: Unexpected EOF: surface is incomplete"); |
331 | 0 | } |
332 | 13.1k | if (TokenMatch(mBuffer.data, "mat", 3)) { |
333 | 4.37k | SkipSpaces(&mBuffer.data, mBuffer.end); |
334 | 4.37k | surf.mat = strtoul10(mBuffer.data); |
335 | 8.75k | } else if (TokenMatch(mBuffer.data, "refs", 4)) { |
336 | | // --- see fix notes above |
337 | 4.37k | if (Q3DWorkAround) { |
338 | 0 | if (!surf.entries.empty()) { |
339 | 0 | mBuffer.data -= 6; |
340 | 0 | break; |
341 | 0 | } |
342 | 0 | } |
343 | | |
344 | 4.37k | SkipSpaces(&mBuffer.data, mBuffer.end); |
345 | 4.37k | const unsigned int m = strtoul10(mBuffer.data); |
346 | 4.37k | surf.entries.reserve(m); |
347 | | |
348 | 4.37k | obj.numRefs += m; |
349 | | |
350 | 22.3k | for (unsigned int k = 0; k < m; ++k) { |
351 | 17.9k | if (!GetNextLine()) { |
352 | 0 | ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: surface references are incomplete"); |
353 | 0 | break; |
354 | 0 | } |
355 | 17.9k | surf.entries.emplace_back(); |
356 | 17.9k | Surface::SurfaceEntry &entry = surf.entries.back(); |
357 | | |
358 | 17.9k | entry.first = strtoul10(mBuffer.data, &mBuffer.data); |
359 | 17.9k | SkipSpaces(&mBuffer.data, mBuffer.end); |
360 | 17.9k | mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 2, &entry.second); |
361 | 17.9k | } |
362 | 4.37k | } else { |
363 | 4.37k | --mBuffer.data; // make sure the line is processed a second time |
364 | 4.37k | break; |
365 | 4.37k | } |
366 | 13.1k | } |
367 | 4.37k | } |
368 | 5 | } |
369 | 51 | } |
370 | 13 | ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: \'kids\' line was expected"); |
371 | |
|
372 | 0 | return false; |
373 | 13 | } |
374 | | |
375 | | // ------------------------------------------------------------------------------------------------ |
376 | | // Convert a material from AC3DImporter::Material to aiMaterial |
377 | | void AC3DImporter::ConvertMaterial(const Object &object, |
378 | | const Material &matSrc, |
379 | 5 | aiMaterial &matDest) { |
380 | 5 | aiString s; |
381 | | |
382 | 5 | if (matSrc.name.length()) { |
383 | 5 | s.Set(matSrc.name); |
384 | 5 | matDest.AddProperty(&s, AI_MATKEY_NAME); |
385 | 5 | } |
386 | 5 | if (!object.textures.empty()) { |
387 | 3 | s.Set(object.textures[0]); |
388 | 3 | matDest.AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0)); |
389 | | |
390 | | // UV transformation |
391 | 3 | if (1.f != object.texRepeat.x || 1.f != object.texRepeat.y || |
392 | 2 | object.texOffset.x || object.texOffset.y) { |
393 | 1 | aiUVTransform transform; |
394 | 1 | transform.mScaling = object.texRepeat; |
395 | 1 | transform.mTranslation = object.texOffset; |
396 | 1 | matDest.AddProperty(&transform, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0)); |
397 | 1 | } |
398 | 3 | } |
399 | | |
400 | 5 | matDest.AddProperty<aiColor3D>(&matSrc.rgb, 1, AI_MATKEY_COLOR_DIFFUSE); |
401 | 5 | matDest.AddProperty<aiColor3D>(&matSrc.amb, 1, AI_MATKEY_COLOR_AMBIENT); |
402 | 5 | matDest.AddProperty<aiColor3D>(&matSrc.emis, 1, AI_MATKEY_COLOR_EMISSIVE); |
403 | 5 | matDest.AddProperty<aiColor3D>(&matSrc.spec, 1, AI_MATKEY_COLOR_SPECULAR); |
404 | | |
405 | 5 | int n = -1; |
406 | 5 | if (matSrc.shin) { |
407 | 3 | n = aiShadingMode_Phong; |
408 | 3 | matDest.AddProperty<float>(&matSrc.shin, 1, AI_MATKEY_SHININESS); |
409 | 3 | } else { |
410 | 2 | n = aiShadingMode_Gouraud; |
411 | 2 | } |
412 | 5 | matDest.AddProperty<int>(&n, 1, AI_MATKEY_SHADING_MODEL); |
413 | | |
414 | 5 | float f = 1.f - matSrc.trans; |
415 | 5 | matDest.AddProperty<float>(&f, 1, AI_MATKEY_OPACITY); |
416 | 5 | } |
417 | | |
418 | | // ------------------------------------------------------------------------------------------------ |
419 | | // Converts the loaded data to the internal verbose representation |
420 | | aiNode *AC3DImporter::ConvertObjectSection(Object &object, |
421 | | std::vector<aiMesh *> &meshes, |
422 | | std::vector<aiMaterial *> &outMaterials, |
423 | | const std::vector<Material> &materials, |
424 | 13 | aiNode *parent) { |
425 | 13 | aiNode *node = new aiNode(); |
426 | 13 | node->mParent = parent; |
427 | 13 | if (object.vertices.size()) { |
428 | 5 | if (!object.surfaces.size() || !object.numRefs) { |
429 | | /* " An object with 7 vertices (no surfaces, no materials defined). |
430 | | This is a good way of getting point data into AC3D. |
431 | | The Vertex->create convex-surface/object can be used on these |
432 | | vertices to 'wrap' a 3d shape around them " |
433 | | (http://www.opencity.info/html/ac3dfileformat.html) |
434 | | |
435 | | therefore: if no surfaces are defined return point data only |
436 | | */ |
437 | |
|
438 | 0 | ASSIMP_LOG_INFO("AC3D: No surfaces defined in object definition, " |
439 | 0 | "a point list is returned"); |
440 | |
|
441 | 0 | meshes.push_back(new aiMesh()); |
442 | 0 | aiMesh *mesh = meshes.back(); |
443 | |
|
444 | 0 | mesh->mNumFaces = mesh->mNumVertices = (unsigned int)object.vertices.size(); |
445 | 0 | aiFace *faces = mesh->mFaces = new aiFace[mesh->mNumFaces]; |
446 | 0 | aiVector3D *verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; |
447 | |
|
448 | 0 | for (unsigned int i = 0; i < mesh->mNumVertices; ++i, ++faces, ++verts) { |
449 | 0 | *verts = object.vertices[i]; |
450 | 0 | faces->mNumIndices = 1; |
451 | 0 | faces->mIndices = new unsigned int[1]; |
452 | 0 | faces->mIndices[0] = i; |
453 | 0 | } |
454 | | |
455 | | // use the primary material in this case. this should be the |
456 | | // default material if all objects of the file contain points |
457 | | // and no faces. |
458 | 0 | mesh->mMaterialIndex = 0; |
459 | 0 | outMaterials.push_back(new aiMaterial()); |
460 | 0 | ConvertMaterial(object, materials[0], *outMaterials.back()); |
461 | 5 | } else { |
462 | | // need to generate one or more meshes for this object. |
463 | | // find out how many different materials we have |
464 | 5 | typedef std::pair<unsigned int, unsigned int> IntPair; |
465 | 5 | typedef std::vector<IntPair> MatTable; |
466 | 5 | MatTable needMat(materials.size(), IntPair(0, 0)); |
467 | | |
468 | 5 | std::vector<Surface>::iterator it, end = object.surfaces.end(); |
469 | 5 | std::vector<Surface::SurfaceEntry>::iterator it2, end2; |
470 | | |
471 | 4.38k | for (it = object.surfaces.begin(); it != end; ++it) { |
472 | 4.37k | unsigned int idx = (*it).mat; |
473 | 4.37k | if (idx >= needMat.size()) { |
474 | 0 | ASSIMP_LOG_ERROR("AC3D: material index is out of range"); |
475 | 0 | idx = 0; |
476 | 0 | } |
477 | 4.37k | if ((*it).entries.empty()) { |
478 | 0 | ASSIMP_LOG_WARN("AC3D: surface has zero vertex references"); |
479 | 0 | } |
480 | 4.37k | const bool isDoubleSided = ACDoubleSidedFlag == (it->flags & ACDoubleSidedFlag); |
481 | 4.37k | const int doubleSidedFactor = isDoubleSided ? 2 : 1; |
482 | | |
483 | | // validate all vertex indices to make sure we won't crash here |
484 | 4.37k | for (it2 = (*it).entries.begin(), |
485 | 4.37k | end2 = (*it).entries.end(); |
486 | 22.3k | it2 != end2; ++it2) { |
487 | 17.9k | if ((*it2).first >= object.vertices.size()) { |
488 | 35 | ASSIMP_LOG_WARN("AC3D: Invalid vertex reference"); |
489 | 35 | (*it2).first = 0; |
490 | 35 | } |
491 | 17.9k | } |
492 | | |
493 | 4.37k | if (!needMat[idx].first) { |
494 | 5 | ++node->mNumMeshes; |
495 | 5 | } |
496 | | |
497 | 4.37k | switch ((*it).GetType()) { |
498 | 0 | case Surface::ClosedLine: // closed line |
499 | 0 | needMat[idx].first += static_cast<unsigned int>((*it).entries.size()); |
500 | 0 | needMat[idx].second += static_cast<unsigned int>((*it).entries.size() << 1u); |
501 | 0 | break; |
502 | | |
503 | | // unclosed line |
504 | 0 | case Surface::OpenLine: |
505 | 0 | needMat[idx].first += static_cast<unsigned int>((*it).entries.size() - 1); |
506 | 0 | needMat[idx].second += static_cast<unsigned int>(((*it).entries.size() - 1) << 1u); |
507 | 0 | break; |
508 | | |
509 | | // triangle strip |
510 | 358 | case Surface::TriangleStrip: |
511 | 358 | needMat[idx].first += static_cast<unsigned int>(it->entries.size() - 2) * doubleSidedFactor; |
512 | 358 | needMat[idx].second += static_cast<unsigned int>(it->entries.size() - 2) * 3 * doubleSidedFactor; |
513 | 358 | break; |
514 | | |
515 | 0 | default: |
516 | | // Coerce unknowns to a polygon and warn |
517 | 0 | ASSIMP_LOG_WARN("AC3D: The type flag of a surface is unknown: ", (*it).flags); |
518 | 0 | (*it).flags &= ~(Surface::Mask); |
519 | | // fallthrough |
520 | | |
521 | | // polygon |
522 | 4.02k | case Surface::Polygon: |
523 | | // the number of faces increments by one, the number |
524 | | // of vertices by surface.numref. |
525 | 4.02k | needMat[idx].first += doubleSidedFactor; |
526 | 4.02k | needMat[idx].second += static_cast<unsigned int>(it->entries.size()) * doubleSidedFactor; |
527 | 4.37k | }; |
528 | 4.37k | } |
529 | 5 | unsigned int *pip = node->mMeshes = new unsigned int[node->mNumMeshes]; |
530 | 5 | unsigned int mat = 0; |
531 | 5 | const size_t oldm = meshes.size(); |
532 | 5 | for (MatTable::const_iterator cit = needMat.begin(), cend = needMat.end(); |
533 | 10 | cit != cend; ++cit, ++mat) { |
534 | 5 | if (!(*cit).first) { |
535 | 0 | continue; |
536 | 0 | } |
537 | | |
538 | | // allocate a new aiMesh object |
539 | 5 | *pip++ = (unsigned int)meshes.size(); |
540 | 5 | aiMesh *mesh = new aiMesh(); |
541 | 5 | meshes.push_back(mesh); |
542 | | |
543 | 5 | mesh->mMaterialIndex = static_cast<unsigned int>(outMaterials.size()); |
544 | 5 | outMaterials.push_back(new aiMaterial()); |
545 | 5 | ConvertMaterial(object, materials[mat], *outMaterials.back()); |
546 | | |
547 | | // allocate storage for vertices and normals |
548 | 5 | mesh->mNumFaces = (*cit).first; |
549 | 5 | if (mesh->mNumFaces == 0) { |
550 | 0 | throw DeadlyImportError("AC3D: No faces"); |
551 | 5 | } else if (mesh->mNumFaces > AI_MAX_ALLOC(aiFace)) { |
552 | 0 | throw DeadlyImportError("AC3D: Too many faces, would run out of memory"); |
553 | 0 | } |
554 | 5 | aiFace *faces = mesh->mFaces = new aiFace[mesh->mNumFaces]; |
555 | | |
556 | 5 | mesh->mNumVertices = (*cit).second; |
557 | 5 | if (mesh->mNumVertices == 0) { |
558 | 0 | throw DeadlyImportError("AC3D: No vertices"); |
559 | 5 | } else if (mesh->mNumVertices > AI_MAX_ALLOC(aiVector3D)) { |
560 | 0 | throw DeadlyImportError("AC3D: Too many vertices, would run out of memory"); |
561 | 0 | } |
562 | 5 | aiVector3D *vertices = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; |
563 | 5 | unsigned int cur = 0; |
564 | | |
565 | | // allocate UV coordinates, but only if the texture name for the |
566 | | // surface is not empty |
567 | 5 | aiVector3D *uv = nullptr; |
568 | 5 | if (!object.textures.empty()) { |
569 | 3 | uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; |
570 | 3 | mesh->mNumUVComponents[0] = 2; |
571 | 3 | } |
572 | | |
573 | 4.38k | for (it = object.surfaces.begin(); it != end; ++it) { |
574 | 4.37k | if (mat == (*it).mat) { |
575 | 4.37k | const Surface &src = *it; |
576 | 4.37k | const bool isDoubleSided = ACDoubleSidedFlag == (src.flags & ACDoubleSidedFlag); |
577 | | |
578 | | // closed polygon |
579 | 4.37k | uint8_t type = (*it).GetType(); |
580 | 4.37k | if (type == Surface::Polygon) { |
581 | 4.02k | aiFace &face = *faces++; |
582 | 4.02k | face.mNumIndices = (unsigned int)src.entries.size(); |
583 | 4.02k | if (0 != face.mNumIndices) { |
584 | 4.02k | face.mIndices = new unsigned int[face.mNumIndices]; |
585 | 16.3k | for (unsigned int i = 0; i < face.mNumIndices; ++i, ++vertices) { |
586 | 12.3k | const Surface::SurfaceEntry &entry = src.entries[i]; |
587 | 12.3k | face.mIndices[i] = cur++; |
588 | | |
589 | | // copy vertex positions |
590 | 12.3k | if (static_cast<unsigned>(vertices - mesh->mVertices) >= mesh->mNumVertices) { |
591 | 0 | throw DeadlyImportError("AC3D: Invalid number of vertices"); |
592 | 0 | } |
593 | 12.3k | *vertices = object.vertices[entry.first] + object.translation; |
594 | | |
595 | | // copy texture coordinates |
596 | 12.3k | if (uv) { |
597 | 1.10k | uv->x = entry.second.x; |
598 | 1.10k | uv->y = entry.second.y; |
599 | 1.10k | ++uv; |
600 | 1.10k | } |
601 | 12.3k | } |
602 | 4.02k | if(isDoubleSided) // Need a backface? |
603 | 0 | buildBacksideOfFace(faces[-1], faces, vertices, mesh->mVertices, uv, mesh->mTextureCoords[0], cur); |
604 | 4.02k | } |
605 | 4.02k | } else if (type == Surface::TriangleStrip) { |
606 | 5.27k | for (unsigned int i = 0; i < (unsigned int)src.entries.size() - 2; ++i) { |
607 | 4.91k | const Surface::SurfaceEntry &entry1 = src.entries[i]; |
608 | 4.91k | const Surface::SurfaceEntry &entry2 = src.entries[i + 1]; |
609 | 4.91k | const Surface::SurfaceEntry &entry3 = src.entries[i + 2]; |
610 | 4.91k | const unsigned int verticesNeeded = isDoubleSided ? 6 : 3; |
611 | 4.91k | if (static_cast<unsigned>(vertices - mesh->mVertices) + verticesNeeded > mesh->mNumVertices) { |
612 | 0 | throw DeadlyImportError("AC3D: Invalid number of vertices"); |
613 | 0 | } |
614 | | |
615 | 4.91k | aiFace &face = *faces++; |
616 | 4.91k | face.mNumIndices = 3; |
617 | 4.91k | face.mIndices = new unsigned int[face.mNumIndices]; |
618 | 4.91k | face.mIndices[0] = cur++; |
619 | 4.91k | face.mIndices[1] = cur++; |
620 | 4.91k | face.mIndices[2] = cur++; |
621 | 4.91k | if (!(i & 1)) { |
622 | 2.55k | *vertices++ = object.vertices[entry1.first] + object.translation; |
623 | 2.55k | if (uv) { |
624 | 144 | uv->x = entry1.second.x; |
625 | 144 | uv->y = entry1.second.y; |
626 | 144 | ++uv; |
627 | 144 | } |
628 | 2.55k | *vertices++ = object.vertices[entry2.first] + object.translation; |
629 | 2.55k | if (uv) { |
630 | 144 | uv->x = entry2.second.x; |
631 | 144 | uv->y = entry2.second.y; |
632 | 144 | ++uv; |
633 | 144 | } |
634 | 2.55k | } else { |
635 | 2.36k | *vertices++ = object.vertices[entry2.first] + object.translation; |
636 | 2.36k | if (uv) { |
637 | 142 | uv->x = entry2.second.x; |
638 | 142 | uv->y = entry2.second.y; |
639 | 142 | ++uv; |
640 | 142 | } |
641 | 2.36k | *vertices++ = object.vertices[entry1.first] + object.translation; |
642 | 2.36k | if (uv) { |
643 | 142 | uv->x = entry1.second.x; |
644 | 142 | uv->y = entry1.second.y; |
645 | 142 | ++uv; |
646 | 142 | } |
647 | 2.36k | } |
648 | 4.91k | if (static_cast<unsigned>(vertices - mesh->mVertices) >= mesh->mNumVertices) { |
649 | 0 | throw DeadlyImportError("AC3D: Invalid number of vertices"); |
650 | 0 | } |
651 | 4.91k | *vertices++ = object.vertices[entry3.first] + object.translation; |
652 | 4.91k | if (uv) { |
653 | 286 | uv->x = entry3.second.x; |
654 | 286 | uv->y = entry3.second.y; |
655 | 286 | ++uv; |
656 | 286 | } |
657 | 4.91k | if(isDoubleSided) // Need a backface? |
658 | 4.63k | buildBacksideOfFace(faces[-1], faces, vertices, mesh->mVertices, uv, mesh->mTextureCoords[0], cur); |
659 | 4.91k | } |
660 | 358 | } else { |
661 | |
|
662 | 0 | it2 = (*it).entries.begin(); |
663 | | |
664 | | // either a closed or an unclosed line |
665 | 0 | unsigned int tmp = (unsigned int)(*it).entries.size(); |
666 | 0 | if (Surface::OpenLine == type) --tmp; |
667 | 0 | for (unsigned int m = 0; m < tmp; ++m) { |
668 | 0 | if (static_cast<unsigned>(vertices - mesh->mVertices) + 2 > mesh->mNumVertices) { |
669 | 0 | throw DeadlyImportError("AC3D: Invalid number of vertices"); |
670 | 0 | } |
671 | | |
672 | 0 | aiFace &face = *faces++; |
673 | |
|
674 | 0 | face.mNumIndices = 2; |
675 | 0 | face.mIndices = new unsigned int[2]; |
676 | 0 | face.mIndices[0] = cur++; |
677 | 0 | face.mIndices[1] = cur++; |
678 | | |
679 | | // copy vertex positions |
680 | 0 | if (it2 == (*it).entries.end()) { |
681 | 0 | throw DeadlyImportError("AC3D: Bad line"); |
682 | 0 | } |
683 | 0 | ai_assert((*it2).first < object.vertices.size()); |
684 | 0 | *vertices++ = object.vertices[(*it2).first]; |
685 | | |
686 | | // copy texture coordinates |
687 | 0 | if (uv) { |
688 | 0 | uv->x = (*it2).second.x; |
689 | 0 | uv->y = (*it2).second.y; |
690 | 0 | ++uv; |
691 | 0 | } |
692 | |
|
693 | 0 | if (Surface::ClosedLine == type && tmp - 1 == m) { |
694 | | // if this is a closed line repeat its beginning now |
695 | 0 | it2 = (*it).entries.begin(); |
696 | 0 | } else |
697 | 0 | ++it2; |
698 | | |
699 | | // second point |
700 | 0 | *vertices++ = object.vertices[(*it2).first]; |
701 | |
|
702 | 0 | if (uv) { |
703 | 0 | uv->x = (*it2).second.x; |
704 | 0 | uv->y = (*it2).second.y; |
705 | 0 | ++uv; |
706 | 0 | } |
707 | 0 | } |
708 | 0 | } |
709 | 4.37k | } |
710 | 4.37k | } |
711 | 5 | } |
712 | | |
713 | | // Now apply catmull clark subdivision if necessary. We split meshes into |
714 | | // materials which is not done by AC3D during smoothing, so we need to |
715 | | // collect all meshes using the same material group. |
716 | 5 | if (object.subDiv) { |
717 | 2 | if (configEvalSubdivision) { |
718 | 2 | std::unique_ptr<Subdivider> div(Subdivider::Create(Subdivider::CATMULL_CLARKE)); |
719 | 2 | ASSIMP_LOG_INFO("AC3D: Evaluating subdivision surface: ", object.name); |
720 | | |
721 | 2 | std::vector<aiMesh *> cpy(meshes.size() - oldm, nullptr); |
722 | 2 | div->Subdivide(&meshes[oldm], cpy.size(), &cpy.front(), object.subDiv, true); |
723 | 2 | std::copy(cpy.begin(), cpy.end(), meshes.begin() + oldm); |
724 | | |
725 | | // previous meshes are deleted vy Subdivide(). |
726 | 2 | } else { |
727 | 0 | ASSIMP_LOG_INFO("AC3D: Letting the subdivision surface untouched due to my configuration: ", object.name); |
728 | 0 | } |
729 | 2 | } |
730 | 5 | } |
731 | 5 | } |
732 | | |
733 | 13 | if (object.name.length()) |
734 | 8 | node->mName.Set(object.name); |
735 | 5 | else { |
736 | | // generate a name depending on the type of the node |
737 | 5 | switch (object.type) { |
738 | 0 | case Object::Group: |
739 | 0 | node->mName.length = ::ai_snprintf(node->mName.data, AI_MAXLEN, "ACGroup_%i", mGroupsCounter++); |
740 | 0 | break; |
741 | 0 | case Object::Poly: |
742 | 0 | node->mName.length = ::ai_snprintf(node->mName.data, AI_MAXLEN, "ACPoly_%i", mPolysCounter++); |
743 | 0 | break; |
744 | 0 | case Object::Light: |
745 | 0 | node->mName.length = ::ai_snprintf(node->mName.data, AI_MAXLEN, "ACLight_%i", mLightsCounter++); |
746 | 0 | break; |
747 | | |
748 | | // there shouldn't be more than one world, but we don't care |
749 | 5 | case Object::World: |
750 | 5 | node->mName.length = ::ai_snprintf(node->mName.data, AI_MAXLEN, "ACWorld_%i", mWorldsCounter++); |
751 | 5 | break; |
752 | 5 | } |
753 | 5 | } |
754 | | |
755 | | // setup the local transformation matrix of the object |
756 | | // compute the transformation offset to the parent node |
757 | 13 | node->mTransformation = aiMatrix4x4(object.rotation); |
758 | | |
759 | 13 | if (object.type == Object::Group || !object.numRefs) { |
760 | 8 | node->mTransformation.a4 = object.translation.x; |
761 | 8 | node->mTransformation.b4 = object.translation.y; |
762 | 8 | node->mTransformation.c4 = object.translation.z; |
763 | 8 | } |
764 | | |
765 | | // add children to the object |
766 | 13 | if (object.children.size()) { |
767 | 5 | node->mNumChildren = (unsigned int)object.children.size(); |
768 | 5 | node->mChildren = new aiNode *[node->mNumChildren]; |
769 | 13 | for (unsigned int i = 0; i < node->mNumChildren; ++i) { |
770 | 8 | node->mChildren[i] = ConvertObjectSection(object.children[i], meshes, outMaterials, materials, node); |
771 | 8 | } |
772 | 5 | } |
773 | | |
774 | 13 | return node; |
775 | 13 | } |
776 | | |
777 | | // ------------------------------------------------------------------------------------------------ |
778 | 5 | void AC3DImporter::SetupProperties(const Importer *pImp) { |
779 | 5 | configSplitBFCull = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL, 1) ? true : false; |
780 | 5 | configEvalSubdivision = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION, 1) ? true : false; |
781 | 5 | } |
782 | | |
783 | | // ------------------------------------------------------------------------------------------------ |
784 | | // Imports the given file into the given scene structure. |
785 | | void AC3DImporter::InternReadFile(const std::string &pFile, |
786 | 5 | aiScene *pScene, IOSystem *pIOHandler) { |
787 | 5 | std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb")); |
788 | | |
789 | | // Check whether we can read from the file |
790 | 5 | if (file == nullptr) { |
791 | 0 | throw DeadlyImportError("Failed to open AC3D file ", pFile, "."); |
792 | 0 | } |
793 | | |
794 | | // allocate storage and copy the contents of the file to a memory buffer |
795 | 5 | std::vector<char> mBuffer2; |
796 | 5 | TextFileToBuffer(file.get(), mBuffer2); |
797 | | |
798 | 5 | mBuffer.data = &mBuffer2[0]; |
799 | 5 | mBuffer.end = &mBuffer2[0] + mBuffer2.size(); |
800 | 5 | mNumMeshes = 0; |
801 | | |
802 | 5 | mLightsCounter = mPolysCounter = mWorldsCounter = mGroupsCounter = 0; |
803 | | |
804 | 5 | if (::strncmp(mBuffer.data, "AC3D", 4)) { |
805 | 0 | throw DeadlyImportError("AC3D: No valid AC3D file, magic sequence not found"); |
806 | 0 | } |
807 | | |
808 | | // print the file format version to the console |
809 | 5 | unsigned int version = HexDigitToDecimal(mBuffer.data[4]); |
810 | 5 | char msg[3]; |
811 | 5 | ASSIMP_itoa10(msg, 3, version); |
812 | 5 | ASSIMP_LOG_INFO("AC3D file format version: ", msg); |
813 | | |
814 | 5 | std::vector<Material> materials; |
815 | 5 | materials.reserve(5); |
816 | | |
817 | 5 | std::vector<Object> rootObjects; |
818 | 5 | rootObjects.reserve(5); |
819 | | |
820 | 5 | std::vector<aiLight *> lights; |
821 | 5 | mLights = &lights; |
822 | | |
823 | 15 | while (GetNextLine()) { |
824 | 10 | if (TokenMatch(mBuffer.data, "MATERIAL", 8)) { |
825 | 5 | materials.emplace_back(); |
826 | 5 | Material &mat = materials.back(); |
827 | | |
828 | | // manually parse the material ... sscanf would use the buldin atof ... |
829 | | // Format: (name) rgb %f %f %f amb %f %f %f emis %f %f %f spec %f %f %f shi %d trans %f |
830 | | |
831 | 5 | mBuffer.data = AcSkipToNextToken(mBuffer.data, mBuffer.end); |
832 | 5 | if ('\"' == *mBuffer.data) { |
833 | 5 | mBuffer.data = AcGetString(mBuffer.data, mBuffer.end, mat.name); |
834 | 5 | mBuffer.data = AcSkipToNextToken(mBuffer.data, mBuffer.end); |
835 | 5 | } |
836 | | |
837 | 5 | mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "rgb", 3, 3, &mat.rgb); |
838 | 5 | mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "amb", 3, 3, &mat.amb); |
839 | 5 | mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "emis", 4, 3, &mat.emis); |
840 | 5 | mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "spec", 4, 3, &mat.spec); |
841 | 5 | mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "shi", 3, 1, &mat.shin); |
842 | 5 | mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "trans", 5, 1, &mat.trans); |
843 | 5 | } else { |
844 | 5 | LoadObjectSection(rootObjects); |
845 | 5 | } |
846 | 10 | } |
847 | | |
848 | 5 | if (rootObjects.empty() || mNumMeshes == 0u) { |
849 | 0 | throw DeadlyImportError("AC3D: No meshes have been loaded"); |
850 | 0 | } |
851 | 5 | if (materials.empty()) { |
852 | 0 | ASSIMP_LOG_WARN("AC3D: No material has been found"); |
853 | 0 | materials.emplace_back(); |
854 | 0 | } |
855 | | |
856 | 5 | mNumMeshes += (mNumMeshes >> 2u) + 1; |
857 | 5 | std::vector<aiMesh *> meshes; |
858 | 5 | meshes.reserve(mNumMeshes); |
859 | | |
860 | 5 | std::vector<aiMaterial *> omaterials; |
861 | 5 | materials.reserve(mNumMeshes); |
862 | | |
863 | | // generate a dummy root if there are multiple objects on the top layer |
864 | 5 | Object *root = nullptr; |
865 | 5 | if (1 == rootObjects.size()) |
866 | 5 | root = &rootObjects[0]; |
867 | 0 | else { |
868 | 0 | root = new Object(); |
869 | 0 | } |
870 | | |
871 | | // now convert the imported stuff to our output data structure |
872 | 5 | pScene->mRootNode = ConvertObjectSection(*root, meshes, omaterials, materials); |
873 | 5 | if (1 != rootObjects.size()) { |
874 | 0 | delete root; |
875 | 0 | } |
876 | | |
877 | 5 | if (::strncmp(pScene->mRootNode->mName.data, "Node", 4) == 0) { |
878 | 0 | pScene->mRootNode->mName.Set("<AC3DWorld>"); |
879 | 0 | } |
880 | | |
881 | | // copy meshes |
882 | 5 | if (meshes.empty()) { |
883 | 0 | throw DeadlyImportError("An unknown error occurred during converting"); |
884 | 0 | } |
885 | 5 | pScene->mNumMeshes = (unsigned int)meshes.size(); |
886 | 5 | pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]; |
887 | 5 | ::memcpy(pScene->mMeshes, &meshes[0], pScene->mNumMeshes * sizeof(void *)); |
888 | | |
889 | | // copy materials |
890 | 5 | pScene->mNumMaterials = (unsigned int)omaterials.size(); |
891 | 5 | pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials]; |
892 | 5 | ::memcpy(pScene->mMaterials, &omaterials[0], pScene->mNumMaterials * sizeof(void *)); |
893 | | |
894 | | // copy lights |
895 | 5 | pScene->mNumLights = (unsigned int)lights.size(); |
896 | 5 | if (!lights.empty()) { |
897 | 3 | pScene->mLights = new aiLight *[lights.size()]; |
898 | 3 | ::memcpy(pScene->mLights, &lights[0], lights.size() * sizeof(void *)); |
899 | 3 | } |
900 | 5 | } |
901 | | |
902 | | } // namespace Assimp |
903 | | |
904 | | #endif //!defined ASSIMP_BUILD_NO_AC_IMPORTER |