Coverage Report

Created: 2026-09-14 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
781k
inline const char *AcSkipToNextToken(const char *buffer, const char *end) {
83
781k
    if (!SkipSpaces(&buffer, end)) {
84
1.83k
        ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL");
85
1.83k
    }
86
781k
    return buffer;
87
781k
}
88
89
// ------------------------------------------------------------------------------------------------
90
// read a string (may be enclosed in double quotation marks). buffer must point to "
91
16.8k
inline const char *AcGetString(const char *buffer, const char *end, std::string &out) {
92
16.8k
    if (*buffer == '\0') {
93
8
        throw DeadlyImportError("AC3D: Unexpected EOF in string");
94
8
    }
95
16.8k
    ++buffer;
96
16.8k
    const char *sz = buffer;
97
387k
    while ('\"' != *buffer && buffer != end) {
98
373k
        if (IsLineEnd(*buffer)) {
99
2.95k
            ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL in string");
100
2.95k
            out = "ERROR";
101
2.95k
            break;
102
2.95k
        }
103
370k
        ++buffer;
104
370k
    }
105
16.8k
    if (IsLineEnd(*buffer)) {
106
2.95k
        return buffer;
107
2.95k
    }
108
13.9k
    out = std::string(sz, (unsigned int)(buffer - sz));
109
13.9k
    ++buffer;
110
111
13.9k
    return buffer;
112
16.8k
}
113
114
// ------------------------------------------------------------------------------------------------
115
// read 1 to n floats prefixed with an optional predefined identifier
116
template <class T>
117
239k
inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *end, const char *name, size_t name_length, size_t num, T *out) {
118
239k
    buffer = AcSkipToNextToken(buffer, end);
119
239k
    if (0 != name_length) {
120
28.7k
        if (0 != strncmp(buffer, name, name_length) || !IsSpace(buffer[name_length])) {
121
21.3k
            ASSIMP_LOG_ERROR("AC3D: Unexpected token. ", name, " was expected.");
122
21.3k
            return buffer;
123
21.3k
        }
124
7.39k
        buffer += name_length + 1;
125
7.39k
    }
126
750k
    for (unsigned int _i = 0; _i < num; ++_i) {
127
532k
        buffer = AcSkipToNextToken(buffer, end);
128
532k
        buffer = fast_atoreal_move(buffer, ((float *)out)[_i]);
129
532k
    }
130
131
218k
    return buffer;
132
239k
}
char const* Assimp::TAcCheckedLoadFloatArray<aiVector2t<float> >(char const*, char const*, char const*, unsigned long, unsigned long, aiVector2t<float>*)
Line
Count
Source
117
117k
inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *end, const char *name, size_t name_length, size_t num, T *out) {
118
117k
    buffer = AcSkipToNextToken(buffer, end);
119
117k
    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
353k
    for (unsigned int _i = 0; _i < num; ++_i) {
127
235k
        buffer = AcSkipToNextToken(buffer, end);
128
235k
        buffer = fast_atoreal_move(buffer, ((float *)out)[_i]);
129
235k
    }
130
131
117k
    return buffer;
132
117k
}
char const* Assimp::TAcCheckedLoadFloatArray<aiMatrix3x3t<float> >(char const*, char const*, char const*, unsigned long, unsigned long, aiMatrix3x3t<float>*)
Line
Count
Source
117
28
inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *end, const char *name, size_t name_length, size_t num, T *out) {
118
28
    buffer = AcSkipToNextToken(buffer, end);
119
28
    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
165
    for (unsigned int _i = 0; _i < num; ++_i) {
127
137
        buffer = AcSkipToNextToken(buffer, end);
128
137
        buffer = fast_atoreal_move(buffer, ((float *)out)[_i]);
129
137
    }
130
131
28
    return buffer;
132
28
}
char const* Assimp::TAcCheckedLoadFloatArray<aiVector3t<float> >(char const*, char const*, char const*, unsigned long, unsigned long, aiVector3t<float>*)
Line
Count
Source
117
8.42k
inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *end, const char *name, size_t name_length, size_t num, T *out) {
118
8.42k
    buffer = AcSkipToNextToken(buffer, end);
119
8.42k
    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
33.6k
    for (unsigned int _i = 0; _i < num; ++_i) {
127
25.1k
        buffer = AcSkipToNextToken(buffer, end);
128
25.1k
        buffer = fast_atoreal_move(buffer, ((float *)out)[_i]);
129
25.1k
    }
130
131
8.42k
    return buffer;
132
8.42k
}
char const* Assimp::TAcCheckedLoadFloatArray<float>(char const*, char const*, char const*, unsigned long, unsigned long, float*)
Line
Count
Source
117
93.7k
inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *end, const char *name, size_t name_length, size_t num, T *out) {
118
93.7k
    buffer = AcSkipToNextToken(buffer, end);
119
93.7k
    if (0 != name_length) {
120
9.55k
        if (0 != strncmp(buffer, name, name_length) || !IsSpace(buffer[name_length])) {
121
8.05k
            ASSIMP_LOG_ERROR("AC3D: Unexpected token. ", name, " was expected.");
122
8.05k
            return buffer;
123
8.05k
        }
124
1.50k
        buffer += name_length + 1;
125
1.50k
    }
126
339k
    for (unsigned int _i = 0; _i < num; ++_i) {
127
253k
        buffer = AcSkipToNextToken(buffer, end);
128
253k
        buffer = fast_atoreal_move(buffer, ((float *)out)[_i]);
129
253k
    }
130
131
85.6k
    return buffer;
132
93.7k
}
char const* Assimp::TAcCheckedLoadFloatArray<aiColor3D>(char const*, char const*, char const*, unsigned long, unsigned long, aiColor3D*)
Line
Count
Source
117
19.1k
inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *end, const char *name, size_t name_length, size_t num, T *out) {
118
19.1k
    buffer = AcSkipToNextToken(buffer, end);
119
19.1k
    if (0 != name_length) {
120
19.1k
        if (0 != strncmp(buffer, name, name_length) || !IsSpace(buffer[name_length])) {
121
13.2k
            ASSIMP_LOG_ERROR("AC3D: Unexpected token. ", name, " was expected.");
122
13.2k
            return buffer;
123
13.2k
        }
124
5.89k
        buffer += name_length + 1;
125
5.89k
    }
126
23.5k
    for (unsigned int _i = 0; _i < num; ++_i) {
127
17.6k
        buffer = AcSkipToNextToken(buffer, end);
128
17.6k
        buffer = fast_atoreal_move(buffer, ((float *)out)[_i]);
129
17.6k
    }
130
131
5.89k
    return buffer;
132
19.1k
}
133
134
// ------------------------------------------------------------------------------------------------
135
// Reverses vertex indices in a face.
136
9.15k
static void flipWindingOrder(aiFace &f) {
137
9.15k
    std::reverse(f.mIndices, f.mIndices + f.mNumIndices);
138
9.15k
}
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
9.15k
    aiVector3D *&outUV, const aiVector3D *allUV, unsigned &curIdx) {
145
9.15k
    auto &newFace = *outFaces++;
146
9.15k
    newFace = origFace;
147
9.15k
    flipWindingOrder(newFace);
148
36.2k
    for (unsigned f = 0; f < newFace.mNumIndices; ++f) {
149
27.1k
        *outVertices++ = allVertices[newFace.mIndices[f]];
150
27.1k
        if (outUV) {
151
44
            *outUV = allUV[newFace.mIndices[f]];
152
44
            outUV++;
153
44
        }
154
27.1k
        newFace.mIndices[f] = curIdx++;
155
27.1k
    }
156
9.15k
}
157
158
// ------------------------------------------------------------------------------------------------
159
// Constructor to be privately used by Importer
160
AC3DImporter::AC3DImporter() :
161
146k
        mBuffer(),
162
        configSplitBFCull(),
163
        configEvalSubdivision(),
164
        mNumMeshes(),
165
        mLights(),
166
146k
        mLightsCounter(0),
167
146k
        mGroupsCounter(0),
168
146k
        mPolysCounter(0),
169
146k
        mWorldsCounter(0) {
170
    // nothing to be done here
171
146k
}
172
173
// ------------------------------------------------------------------------------------------------
174
// Returns whether the class can handle the format of the given file.
175
17.9k
bool AC3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
176
17.9k
    static constexpr uint32_t tokens[] = { AI_MAKE_MAGIC("AC3D") };
177
17.9k
    return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
178
17.9k
}
179
180
// ------------------------------------------------------------------------------------------------
181
// Loader meta information
182
146k
const aiImporterDesc *AC3DImporter::GetInfo() const {
183
146k
    return &desc;
184
146k
}
185
186
// ------------------------------------------------------------------------------------------------
187
// Get a pointer to the next line from the file
188
29.2M
bool AC3DImporter::GetNextLine() {
189
29.2M
    SkipLine(&mBuffer.data, mBuffer.end);
190
29.2M
    return SkipSpaces(&mBuffer.data, mBuffer.end);
191
29.2M
}
192
193
// ------------------------------------------------------------------------------------------------
194
// Parse an object section in an AC file
195
35.6k
bool AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
196
35.6k
    if (!TokenMatch(mBuffer.data, "OBJECT", 6)) {
197
20.9k
        return false;
198
20.9k
    }
199
200
14.6k
    SkipSpaces(&mBuffer.data, mBuffer.end);
201
202
14.6k
    ++mNumMeshes;
203
204
14.6k
    objects.emplace_back();
205
14.6k
    Object &obj = objects.back();
206
207
14.6k
    aiLight *light = nullptr;
208
14.6k
    if (!ASSIMP_strincmp(mBuffer.data, "light", 5)) {
209
        // This is a light source. Add it to the list
210
2.41k
        mLights->push_back(light = new aiLight());
211
212
        // Return a point light with no attenuation
213
2.41k
        light->mType = aiLightSource_POINT;
214
2.41k
        light->mColorDiffuse = light->mColorSpecular = aiColor3D(1.f, 1.f, 1.f);
215
2.41k
        light->mAttenuationConstant = 1.f;
216
217
        // Generate a default name for both the light source and the node
218
2.41k
        light->mName.length = ::ai_snprintf(light->mName.data, AI_MAXLEN, "ACLight_%i", static_cast<unsigned int>(mLights->size()) - 1);
219
2.41k
        obj.name = std::string(light->mName.data);
220
221
2.41k
        ASSIMP_LOG_VERBOSE_DEBUG("AC3D: Light source encountered");
222
2.41k
        obj.type = Object::Light;
223
12.2k
    } else if (!ASSIMP_strincmp(mBuffer.data, "group", 5)) {
224
4
        obj.type = Object::Group;
225
12.2k
    } else if (!ASSIMP_strincmp(mBuffer.data, "world", 5)) {
226
3.95k
        obj.type = Object::World;
227
8.30k
    } else {
228
8.30k
        obj.type = Object::Poly;
229
8.30k
    }
230
231
249k
    while (GetNextLine()) {
232
247k
        if (TokenMatch(mBuffer.data, "kids", 4)) {
233
12.7k
            SkipSpaces(&mBuffer.data, mBuffer.end);
234
12.7k
            unsigned int num = strtoul10(mBuffer.data, &mBuffer.data);
235
12.7k
            GetNextLine();
236
12.7k
            if (num) {
237
                // load the children of this object recursively
238
8.56k
                obj.children.reserve(num);
239
17.1k
                for (unsigned int i = 0; i < num; ++i) {
240
12.1k
                    if (!LoadObjectSection(obj.children)) {
241
3.57k
                        ASSIMP_LOG_WARN("AC3D: wrong number of kids");
242
3.57k
                        break;
243
3.57k
                    }
244
12.1k
                }
245
8.56k
            }
246
12.7k
            return true;
247
235k
        } else if (TokenMatch(mBuffer.data, "name", 4)) {
248
10.3k
            SkipSpaces(&mBuffer.data, mBuffer.data);
249
10.3k
            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
10.3k
            if (light) {
254
3.00k
                light->mName.Set(obj.name);
255
3.00k
            }
256
224k
        } else if (TokenMatch(mBuffer.data, "texture", 7)) {
257
2.22k
            SkipSpaces(&mBuffer.data, mBuffer.end);
258
2.22k
            std::string texture;
259
2.22k
            mBuffer.data = AcGetString(mBuffer.data, mBuffer.end, texture);
260
2.22k
            obj.textures.push_back(texture);
261
222k
        } else if (TokenMatch(mBuffer.data, "texrep", 6)) {
262
79
            SkipSpaces(&mBuffer.data, mBuffer.end);
263
79
            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 2, &obj.texRepeat);
264
79
            if (!obj.texRepeat.x || !obj.texRepeat.y)
265
6
                obj.texRepeat = aiVector2D(1.f, 1.f);
266
222k
        } else if (TokenMatch(mBuffer.data, "texoff", 6)) {
267
64
            SkipSpaces(&mBuffer.data, mBuffer.end);
268
64
            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 2, &obj.texOffset);
269
222k
        } else if (TokenMatch(mBuffer.data, "rot", 3)) {
270
28
            SkipSpaces(&mBuffer.data, mBuffer.end);
271
28
            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 9, &obj.rotation);
272
222k
        } else if (TokenMatch(mBuffer.data, "loc", 3)) {
273
8.42k
            SkipSpaces(&mBuffer.data, mBuffer.end);
274
8.42k
            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 3, &obj.translation);
275
213k
        } else if (TokenMatch(mBuffer.data, "subdiv", 6)) {
276
3.52k
            SkipSpaces(&mBuffer.data, mBuffer.end);
277
3.52k
            obj.subDiv = strtoul10(mBuffer.data, &mBuffer.data);
278
210k
        } else if (TokenMatch(mBuffer.data, "crease", 6)) {
279
1.52k
            SkipSpaces(&mBuffer.data, mBuffer.end);
280
1.52k
            obj.crease = fast_atof(mBuffer.data);
281
208k
        } else if (TokenMatch(mBuffer.data, "numvert", 7)) {
282
5.32k
            SkipSpaces(&mBuffer.data, mBuffer.end);
283
284
5.32k
            unsigned int t = strtoul10(mBuffer.data, &mBuffer.data);
285
5.32k
            if (t >= AI_MAX_ALLOC(aiVector3D)) {
286
7
                throw DeadlyImportError("AC3D: Too many vertices, would run out of memory");
287
7
            }
288
5.31k
            obj.vertices.reserve(t);
289
89.4k
            for (unsigned int i = 0; i < t; ++i) {
290
87.6k
                if (!GetNextLine()) {
291
115
                    ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: not all vertices have been parsed yet");
292
115
                    break;
293
87.5k
                } else if (!IsNumeric(*mBuffer.data)) {
294
3.35k
                    ASSIMP_LOG_ERROR("AC3D: Unexpected token: not all vertices have been parsed yet");
295
3.35k
                    --mBuffer.data; // make sure the line is processed a second time
296
3.35k
                    break;
297
3.35k
                }
298
84.1k
                obj.vertices.emplace_back();
299
84.1k
                aiVector3D &v = obj.vertices.back();
300
84.1k
                mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 3, &v.x);
301
84.1k
            }
302
203k
        } else if (TokenMatch(mBuffer.data, "numsurf", 7)) {
303
6.02k
            SkipSpaces(&mBuffer.data, mBuffer.end);
304
305
6.02k
            bool Q3DWorkAround = false;
306
307
6.02k
            const unsigned int t = strtoul10(mBuffer.data, &mBuffer.data);
308
6.02k
            obj.surfaces.reserve(t);
309
14.3M
            for (unsigned int i = 0; i < t; ++i) {
310
14.3M
                GetNextLine();
311
14.3M
                if (!TokenMatch(mBuffer.data, "SURF", 4)) {
312
                    // FIX: this can occur for some files - Quick 3D for
313
                    // example writes no surf chunks
314
14.2M
                    if (!Q3DWorkAround) {
315
3.73k
                        ASSIMP_LOG_WARN("AC3D: SURF token was expected");
316
3.73k
                        ASSIMP_LOG_VERBOSE_DEBUG("Continuing with Quick3D Workaround enabled");
317
3.73k
                    }
318
14.2M
                    --mBuffer.data; // make sure the line is processed a second time
319
                    // break; --- see fix notes above
320
321
14.2M
                    Q3DWorkAround = true;
322
14.2M
                }
323
14.3M
                SkipSpaces(&mBuffer.data, mBuffer.end);
324
14.3M
                obj.surfaces.emplace_back();
325
14.3M
                Surface &surf = obj.surfaces.back();
326
14.3M
                surf.flags = strtoul_cppstyle(mBuffer.data);
327
328
14.3M
                while (true) {
329
14.3M
                    if (!GetNextLine()) {
330
52
                        throw DeadlyImportError("AC3D: Unexpected EOF: surface is incomplete");
331
52
                    }
332
14.3M
                    if (TokenMatch(mBuffer.data, "mat", 3)) {
333
34.2k
                        SkipSpaces(&mBuffer.data, mBuffer.end);
334
34.2k
                        surf.mat = strtoul10(mBuffer.data);
335
14.3M
                    } else if (TokenMatch(mBuffer.data, "refs", 4)) {
336
                        // --- see fix notes above
337
33.6k
                        if (Q3DWorkAround) {
338
790
                            if (!surf.entries.empty()) {
339
1
                                mBuffer.data -= 6;
340
1
                                break;
341
1
                            }
342
790
                        }
343
344
33.6k
                        SkipSpaces(&mBuffer.data, mBuffer.end);
345
33.6k
                        const unsigned int m = strtoul10(mBuffer.data);
346
33.6k
                        surf.entries.reserve(m);
347
348
33.6k
                        obj.numRefs += m;
349
350
151k
                        for (unsigned int k = 0; k < m; ++k) {
351
117k
                            if (!GetNextLine()) {
352
33
                                ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: surface references are incomplete");
353
33
                                break;
354
33
                            }
355
117k
                            surf.entries.emplace_back();
356
117k
                            Surface::SurfaceEntry &entry = surf.entries.back();
357
358
117k
                            entry.first = strtoul10(mBuffer.data, &mBuffer.data);
359
117k
                            SkipSpaces(&mBuffer.data, mBuffer.end);
360
117k
                            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "", 0, 2, &entry.second);
361
117k
                        }
362
14.3M
                    } else {
363
14.3M
                        --mBuffer.data; // make sure the line is processed a second time
364
14.3M
                        break;
365
14.3M
                    }
366
14.3M
                }
367
14.3M
            }
368
6.02k
        }
369
247k
    }
370
14.6k
    ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: \'kids\' line was expected");
371
372
1.90k
    return false;
373
14.6k
}
374
375
// ------------------------------------------------------------------------------------------------
376
// Convert a material from AC3DImporter::Material to aiMaterial
377
void AC3DImporter::ConvertMaterial(const Object &object,
378
        const Material &matSrc,
379
2.62k
        aiMaterial &matDest) {
380
2.62k
    aiString s;
381
382
2.62k
    if (matSrc.name.length()) {
383
958
        s.Set(matSrc.name);
384
958
        matDest.AddProperty(&s, AI_MATKEY_NAME);
385
958
    }
386
2.62k
    if (!object.textures.empty()) {
387
390
        s.Set(object.textures[0]);
388
390
        matDest.AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0));
389
390
        // UV transformation
391
390
        if (1.f != object.texRepeat.x || 1.f != object.texRepeat.y ||
392
380
                object.texOffset.x || object.texOffset.y) {
393
15
            aiUVTransform transform;
394
15
            transform.mScaling = object.texRepeat;
395
15
            transform.mTranslation = object.texOffset;
396
15
            matDest.AddProperty(&transform, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0));
397
15
        }
398
390
    }
399
400
2.62k
    matDest.AddProperty<aiColor3D>(&matSrc.rgb, 1, AI_MATKEY_COLOR_DIFFUSE);
401
2.62k
    matDest.AddProperty<aiColor3D>(&matSrc.amb, 1, AI_MATKEY_COLOR_AMBIENT);
402
2.62k
    matDest.AddProperty<aiColor3D>(&matSrc.emis, 1, AI_MATKEY_COLOR_EMISSIVE);
403
2.62k
    matDest.AddProperty<aiColor3D>(&matSrc.spec, 1, AI_MATKEY_COLOR_SPECULAR);
404
405
2.62k
    int n = -1;
406
2.62k
    if (matSrc.shin) {
407
628
        n = aiShadingMode_Phong;
408
628
        matDest.AddProperty<float>(&matSrc.shin, 1, AI_MATKEY_SHININESS);
409
1.99k
    } else {
410
1.99k
        n = aiShadingMode_Gouraud;
411
1.99k
    }
412
2.62k
    matDest.AddProperty<int>(&n, 1, AI_MATKEY_SHADING_MODEL);
413
414
2.62k
    float f = 1.f - matSrc.trans;
415
2.62k
    matDest.AddProperty<float>(&f, 1, AI_MATKEY_OPACITY);
416
2.62k
}
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
8.67k
        aiNode *parent) {
425
8.67k
    aiNode *node = new aiNode();
426
8.67k
    node->mParent = parent;
427
8.67k
    if (object.vertices.size()) {
428
2.62k
        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
1.58k
            ASSIMP_LOG_INFO("AC3D: No surfaces defined in object definition, "
439
1.58k
                            "a point list is returned");
440
441
1.58k
            meshes.push_back(new aiMesh());
442
1.58k
            aiMesh *mesh = meshes.back();
443
444
1.58k
            mesh->mNumFaces = mesh->mNumVertices = (unsigned int)object.vertices.size();
445
1.58k
            aiFace *faces = mesh->mFaces = new aiFace[mesh->mNumFaces];
446
1.58k
            aiVector3D *verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
447
448
12.2k
            for (unsigned int i = 0; i < mesh->mNumVertices; ++i, ++faces, ++verts) {
449
10.6k
                *verts = object.vertices[i];
450
10.6k
                faces->mNumIndices = 1;
451
10.6k
                faces->mIndices = new unsigned int[1];
452
10.6k
                faces->mIndices[0] = i;
453
10.6k
            }
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
1.58k
            mesh->mMaterialIndex = 0;
459
1.58k
            outMaterials.push_back(new aiMaterial());
460
1.58k
            ConvertMaterial(object, materials[0], *outMaterials.back());
461
1.58k
        } else {
462
            // need to generate one or more meshes for this object.
463
            // find out how many different materials we have
464
1.03k
            typedef std::pair<unsigned int, unsigned int> IntPair;
465
1.03k
            typedef std::vector<IntPair> MatTable;
466
1.03k
            MatTable needMat(materials.size(), IntPair(0, 0));
467
468
1.03k
            std::vector<Surface>::iterator it, end = object.surfaces.end();
469
1.03k
            std::vector<Surface::SurfaceEntry>::iterator it2, end2;
470
471
5.09M
            for (it = object.surfaces.begin(); it != end; ++it) {
472
5.09M
                unsigned int idx = (*it).mat;
473
5.09M
                if (idx >= needMat.size()) {
474
171
                    ASSIMP_LOG_ERROR("AC3D: material index is out of range");
475
171
                    idx = 0;
476
171
                }
477
5.09M
                if ((*it).entries.empty()) {
478
5.06M
                    ASSIMP_LOG_WARN("AC3D: surface has zero vertex references");
479
5.06M
                }
480
5.09M
                const bool isDoubleSided = ACDoubleSidedFlag == (it->flags & ACDoubleSidedFlag);
481
5.09M
                const int doubleSidedFactor = isDoubleSided ? 2 : 1;
482
483
                // validate all vertex indices to make sure we won't crash here
484
5.09M
                for (it2 = (*it).entries.begin(),
485
5.09M
                    end2 = (*it).entries.end();
486
5.19M
                        it2 != end2; ++it2) {
487
104k
                    if ((*it2).first >= object.vertices.size()) {
488
17.1k
                        ASSIMP_LOG_WARN("AC3D: Invalid vertex reference");
489
17.1k
                        (*it2).first = 0;
490
17.1k
                    }
491
104k
                }
492
493
5.09M
                if (!needMat[idx].first) {
494
1.05k
                    ++node->mNumMeshes;
495
1.05k
                }
496
497
5.09M
                switch ((*it).GetType()) {
498
218
                case Surface::ClosedLine: // closed line
499
218
                    needMat[idx].first += static_cast<unsigned int>((*it).entries.size());
500
218
                    needMat[idx].second += static_cast<unsigned int>((*it).entries.size() << 1u);
501
218
                    break;
502
503
                    // unclosed line
504
128
                case Surface::OpenLine:
505
128
                    needMat[idx].first += static_cast<unsigned int>((*it).entries.size() - 1);
506
128
                    needMat[idx].second += static_cast<unsigned int>(((*it).entries.size() - 1) << 1u);
507
128
                    break;
508
509
                    // triangle strip
510
699
                case Surface::TriangleStrip:
511
699
                    needMat[idx].first += static_cast<unsigned int>(it->entries.size() - 2) * doubleSidedFactor;
512
699
                    needMat[idx].second += static_cast<unsigned int>(it->entries.size() - 2) * 3 * doubleSidedFactor;
513
699
                    break;
514
515
558
                default:
516
                    // Coerce unknowns to a polygon and warn
517
558
                    ASSIMP_LOG_WARN("AC3D: The type flag of a surface is unknown: ", (*it).flags);
518
558
                    (*it).flags &= ~(Surface::Mask);
519
                    // fallthrough
520
521
                    // polygon
522
5.08M
                case Surface::Polygon:
523
                    // the number of faces increments by one, the number
524
                    // of vertices by surface.numref.
525
5.08M
                    needMat[idx].first += doubleSidedFactor;
526
5.08M
                    needMat[idx].second += static_cast<unsigned int>(it->entries.size()) * doubleSidedFactor;
527
5.09M
                };
528
5.09M
            }
529
1.03k
            unsigned int *pip = node->mMeshes = new unsigned int[node->mNumMeshes];
530
1.03k
            unsigned int mat = 0;
531
1.03k
            const size_t oldm = meshes.size();
532
1.03k
            for (MatTable::const_iterator cit = needMat.begin(), cend = needMat.end();
533
2.04k
                    cit != cend; ++cit, ++mat) {
534
1.04k
                if (!(*cit).first) {
535
3
                    continue;
536
3
                }
537
538
                // allocate a new aiMesh object
539
1.03k
                *pip++ = (unsigned int)meshes.size();
540
1.03k
                aiMesh *mesh = new aiMesh();
541
1.03k
                meshes.push_back(mesh);
542
543
1.03k
                mesh->mMaterialIndex = static_cast<unsigned int>(outMaterials.size());
544
1.03k
                outMaterials.push_back(new aiMaterial());
545
1.03k
                ConvertMaterial(object, materials[mat], *outMaterials.back());
546
547
                // allocate storage for vertices and normals
548
1.03k
                mesh->mNumFaces = (*cit).first;
549
1.03k
                if (mesh->mNumFaces == 0) {
550
0
                    throw DeadlyImportError("AC3D: No faces");
551
1.03k
                } else if (mesh->mNumFaces > AI_MAX_ALLOC(aiFace)) {
552
1
                    throw DeadlyImportError("AC3D: Too many faces, would run out of memory");
553
1
                }
554
1.03k
                aiFace *faces = mesh->mFaces = new aiFace[mesh->mNumFaces];
555
556
1.03k
                mesh->mNumVertices = (*cit).second;
557
1.03k
                if (mesh->mNumVertices == 0) {
558
8
                    throw DeadlyImportError("AC3D: No vertices");
559
1.02k
                } else if (mesh->mNumVertices > AI_MAX_ALLOC(aiVector3D)) {
560
2
                    throw DeadlyImportError("AC3D: Too many vertices, would run out of memory");
561
2
                }
562
1.02k
                aiVector3D *vertices = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
563
1.02k
                unsigned int cur = 0;
564
565
                // allocate UV coordinates, but only if the texture name for the
566
                // surface is not empty
567
1.02k
                aiVector3D *uv = nullptr;
568
1.02k
                if (!object.textures.empty()) {
569
153
                    uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
570
153
                    mesh->mNumUVComponents[0] = 2;
571
153
                }
572
573
4.89M
                for (it = object.surfaces.begin(); it != end; ++it) {
574
4.89M
                    if (mat == (*it).mat) {
575
4.89M
                        const Surface &src = *it;
576
4.89M
                        const bool isDoubleSided = ACDoubleSidedFlag == (src.flags & ACDoubleSidedFlag);
577
578
                        // closed polygon
579
4.89M
                        uint8_t type = (*it).GetType();
580
4.89M
                        if (type == Surface::Polygon) {
581
4.89M
                            aiFace &face = *faces++;
582
4.89M
                            face.mNumIndices = (unsigned int)src.entries.size();
583
4.89M
                            if (0 != face.mNumIndices) {
584
27.8k
                                face.mIndices = new unsigned int[face.mNumIndices];
585
120k
                                for (unsigned int i = 0; i < face.mNumIndices; ++i, ++vertices) {
586
92.5k
                                    const Surface::SurfaceEntry &entry = src.entries[i];
587
92.5k
                                    face.mIndices[i] = cur++;
588
589
                                    // copy vertex positions
590
92.5k
                                    if (static_cast<unsigned>(vertices - mesh->mVertices) >= mesh->mNumVertices) {
591
1
                                        throw DeadlyImportError("AC3D: Invalid number of vertices");
592
1
                                    }
593
92.5k
                                    *vertices = object.vertices[entry.first] + object.translation;
594
595
                                    // copy texture coordinates
596
92.5k
                                    if (uv) {
597
35.2k
                                        uv->x = entry.second.x;
598
35.2k
                                        uv->y = entry.second.y;
599
35.2k
                                        ++uv;
600
35.2k
                                    }
601
92.5k
                                }
602
27.7k
                                if(isDoubleSided) // Need a backface?
603
468
                                    buildBacksideOfFace(faces[-1], faces, vertices, mesh->mVertices, uv, mesh->mTextureCoords[0], cur);
604
27.7k
                            }
605
4.89M
                        } else if (type == Surface::TriangleStrip) {
606
9.80k
                            for (unsigned int i = 0; i < (unsigned int)src.entries.size() - 2; ++i) {
607
9.12k
                                const Surface::SurfaceEntry &entry1 = src.entries[i];
608
9.12k
                                const Surface::SurfaceEntry &entry2 = src.entries[i + 1];
609
9.12k
                                const Surface::SurfaceEntry &entry3 = src.entries[i + 2];
610
9.12k
                                const unsigned int verticesNeeded = isDoubleSided ? 6 : 3;
611
9.12k
                                if (static_cast<unsigned>(vertices - mesh->mVertices) + verticesNeeded > mesh->mNumVertices) {
612
7
                                    throw DeadlyImportError("AC3D: Invalid number of vertices");
613
7
                                }
614
615
9.11k
                                aiFace &face = *faces++;
616
9.11k
                                face.mNumIndices = 3;
617
9.11k
                                face.mIndices = new unsigned int[face.mNumIndices];
618
9.11k
                                face.mIndices[0] = cur++;
619
9.11k
                                face.mIndices[1] = cur++;
620
9.11k
                                face.mIndices[2] = cur++;
621
9.11k
                                if (!(i & 1)) {
622
4.71k
                                    *vertices++ = object.vertices[entry1.first] + object.translation;
623
4.71k
                                    if (uv) {
624
205
                                        uv->x = entry1.second.x;
625
205
                                        uv->y = entry1.second.y;
626
205
                                        ++uv;
627
205
                                    }
628
4.71k
                                    *vertices++ = object.vertices[entry2.first] + object.translation;
629
4.71k
                                    if (uv) {
630
205
                                        uv->x = entry2.second.x;
631
205
                                        uv->y = entry2.second.y;
632
205
                                        ++uv;
633
205
                                    }
634
4.71k
                                } else {
635
4.40k
                                    *vertices++ = object.vertices[entry2.first] + object.translation;
636
4.40k
                                    if (uv) {
637
201
                                        uv->x = entry2.second.x;
638
201
                                        uv->y = entry2.second.y;
639
201
                                        ++uv;
640
201
                                    }
641
4.40k
                                    *vertices++ = object.vertices[entry1.first] + object.translation;
642
4.40k
                                    if (uv) {
643
201
                                        uv->x = entry1.second.x;
644
201
                                        uv->y = entry1.second.y;
645
201
                                        ++uv;
646
201
                                    }
647
4.40k
                                }
648
9.11k
                                if (static_cast<unsigned>(vertices - mesh->mVertices) >= mesh->mNumVertices) {
649
0
                                    throw DeadlyImportError("AC3D: Invalid number of vertices");
650
0
                                }
651
9.11k
                                *vertices++ = object.vertices[entry3.first] + object.translation;
652
9.11k
                                if (uv) {
653
406
                                    uv->x = entry3.second.x;
654
406
                                    uv->y = entry3.second.y;
655
406
                                    ++uv;
656
406
                                }
657
9.11k
                                if(isDoubleSided) // Need a backface?
658
8.68k
                                    buildBacksideOfFace(faces[-1], faces, vertices, mesh->mVertices, uv, mesh->mTextureCoords[0], cur);
659
9.11k
                            }
660
684
                        } else {
661
662
285
                            it2 = (*it).entries.begin();
663
664
                            // either a closed or an unclosed line
665
285
                            unsigned int tmp = (unsigned int)(*it).entries.size();
666
285
                            if (Surface::OpenLine == type) --tmp;
667
1.04k
                            for (unsigned int m = 0; m < tmp; ++m) {
668
769
                                if (static_cast<unsigned>(vertices - mesh->mVertices) + 2 > mesh->mNumVertices) {
669
5
                                    throw DeadlyImportError("AC3D: Invalid number of vertices");
670
5
                                }
671
672
764
                                aiFace &face = *faces++;
673
674
764
                                face.mNumIndices = 2;
675
764
                                face.mIndices = new unsigned int[2];
676
764
                                face.mIndices[0] = cur++;
677
764
                                face.mIndices[1] = cur++;
678
679
                                // copy vertex positions
680
764
                                if (it2 == (*it).entries.end()) {
681
5
                                    throw DeadlyImportError("AC3D: Bad line");
682
5
                                }
683
764
                                ai_assert((*it2).first < object.vertices.size());
684
759
                                *vertices++ = object.vertices[(*it2).first];
685
686
                                // copy texture coordinates
687
759
                                if (uv) {
688
114
                                    uv->x = (*it2).second.x;
689
114
                                    uv->y = (*it2).second.y;
690
114
                                    ++uv;
691
114
                                }
692
693
759
                                if (Surface::ClosedLine == type && tmp - 1 == m) {
694
                                    // if this is a closed line repeat its beginning now
695
184
                                    it2 = (*it).entries.begin();
696
184
                                } else
697
575
                                    ++it2;
698
699
                                // second point
700
759
                                *vertices++ = object.vertices[(*it2).first];
701
702
759
                                if (uv) {
703
114
                                    uv->x = (*it2).second.x;
704
114
                                    uv->y = (*it2).second.y;
705
114
                                    ++uv;
706
114
                                }
707
759
                            }
708
285
                        }
709
4.89M
                    }
710
4.89M
                }
711
1.02k
            }
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
1.00k
            if (object.subDiv) {
717
530
                if (configEvalSubdivision) {
718
530
                    std::unique_ptr<Subdivider> div(Subdivider::Create(Subdivider::CATMULL_CLARKE));
719
530
                    ASSIMP_LOG_INFO("AC3D: Evaluating subdivision surface: ", object.name);
720
721
530
                    std::vector<aiMesh *> cpy(meshes.size() - oldm, nullptr);
722
530
                    div->Subdivide(&meshes[oldm], cpy.size(), &cpy.front(), object.subDiv, true);
723
530
                    std::copy(cpy.begin(), cpy.end(), meshes.begin() + oldm);
724
725
                    // previous meshes are deleted vy Subdivide().
726
530
                } else {
727
0
                    ASSIMP_LOG_INFO("AC3D: Letting the subdivision surface untouched due to my configuration: ", object.name);
728
0
                }
729
530
            }
730
1.00k
        }
731
2.62k
    }
732
733
8.64k
    if (object.name.length())
734
5.70k
        node->mName.Set(object.name);
735
2.93k
    else {
736
        // generate a name depending on the type of the node
737
2.93k
        switch (object.type) {
738
1
        case Object::Group:
739
1
            node->mName.length = ::ai_snprintf(node->mName.data, AI_MAXLEN, "ACGroup_%i", mGroupsCounter++);
740
1
            break;
741
1.54k
        case Object::Poly:
742
1.54k
            node->mName.length = ::ai_snprintf(node->mName.data, AI_MAXLEN, "ACPoly_%i", mPolysCounter++);
743
1.54k
            break;
744
27
        case Object::Light:
745
27
            node->mName.length = ::ai_snprintf(node->mName.data, AI_MAXLEN, "ACLight_%i", mLightsCounter++);
746
27
            break;
747
748
            // there shouldn't be more than one world, but we don't care
749
1.36k
        case Object::World:
750
1.36k
            node->mName.length = ::ai_snprintf(node->mName.data, AI_MAXLEN, "ACWorld_%i", mWorldsCounter++);
751
1.36k
            break;
752
2.93k
        }
753
2.93k
    }
754
755
    // setup the local transformation matrix of the object
756
    // compute the transformation offset to the parent node
757
8.64k
    node->mTransformation = aiMatrix4x4(object.rotation);
758
759
8.64k
    if (object.type == Object::Group || !object.numRefs) {
760
7.60k
        node->mTransformation.a4 = object.translation.x;
761
7.60k
        node->mTransformation.b4 = object.translation.y;
762
7.60k
        node->mTransformation.c4 = object.translation.z;
763
7.60k
    }
764
765
    // add children to the object
766
8.64k
    if (object.children.size()) {
767
4.88k
        node->mNumChildren = (unsigned int)object.children.size();
768
4.88k
        node->mChildren = new aiNode *[node->mNumChildren];
769
11.5k
        for (unsigned int i = 0; i < node->mNumChildren; ++i) {
770
6.68k
            node->mChildren[i] = ConvertObjectSection(object.children[i], meshes, outMaterials, materials, node);
771
6.68k
        }
772
4.88k
    }
773
774
8.64k
    return node;
775
8.64k
}
776
777
// ------------------------------------------------------------------------------------------------
778
2.85k
void AC3DImporter::SetupProperties(const Importer *pImp) {
779
2.85k
    configSplitBFCull = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL, 1) ? true : false;
780
2.85k
    configEvalSubdivision = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION, 1) ? true : false;
781
2.85k
}
782
783
// ------------------------------------------------------------------------------------------------
784
// Imports the given file into the given scene structure.
785
void AC3DImporter::InternReadFile(const std::string &pFile,
786
2.85k
        aiScene *pScene, IOSystem *pIOHandler) {
787
2.85k
    std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
788
789
    // Check whether we can read from the file
790
2.85k
    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
2.85k
    std::vector<char> mBuffer2;
796
2.85k
    TextFileToBuffer(file.get(), mBuffer2);
797
798
2.85k
    mBuffer.data = &mBuffer2[0];
799
2.85k
    mBuffer.end = &mBuffer2[0] + mBuffer2.size();
800
2.85k
    mNumMeshes = 0;
801
802
2.85k
    mLightsCounter = mPolysCounter = mWorldsCounter = mGroupsCounter = 0;
803
804
2.85k
    if (::strncmp(mBuffer.data, "AC3D", 4)) {
805
2
        throw DeadlyImportError("AC3D: No valid AC3D file, magic sequence not found");
806
2
    }
807
808
    // print the file format version to the console
809
2.85k
    unsigned int version = HexDigitToDecimal(mBuffer.data[4]);
810
2.85k
    char msg[3];
811
2.85k
    ASSIMP_itoa10(msg, 3, version);
812
2.85k
    ASSIMP_LOG_INFO("AC3D file format version: ", msg);
813
814
2.85k
    std::vector<Material> materials;
815
2.85k
    materials.reserve(5);
816
817
2.85k
    std::vector<Object> rootObjects;
818
2.85k
    rootObjects.reserve(5);
819
820
2.85k
    std::vector<aiLight *> lights;
821
2.85k
    mLights = &lights;
822
823
31.1k
    while (GetNextLine()) {
824
28.3k
        if (TokenMatch(mBuffer.data, "MATERIAL", 8)) {
825
4.81k
            materials.emplace_back();
826
4.81k
            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
4.81k
            mBuffer.data = AcSkipToNextToken(mBuffer.data, mBuffer.end);
832
4.81k
            if ('\"' == *mBuffer.data) {
833
4.29k
                mBuffer.data = AcGetString(mBuffer.data, mBuffer.end, mat.name);
834
4.29k
                mBuffer.data = AcSkipToNextToken(mBuffer.data, mBuffer.end);
835
4.29k
            }
836
837
4.81k
            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "rgb", 3, 3, &mat.rgb);
838
4.81k
            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "amb", 3, 3, &mat.amb);
839
4.81k
            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "emis", 4, 3, &mat.emis);
840
4.81k
            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "spec", 4, 3, &mat.spec);
841
4.81k
            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "shi", 3, 1, &mat.shin);
842
4.81k
            mBuffer.data = TAcCheckedLoadFloatArray(mBuffer.data, mBuffer.end, "trans", 5, 1, &mat.trans);
843
23.4k
        } else {
844
23.4k
            LoadObjectSection(rootObjects);
845
23.4k
        }
846
28.3k
    }
847
848
2.85k
    if (rootObjects.empty() || mNumMeshes == 0u) {
849
367
        throw DeadlyImportError("AC3D: No meshes have been loaded");
850
367
    }
851
2.48k
    if (materials.empty()) {
852
651
        ASSIMP_LOG_WARN("AC3D: No material has been found");
853
651
        materials.emplace_back();
854
651
    }
855
856
2.48k
    mNumMeshes += (mNumMeshes >> 2u) + 1;
857
2.48k
    std::vector<aiMesh *> meshes;
858
2.48k
    meshes.reserve(mNumMeshes);
859
860
2.48k
    std::vector<aiMaterial *> omaterials;
861
2.48k
    materials.reserve(mNumMeshes);
862
863
    // generate a dummy root if there are multiple objects on the top layer
864
2.48k
    Object *root = nullptr;
865
2.48k
    if (1 == rootObjects.size())
866
1.79k
        root = &rootObjects[0];
867
693
    else {
868
693
        root = new Object();
869
693
    }
870
871
    // now convert the imported stuff to our output data structure
872
2.48k
    pScene->mRootNode = ConvertObjectSection(*root, meshes, omaterials, materials);
873
2.48k
    if (1 != rootObjects.size()) {
874
194
        delete root;
875
194
    }
876
877
2.48k
    if (::strncmp(pScene->mRootNode->mName.data, "Node", 4) == 0) {
878
3
        pScene->mRootNode->mName.Set("<AC3DWorld>");
879
3
    }
880
881
    // copy meshes
882
2.48k
    if (meshes.empty()) {
883
835
        throw DeadlyImportError("An unknown error occurred during converting");
884
835
    }
885
1.65k
    pScene->mNumMeshes = (unsigned int)meshes.size();
886
1.65k
    pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
887
1.65k
    ::memcpy(pScene->mMeshes, &meshes[0], pScene->mNumMeshes * sizeof(void *));
888
889
    // copy materials
890
1.65k
    pScene->mNumMaterials = (unsigned int)omaterials.size();
891
1.65k
    pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials];
892
1.65k
    ::memcpy(pScene->mMaterials, &omaterials[0], pScene->mNumMaterials * sizeof(void *));
893
894
    // copy lights
895
1.65k
    pScene->mNumLights = (unsigned int)lights.size();
896
1.65k
    if (!lights.empty()) {
897
210
        pScene->mLights = new aiLight *[lights.size()];
898
210
        ::memcpy(pScene->mLights, &lights[0], lights.size() * sizeof(void *));
899
210
    }
900
1.65k
}
901
902
} // namespace Assimp
903
904
#endif //!defined ASSIMP_BUILD_NO_AC_IMPORTER