Coverage Report

Created: 2025-12-05 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/NFF/NFFLoader.cpp
Line
Count
Source
1
/*
2
---------------------------------------------------------------------------
3
Open Asset Import Library (assimp)
4
---------------------------------------------------------------------------
5
6
Copyright (c) 2006-2025, 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 STL importer class */
43
44
#ifndef ASSIMP_BUILD_NO_NFF_IMPORTER
45
46
// internal headers
47
#include "NFFLoader.h"
48
#include <assimp/ParsingUtils.h>
49
#include <assimp/RemoveComments.h>
50
#include <assimp/StandardShapes.h>
51
#include <assimp/fast_atof.h>
52
#include <assimp/importerdesc.h>
53
#include <assimp/qnan.h>
54
#include <assimp/scene.h>
55
#include <assimp/DefaultLogger.hpp>
56
#include <assimp/IOSystem.hpp>
57
#include <memory>
58
59
namespace Assimp {
60
61
static constexpr aiImporterDesc desc = {
62
    "Neutral File Format Importer",
63
    "",
64
    "",
65
    "",
66
    aiImporterFlags_SupportBinaryFlavour,
67
    0,
68
    0,
69
    0,
70
    0,
71
    "enff nff"
72
};
73
74
// ------------------------------------------------------------------------------------------------
75
// Returns whether the class can handle the format of the given file.
76
497
bool NFFImporter::CanRead(const std::string & pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const {
77
497
    return SimpleExtensionCheck(pFile, "nff", "enff");
78
497
}
79
80
// ------------------------------------------------------------------------------------------------
81
// Get the list of all supported file extensions
82
1.21k
const aiImporterDesc *NFFImporter::GetInfo() const {
83
1.21k
    return &desc;
84
1.21k
}
85
86
// ------------------------------------------------------------------------------------------------
87
#define AI_NFF_PARSE_FLOAT(f) \
88
10.9k
    SkipSpaces(&sz, lineEnd);          \
89
10.9k
    if (!IsLineEnd(*sz)) sz = fast_atoreal_move(sz, (ai_real &)f);
90
91
// ------------------------------------------------------------------------------------------------
92
#define AI_NFF_PARSE_TRIPLE(v) \
93
3.19k
    AI_NFF_PARSE_FLOAT(v[0])   \
94
3.19k
    AI_NFF_PARSE_FLOAT(v[1])   \
95
3.19k
    AI_NFF_PARSE_FLOAT(v[2])
96
97
// ------------------------------------------------------------------------------------------------
98
#define AI_NFF_PARSE_SHAPE_INFORMATION()                     \
99
948
    aiVector3D center, radius(1.0f, get_qnan(), get_qnan()); \
100
948
    AI_NFF_PARSE_TRIPLE(center);                             \
101
948
    AI_NFF_PARSE_TRIPLE(radius);                             \
102
948
    if (is_qnan(radius.z)) radius.z = radius.x;              \
103
948
    if (is_qnan(radius.y)) radius.y = radius.x;              \
104
948
    curMesh.radius = radius;                                 \
105
948
    curMesh.center = center;
106
107
// ------------------------------------------------------------------------------------------------
108
#define AI_NFF2_GET_NEXT_TOKEN()                                            \
109
0
    do {                                                                    \
110
0
        if (!GetNextLine(buffer, line)) {                                   \
111
0
            ASSIMP_LOG_WARN("NFF2: Unexpected EOF, can't read next token"); \
112
0
            break;                                                          \
113
0
        }                                                                   \
114
0
        SkipSpaces(line, &sz, lineEnd);                                              \
115
0
    } while (IsLineEnd(*sz))
116
117
// ------------------------------------------------------------------------------------------------
118
// Loads the material table for the NFF2 file format from an external file
119
void NFFImporter::LoadNFF2MaterialTable(std::vector<ShadingInfo> &output,
120
0
        const std::string &path, IOSystem *pIOHandler) {
121
0
    std::unique_ptr<IOStream> file(pIOHandler->Open(path, "rb"));
122
123
    // Check whether we can read from the file
124
0
    if (!file) {
125
0
        ASSIMP_LOG_ERROR("NFF2: Unable to open material library ", path, ".");
126
0
        return;
127
0
    }
128
129
    // get the size of the file
130
0
    const unsigned int m = (unsigned int)file->FileSize();
131
132
    // allocate storage and copy the contents of the file to a memory buffer
133
    // (terminate it with zero)
134
0
    std::vector<char> mBuffer2(m + 1);
135
0
    TextFileToBuffer(file.get(), mBuffer2);
136
0
    const char *buffer = &mBuffer2[0];
137
138
    // First of all: remove all comments from the file
139
0
    CommentRemover::RemoveLineComments("//", &mBuffer2[0]);
140
141
    // The file should start with the magic sequence "mat"
142
0
    if (!TokenMatch(buffer, "mat", 3)) {
143
0
        ASSIMP_LOG_ERROR("NFF2: Not a valid material library ", path, ".");
144
0
        return;
145
0
    }
146
147
0
    ShadingInfo *curShader = nullptr;
148
149
    // No read the file line per line
150
0
    char line[4096];
151
0
    const char *sz, *lineEnd = &line[2095]+1;
152
0
    while (GetNextLine(buffer, line)) {
153
0
        SkipSpaces(line, &sz, lineEnd);
154
155
        // 'version' defines the version of the file format
156
0
        if (TokenMatch(sz, "version", 7)) {
157
0
            ASSIMP_LOG_INFO("NFF (Sense8) material library file format: ", std::string(sz));
158
0
        }
159
        // 'matdef' starts a new material in the file
160
0
        else if (TokenMatch(sz, "matdef", 6)) {
161
            // add a new material to the list
162
0
            output.emplace_back();
163
0
            curShader = &output.back();
164
165
            // parse the name of the material
166
0
        } else if (!TokenMatch(sz, "valid", 5)) {
167
            // check whether we have an active material at the moment
168
0
            if (!IsLineEnd(*sz)) {
169
0
                if (!curShader) {
170
0
                    ASSIMP_LOG_ERROR("NFF2 material library: Found element ", sz, "but there is no active material");
171
0
                    continue;
172
0
                }
173
0
            } else
174
0
                continue;
175
176
            // now read the material property and determine its type
177
0
            aiColor3D c;
178
0
            if (TokenMatch(sz, "ambient", 7)) {
179
0
                AI_NFF_PARSE_TRIPLE(c);
180
0
                curShader->ambient = c;
181
0
            } else if (TokenMatch(sz, "diffuse", 7) || TokenMatch(sz, "ambientdiffuse", 14) /* correct? */) {
182
0
                AI_NFF_PARSE_TRIPLE(c);
183
0
                curShader->diffuse = curShader->ambient = c;
184
0
            } else if (TokenMatch(sz, "specular", 8)) {
185
0
                AI_NFF_PARSE_TRIPLE(c);
186
0
                curShader->specular = c;
187
0
            } else if (TokenMatch(sz, "emission", 8)) {
188
0
                AI_NFF_PARSE_TRIPLE(c);
189
0
                curShader->emissive = c;
190
0
            } else if (TokenMatch(sz, "shininess", 9)) {
191
0
                AI_NFF_PARSE_FLOAT(curShader->shininess);
192
0
            } else if (TokenMatch(sz, "opacity", 7)) {
193
0
                AI_NFF_PARSE_FLOAT(curShader->opacity);
194
0
            }
195
0
        }
196
0
    }
197
0
}
198
199
// ------------------------------------------------------------------------------------------------
200
// Imports the given file into the given scene structure.
201
11
void NFFImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSystem *pIOHandler) {
202
11
    std::unique_ptr<IOStream> stream(pIOHandler->Open(file, "rb"));
203
11
    if (!stream) {
204
0
        throw DeadlyImportError("Failed to open NFF file ", file, ".");
205
0
    }
206
207
    // allocate storage and copy the contents of the file to a memory buffer
208
    // (terminate it with zero)
209
11
    std::vector<char> mBuffer2;
210
11
    TextFileToBuffer(stream.get(), mBuffer2);
211
11
    const char *buffer = &mBuffer2[0];
212
213
    // mesh arrays - separate here to make the handling of the pointers below easier.
214
11
    std::vector<MeshInfo> meshes;
215
11
    std::vector<MeshInfo> meshesWithNormals;
216
11
    std::vector<MeshInfo> meshesWithUVCoords;
217
11
    std::vector<MeshInfo> meshesLocked;
218
219
11
    char line[4096];
220
11
    const char *lineEnd = &line[4096];
221
11
    const char *sz;
222
223
224
    // camera parameters
225
11
    aiVector3D camPos, camUp(0.f, 1.f, 0.f), camLookAt(0.f, 0.f, 1.f);
226
11
    ai_real angle = 45.f;
227
11
    aiVector2D resolution;
228
229
11
    bool hasCam = false;
230
231
11
    MeshInfo *currentMeshWithNormals = nullptr;
232
11
    MeshInfo *currentMesh = nullptr;
233
11
    MeshInfo *currentMeshWithUVCoords = nullptr;
234
235
11
    ShadingInfo s; // current material info
236
237
    // degree of tessellation
238
11
    unsigned int iTesselation = 4;
239
240
    // some temporary variables we need to parse the file
241
11
    unsigned int sphere = 0,
242
11
                 cylinder = 0,
243
11
                 cone = 0,
244
11
                 numNamed = 0,
245
11
                 dodecahedron = 0,
246
11
                 octahedron = 0,
247
11
                 tetrahedron = 0,
248
11
                 hexahedron = 0;
249
250
    // lights imported from the file
251
11
    std::vector<Light> lights;
252
253
    // check whether this is the NFF2 file format
254
11
    if (TokenMatch(buffer, "nff", 3)) {
255
0
        const ai_real qnan = get_qnan();
256
0
        const aiColor4D cQNAN = aiColor4D(qnan, 0.f, 0.f, 1.f);
257
0
        const aiVector3D vQNAN = aiVector3D(qnan, 0.f, 0.f);
258
259
        // another NFF file format ... just a raw parser has been implemented
260
        // no support for further details, I don't think it is worth the effort
261
        // http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/nff/nff2.html
262
        // http://www.netghost.narod.ru/gff/graphics/summary/sense8.htm
263
264
        // First of all: remove all comments from the file
265
0
        CommentRemover::RemoveLineComments("//", &mBuffer2[0]);
266
267
0
        while (GetNextLine(buffer, line)) {
268
0
            SkipSpaces(line, &sz, lineEnd);
269
0
            if (TokenMatch(sz, "version", 7)) {
270
0
                ASSIMP_LOG_INFO("NFF (Sense8) file format: ", sz);
271
0
            } else if (TokenMatch(sz, "viewpos", 7)) {
272
0
                AI_NFF_PARSE_TRIPLE(camPos);
273
0
                hasCam = true;
274
0
            } else if (TokenMatch(sz, "viewdir", 7)) {
275
0
                AI_NFF_PARSE_TRIPLE(camLookAt);
276
0
                hasCam = true;
277
0
            }
278
            // This starts a new object section
279
0
            else if (!IsSpaceOrNewLine(*sz)) {
280
0
                unsigned int subMeshIdx = 0;
281
282
                // read the name of the object, skip all spaces
283
                // at the end of it.
284
0
                const char *sz3 = sz;
285
0
                while (!IsSpaceOrNewLine(*sz))
286
0
                    ++sz;
287
0
                std::string objectName = std::string(sz3, (unsigned int)(sz - sz3));
288
289
0
                const unsigned int objStart = (unsigned int)meshes.size();
290
291
                // There could be a material table in a separate file
292
0
                std::vector<ShadingInfo> materialTable;
293
0
                while (true) {
294
0
                    AI_NFF2_GET_NEXT_TOKEN();
295
296
                    // material table - an external file
297
0
                    if (TokenMatch(sz, "mtable", 6)) {
298
0
                        SkipSpaces(&sz, lineEnd);
299
0
                        sz3 = sz;
300
0
                        while (!IsSpaceOrNewLine(*sz))
301
0
                            ++sz;
302
0
                        const unsigned int diff = (unsigned int)(sz - sz3);
303
0
                        if (!diff)
304
0
                            ASSIMP_LOG_WARN("NFF2: Found empty mtable token");
305
0
                        else {
306
                            // The material table has the file extension .mat.
307
                            // If it is not there, we need to append it
308
0
                            std::string path = std::string(sz3, diff);
309
0
                            if (std::string::npos == path.find_last_of(".mat")) {
310
0
                                path.append(".mat");
311
0
                            }
312
313
                            // Now extract the working directory from the path to
314
                            // this file and append the material library filename
315
                            // to it.
316
0
                            std::string::size_type sepPos;
317
0
                            if ((std::string::npos == (sepPos = path.find_last_of('\\')) || !sepPos) &&
318
0
                                    (std::string::npos == (sepPos = path.find_last_of('/')) || !sepPos)) {
319
0
                                sepPos = file.find_last_of('\\');
320
0
                                if (std::string::npos == sepPos) {
321
0
                                    sepPos = file.find_last_of('/');
322
0
                                }
323
0
                                if (std::string::npos != sepPos) {
324
0
                                    path = file.substr(0, sepPos + 1) + path;
325
0
                                }
326
0
                            }
327
0
                            LoadNFF2MaterialTable(materialTable, path, pIOHandler);
328
0
                        }
329
0
                    } else
330
0
                        break;
331
0
                }
332
333
                // read the number of vertices
334
0
                unsigned int num = strtoul10(sz, &sz);
335
336
                // temporary storage
337
0
                std::vector<aiColor4D> tempColors;
338
0
                std::vector<aiVector3D> tempPositions, tempTextureCoords, tempNormals;
339
340
0
                bool hasNormals = false, hasUVs = false, hasColor = false;
341
342
0
                tempPositions.reserve(num);
343
0
                tempColors.reserve(num);
344
0
                tempNormals.reserve(num);
345
0
                tempTextureCoords.reserve(num);
346
0
                for (unsigned int i = 0; i < num; ++i) {
347
0
                    AI_NFF2_GET_NEXT_TOKEN();
348
0
                    aiVector3D v;
349
0
                    AI_NFF_PARSE_TRIPLE(v);
350
0
                    tempPositions.push_back(v);
351
352
                    // parse all other attributes in the line
353
0
                    while (true) {
354
0
                        SkipSpaces(&sz, lineEnd);
355
0
                        if (IsLineEnd(*sz)) break;
356
357
                        // color definition
358
0
                        if (TokenMatch(sz, "0x", 2)) {
359
0
                            hasColor = true;
360
0
                            unsigned int numIdx = strtoul16(sz, &sz);
361
0
                            aiColor4D clr;
362
0
                            clr.a = 1.f;
363
364
                            // 0xRRGGBB
365
0
                            clr.r = ((numIdx >> 16u) & 0xff) / 255.f;
366
0
                            clr.g = ((numIdx >> 8u) & 0xff) / 255.f;
367
0
                            clr.b = ((numIdx)&0xff) / 255.f;
368
0
                            tempColors.push_back(clr);
369
0
                        }
370
                        // normal vector
371
0
                        else if (TokenMatch(sz, "norm", 4)) {
372
0
                            hasNormals = true;
373
0
                            AI_NFF_PARSE_TRIPLE(v);
374
0
                            tempNormals.push_back(v);
375
0
                        }
376
                        // UV coordinate
377
0
                        else if (TokenMatch(sz, "uv", 2)) {
378
0
                            hasUVs = true;
379
0
                            AI_NFF_PARSE_FLOAT(v.x);
380
0
                            AI_NFF_PARSE_FLOAT(v.y);
381
0
                            v.z = 0.f;
382
0
                            tempTextureCoords.push_back(v);
383
0
                        }
384
0
                    }
385
386
                    // fill in dummies for all attributes that have not been set
387
0
                    if (tempNormals.size() != tempPositions.size())
388
0
                        tempNormals.push_back(vQNAN);
389
390
0
                    if (tempTextureCoords.size() != tempPositions.size())
391
0
                        tempTextureCoords.push_back(vQNAN);
392
393
0
                    if (tempColors.size() != tempPositions.size())
394
0
                        tempColors.push_back(cQNAN);
395
0
                }
396
397
0
                AI_NFF2_GET_NEXT_TOKEN();
398
0
                if (!num)
399
0
                    throw DeadlyImportError("NFF2: There are zero vertices");
400
0
                num = strtoul10(sz, &sz);
401
402
0
                std::vector<unsigned int> tempIdx;
403
0
                tempIdx.reserve(10);
404
0
                for (unsigned int i = 0; i < num; ++i) {
405
0
                    AI_NFF2_GET_NEXT_TOKEN();
406
0
                    SkipSpaces(line, &sz, lineEnd);
407
0
                    unsigned int numIdx = strtoul10(sz, &sz);
408
409
                    // read all faces indices
410
0
                    if (numIdx) {
411
0
                        tempIdx.resize(numIdx);
412
413
0
                        for (unsigned int a = 0; a < numIdx; ++a) {
414
0
                            SkipSpaces(sz, &sz, lineEnd);
415
0
                            unsigned int m = strtoul10(sz, &sz);
416
0
                            if (m >= (unsigned int)tempPositions.size()) {
417
0
                                ASSIMP_LOG_ERROR("NFF2: Vertex index overflow");
418
0
                                m = 0;
419
0
                            }
420
0
                            tempIdx[a] = m;
421
0
                        }
422
0
                    }
423
424
                    // build a temporary shader object for the face.
425
0
                    ShadingInfo shader;
426
0
                    unsigned int matIdx = 0;
427
428
                    // white material color - we have vertex colors
429
0
                    shader.color = aiColor3D(1.f, 1.f, 1.f);
430
0
                    aiColor4D c = aiColor4D(1.f, 1.f, 1.f, 1.f);
431
0
                    while (true) {
432
0
                        SkipSpaces(sz, &sz, lineEnd);
433
0
                        if (IsLineEnd(*sz)) break;
434
435
                        // per-polygon colors
436
0
                        if (TokenMatch(sz, "0x", 2)) {
437
0
                            hasColor = true;
438
0
                            const char *sz2 = sz;
439
0
                            numIdx = strtoul16(sz, &sz);
440
0
                            const unsigned int diff = (unsigned int)(sz - sz2);
441
442
                            // 0xRRGGBB
443
0
                            if (diff > 3) {
444
0
                                c.r = ((numIdx >> 16u) & 0xff) / 255.f;
445
0
                                c.g = ((numIdx >> 8u) & 0xff) / 255.f;
446
0
                                c.b = ((numIdx)&0xff) / 255.f;
447
0
                            }
448
                            // 0xRGB
449
0
                            else {
450
0
                                c.r = ((numIdx >> 8u) & 0xf) / 16.f;
451
0
                                c.g = ((numIdx >> 4u) & 0xf) / 16.f;
452
0
                                c.b = ((numIdx)&0xf) / 16.f;
453
0
                            }
454
0
                        }
455
                        // TODO - implement texture mapping here
456
#if 0
457
                        // mirror vertex texture coordinate?
458
                        else if (TokenMatch(sz,"mirror",6))
459
                        {
460
                        }
461
                        // texture coordinate scaling
462
                        else if (TokenMatch(sz,"scale",5))
463
                        {
464
                        }
465
                        // texture coordinate translation
466
                        else if (TokenMatch(sz,"trans",5))
467
                        {
468
                        }
469
                        // texture coordinate rotation angle
470
                        else if (TokenMatch(sz,"rot",3))
471
                        {
472
                        }
473
#endif
474
475
                        // texture file name for this polygon + mapping information
476
0
                        else if ('_' == sz[0]) {
477
                            // get mapping information
478
0
                            switch (sz[1]) {
479
0
                                case 'v':
480
0
                                case 'V':
481
482
0
                                    shader.shaded = false;
483
0
                                    break;
484
485
0
                                case 't':
486
0
                                case 'T':
487
0
                                case 'u':
488
0
                                case 'U':
489
490
0
                                    ASSIMP_LOG_WARN("Unsupported NFF2 texture attribute: trans");
491
0
                            };
492
0
                            if (!sz[1] || '_' != sz[2]) {
493
0
                                ASSIMP_LOG_WARN("NFF2: Expected underscore after texture attributes");
494
0
                                continue;
495
0
                            }
496
0
                            const char *sz2 = sz + 3;
497
0
                            while (!IsSpaceOrNewLine(*sz))
498
0
                                ++sz;
499
0
                            const unsigned int diff = (unsigned int)(sz - sz2);
500
0
                            if (diff) shader.texFile = std::string(sz2, diff);
501
0
                        }
502
503
                        // Two-sided material?
504
0
                        else if (TokenMatch(sz, "both", 4)) {
505
0
                            shader.twoSided = true;
506
0
                        }
507
508
                        // Material ID?
509
0
                        else if (!materialTable.empty() && TokenMatch(sz, "matid", 5)) {
510
0
                            SkipSpaces(&sz, lineEnd);
511
0
                            matIdx = strtoul10(sz, &sz);
512
0
                            if (matIdx >= materialTable.size()) {
513
0
                                ASSIMP_LOG_ERROR("NFF2: Material index overflow.");
514
0
                                matIdx = 0;
515
0
                            }
516
517
                            // now combine our current shader with the shader we
518
                            // read from the material table.
519
0
                            ShadingInfo &mat = materialTable[matIdx];
520
0
                            shader.ambient = mat.ambient;
521
0
                            shader.diffuse = mat.diffuse;
522
0
                            shader.emissive = mat.emissive;
523
0
                            shader.opacity = mat.opacity;
524
0
                            shader.specular = mat.specular;
525
0
                            shader.shininess = mat.shininess;
526
0
                        } else
527
0
                            SkipToken(sz, lineEnd);
528
0
                    }
529
530
                    // search the list of all shaders we have for this object whether
531
                    // there is an identical one. In this case, we append our mesh
532
                    // data to it.
533
0
                    MeshInfo *mesh = nullptr;
534
0
                    for (std::vector<MeshInfo>::iterator it = meshes.begin() + objStart, end = meshes.end();
535
0
                            it != end; ++it) {
536
0
                        if ((*it).shader == shader && (*it).matIndex == matIdx) {
537
                            // we have one, we can append our data to it
538
0
                            mesh = &(*it);
539
0
                        }
540
0
                    }
541
0
                    if (!mesh) {
542
0
                        meshes.emplace_back(PatchType_Simple, false);
543
0
                        mesh = &meshes.back();
544
0
                        mesh->matIndex = matIdx;
545
546
                        // We need to add a new mesh to the list. We assign
547
                        // an unique name to it to make sure the scene will
548
                        // pass the validation step for the moment.
549
                        // TODO: fix naming of objects in the scene-graph later
550
0
                        if (objectName.length()) {
551
0
                            ::strncpy(mesh->name, objectName.c_str(), objectName.size());
552
0
                            ASSIMP_itoa10(&mesh->name[objectName.length()], 30, subMeshIdx++);
553
0
                        }
554
555
                        // copy the shader to the mesh.
556
0
                        mesh->shader = shader;
557
0
                    }
558
559
                    // fill the mesh with data
560
0
                    if (!tempIdx.empty()) {
561
0
                        mesh->faces.push_back((unsigned int)tempIdx.size());
562
0
                        for (std::vector<unsigned int>::const_iterator it = tempIdx.begin(), end = tempIdx.end();
563
0
                                it != end; ++it) {
564
0
                            unsigned int m = *it;
565
566
                            // copy colors -vertex color specifications override polygon color specifications
567
0
                            if (hasColor) {
568
0
                                const aiColor4D &clr = tempColors[m];
569
0
                                mesh->colors.push_back((is_qnan(clr.r) ? c : clr));
570
0
                            }
571
572
                            // positions should always be there
573
0
                            mesh->vertices.push_back(tempPositions[m]);
574
575
                            // copy normal vectors
576
0
                            if (hasNormals)
577
0
                                mesh->normals.push_back(tempNormals[m]);
578
579
                            // copy texture coordinates
580
0
                            if (hasUVs)
581
0
                                mesh->uvs.push_back(tempTextureCoords[m]);
582
0
                        }
583
0
                    }
584
0
                }
585
0
                if (!num) throw DeadlyImportError("NFF2: There are zero faces");
586
0
            }
587
0
        }
588
0
        camLookAt = camLookAt + camPos;
589
0
    } else // "Normal" Neutral file format that is quite more common
590
11
    {
591
15.8k
        while (GetNextLine(buffer, line)) {
592
15.8k
            sz = line;
593
15.8k
            if ('p' == line[0] || TokenMatch(sz, "tpp", 3)) {
594
262
                MeshInfo *out = nullptr;
595
596
                // 'tpp' - texture polygon patch primitive
597
262
                if ('t' == line[0]) {
598
259
                    currentMeshWithUVCoords = nullptr;
599
259
                    for (auto &mesh : meshesWithUVCoords) {
600
253
                        if (mesh.shader == s) {
601
253
                            currentMeshWithUVCoords = &mesh;
602
253
                            break;
603
253
                        }
604
253
                    }
605
606
259
                    if (!currentMeshWithUVCoords) {
607
6
                        meshesWithUVCoords.emplace_back(PatchType_UVAndNormals);
608
6
                        currentMeshWithUVCoords = &meshesWithUVCoords.back();
609
6
                        currentMeshWithUVCoords->shader = s;
610
6
                    }
611
259
                    out = currentMeshWithUVCoords;
612
259
                }
613
                // 'pp' - polygon patch primitive
614
3
                else if ('p' == line[1]) {
615
0
                    currentMeshWithNormals = nullptr;
616
0
                    for (auto &mesh : meshesWithNormals) {
617
0
                        if (mesh.shader == s) {
618
0
                            currentMeshWithNormals = &mesh;
619
0
                            break;
620
0
                        }
621
0
                    }
622
623
0
                    if (!currentMeshWithNormals) {
624
0
                        meshesWithNormals.emplace_back(PatchType_Normals);
625
0
                        currentMeshWithNormals = &meshesWithNormals.back();
626
0
                        currentMeshWithNormals->shader = s;
627
0
                    }
628
0
                    sz = &line[2];
629
0
                    out = currentMeshWithNormals;
630
0
                }
631
                // 'p' - polygon primitive
632
3
                else {
633
3
                    currentMesh = nullptr;
634
3
                    for (auto &mesh : meshes) {
635
1
                        if (mesh.shader == s) {
636
1
                            currentMesh = &mesh;
637
1
                            break;
638
1
                        }
639
1
                    }
640
641
3
                    if (!currentMesh) {
642
2
                        meshes.emplace_back(PatchType_Simple);
643
2
                        currentMesh = &meshes.back();
644
2
                        currentMesh->shader = s;
645
2
                    }
646
3
                    sz = &line[1];
647
3
                    out = currentMesh;
648
3
                }
649
262
                SkipSpaces(sz, &sz, lineEnd);
650
262
                unsigned int m = strtoul10(sz);
651
652
                // ---- flip the face order
653
262
                out->vertices.resize(out->vertices.size() + m);
654
262
                if (out != currentMesh) {
655
259
                    out->normals.resize(out->vertices.size());
656
259
                }
657
262
                if (out == currentMeshWithUVCoords) {
658
259
                    out->uvs.resize(out->vertices.size());
659
259
                }
660
520
                for (unsigned int n = 0; n < m; ++n) {
661
258
                    if (!GetNextLine(buffer, line)) {
662
0
                        ASSIMP_LOG_ERROR("NFF: Unexpected EOF was encountered. Patch definition incomplete");
663
0
                        continue;
664
0
                    }
665
666
258
                    aiVector3D v;
667
258
                    sz = &line[0];
668
258
                    AI_NFF_PARSE_TRIPLE(v);
669
258
                    out->vertices[out->vertices.size() - n - 1] = v;
670
671
258
                    if (out != currentMesh) {
672
258
                        AI_NFF_PARSE_TRIPLE(v);
673
258
                        out->normals[out->vertices.size() - n - 1] = v;
674
258
                    }
675
258
                    if (out == currentMeshWithUVCoords) {
676
                        // FIX: in one test file this wraps over multiple lines
677
258
                        SkipSpaces(&sz, lineEnd);
678
258
                        if (IsLineEnd(*sz)) {
679
136
                            GetNextLine(buffer, line);
680
136
                            sz = line;
681
136
                        }
682
258
                        AI_NFF_PARSE_FLOAT(v.x);
683
258
                        SkipSpaces(&sz, lineEnd);
684
258
                        if (IsLineEnd(*sz)) {
685
4
                            GetNextLine(buffer, line);
686
4
                            sz = line;
687
4
                        }
688
258
                        AI_NFF_PARSE_FLOAT(v.y);
689
258
                        v.y = 1.f - v.y;
690
258
                        out->uvs[out->vertices.size() - n - 1] = v;
691
258
                    }
692
258
                }
693
262
                out->faces.push_back(m);
694
262
            }
695
            // 'f' - shading information block
696
15.6k
            else if (TokenMatch(sz, "f", 1)) {
697
109
                ai_real d;
698
699
                // read the RGB colors
700
109
                AI_NFF_PARSE_TRIPLE(s.color);
701
702
                // read the other properties
703
109
                AI_NFF_PARSE_FLOAT(s.diffuse.r);
704
109
                AI_NFF_PARSE_FLOAT(s.specular.r);
705
109
                AI_NFF_PARSE_FLOAT(d); // skip shininess and transmittance
706
109
                AI_NFF_PARSE_FLOAT(d);
707
109
                AI_NFF_PARSE_FLOAT(s.refracti);
708
709
                // NFF2 uses full colors here so we need to use them too
710
                // although NFF uses simple scaling factors
711
109
                s.diffuse.g = s.diffuse.b = s.diffuse.r;
712
109
                s.specular.g = s.specular.b = s.specular.r;
713
714
                // if the next one is NOT a number we assume it is a texture file name
715
                // this feature is used by some NFF files on the internet and it has
716
                // been implemented as it can be really useful
717
109
                SkipSpaces(&sz, lineEnd);
718
109
                if (!IsNumeric(*sz)) {
719
                    // TODO: Support full file names with spaces and quotation marks ...
720
109
                    const char *p = sz;
721
109
                    while (!IsSpaceOrNewLine(*sz))
722
0
                        ++sz;
723
724
109
                    unsigned int diff = (unsigned int)(sz - p);
725
109
                    if (diff) {
726
0
                        s.texFile = std::string(p, diff);
727
0
                    }
728
109
                } else {
729
0
                    AI_NFF_PARSE_FLOAT(s.ambient); // optional
730
0
                }
731
15.5k
            } else if (TokenMatch(sz, "shader", 6)) { // 'shader' - other way to specify a texture
732
0
                SkipSpaces(&sz, lineEnd);
733
0
                const char *old = sz;
734
0
                while (!IsSpaceOrNewLine(*sz))
735
0
                    ++sz;
736
0
                s.texFile = std::string(old, (uintptr_t)sz - (uintptr_t)old);
737
0
            }
738
            // 'l' - light source
739
15.5k
            else if (TokenMatch(sz, "l", 1)) {
740
334
                lights.emplace_back();
741
334
                Light &light = lights.back();
742
743
334
                AI_NFF_PARSE_TRIPLE(light.position);
744
334
                AI_NFF_PARSE_FLOAT(light.intensity);
745
334
                AI_NFF_PARSE_TRIPLE(light.color);
746
334
            }
747
            // 's' - sphere
748
15.1k
            else if (TokenMatch(sz, "s", 1)) {
749
0
                meshesLocked.emplace_back(PatchType_Simple, true);
750
0
                MeshInfo &curMesh = meshesLocked.back();
751
0
                curMesh.shader = s;
752
0
                curMesh.shader.mapping = aiTextureMapping_SPHERE;
753
754
0
                AI_NFF_PARSE_SHAPE_INFORMATION();
755
756
                // we don't need scaling or translation here - we do it in the node's transform
757
0
                StandardShapes::MakeSphere(iTesselation, curMesh.vertices);
758
0
                curMesh.faces.resize(curMesh.vertices.size() / 3, 3);
759
760
                // generate a name for the mesh
761
0
                ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "sphere_%i", sphere++);
762
0
            }
763
            // 'dod' - dodecahedron
764
15.1k
            else if (TokenMatch(sz, "dod", 3)) {
765
948
                meshesLocked.emplace_back(PatchType_Simple, true);
766
948
                MeshInfo &curMesh = meshesLocked.back();
767
948
                curMesh.shader = s;
768
948
                curMesh.shader.mapping = aiTextureMapping_SPHERE;
769
770
948
                AI_NFF_PARSE_SHAPE_INFORMATION();
771
772
                // we don't need scaling or translation here - we do it in the node's transform
773
948
                StandardShapes::MakeDodecahedron(curMesh.vertices);
774
948
                curMesh.faces.resize(curMesh.vertices.size() / 3, 3);
775
776
                // generate a name for the mesh
777
948
                ::ai_snprintf(curMesh.name, 128, "dodecahedron_%i", dodecahedron++);
778
948
            }
779
780
            // 'oct' - octahedron
781
14.2k
            else if (TokenMatch(sz, "oct", 3)) {
782
0
                meshesLocked.emplace_back(PatchType_Simple, true);
783
0
                MeshInfo &curMesh = meshesLocked.back();
784
0
                curMesh.shader = s;
785
0
                curMesh.shader.mapping = aiTextureMapping_SPHERE;
786
787
0
                AI_NFF_PARSE_SHAPE_INFORMATION();
788
789
                // we don't need scaling or translation here - we do it in the node's transform
790
0
                StandardShapes::MakeOctahedron(curMesh.vertices);
791
0
                curMesh.faces.resize(curMesh.vertices.size() / 3, 3);
792
793
                // generate a name for the mesh
794
0
                ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "octahedron_%i", octahedron++);
795
0
            }
796
797
            // 'tet' - tetrahedron
798
14.2k
            else if (TokenMatch(sz, "tet", 3)) {
799
0
                meshesLocked.emplace_back(PatchType_Simple, true);
800
0
                MeshInfo &curMesh = meshesLocked.back();
801
0
                curMesh.shader = s;
802
0
                curMesh.shader.mapping = aiTextureMapping_SPHERE;
803
804
0
                AI_NFF_PARSE_SHAPE_INFORMATION();
805
806
                // we don't need scaling or translation here - we do it in the node's transform
807
0
                StandardShapes::MakeTetrahedron(curMesh.vertices);
808
0
                curMesh.faces.resize(curMesh.vertices.size() / 3, 3);
809
810
                // generate a name for the mesh
811
0
                ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "tetrahedron_%i", tetrahedron++);
812
0
            }
813
814
            // 'hex' - hexahedron
815
14.2k
            else if (TokenMatch(sz, "hex", 3)) {
816
0
                meshesLocked.emplace_back(PatchType_Simple, true);
817
0
                MeshInfo &curMesh = meshesLocked.back();
818
0
                curMesh.shader = s;
819
0
                curMesh.shader.mapping = aiTextureMapping_BOX;
820
821
0
                AI_NFF_PARSE_SHAPE_INFORMATION();
822
823
                // we don't need scaling or translation here - we do it in the node's transform
824
0
                StandardShapes::MakeHexahedron(curMesh.vertices);
825
0
                curMesh.faces.resize(curMesh.vertices.size() / 3, 3);
826
827
                // generate a name for the mesh
828
0
                ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "hexahedron_%i", hexahedron++);
829
0
            }
830
            // 'c' - cone
831
14.2k
            else if (TokenMatch(sz, "c", 1)) {
832
1
                meshesLocked.emplace_back(PatchType_Simple, true);
833
1
                MeshInfo &curMesh = meshesLocked.back();
834
1
                curMesh.shader = s;
835
1
                curMesh.shader.mapping = aiTextureMapping_CYLINDER;
836
837
1
                if (!GetNextLine(buffer, line)) {
838
0
                    ASSIMP_LOG_ERROR("NFF: Unexpected end of file (cone definition not complete)");
839
0
                    break;
840
0
                }
841
1
                sz = line;
842
843
                // read the two center points and the respective radii
844
1
                aiVector3D center1, center2;
845
1
                ai_real radius1 = 0.f, radius2 = 0.f;
846
1
                AI_NFF_PARSE_TRIPLE(center1);
847
1
                AI_NFF_PARSE_FLOAT(radius1);
848
849
1
                if (!GetNextLine(buffer, line)) {
850
0
                    ASSIMP_LOG_ERROR("NFF: Unexpected end of file (cone definition not complete)");
851
0
                    break;
852
0
                }
853
1
                sz = line;
854
855
1
                AI_NFF_PARSE_TRIPLE(center2);
856
1
                AI_NFF_PARSE_FLOAT(radius2);
857
858
                // compute the center point of the cone/cylinder -
859
                // it is its local transformation origin
860
1
                curMesh.dir = center2 - center1;
861
1
                curMesh.center = center1 + curMesh.dir / (ai_real)2.0;
862
863
1
                ai_real f;
864
1
                if ((f = curMesh.dir.Length()) < 10e-3f) {
865
0
                    ASSIMP_LOG_ERROR("NFF: Cone height is close to zero");
866
0
                    continue;
867
0
                }
868
1
                curMesh.dir /= f; // normalize
869
870
                // generate the cone - it consists of simple triangles
871
1
                StandardShapes::MakeCone(f, radius1, radius2,
872
1
                        integer_pow(4, iTesselation), curMesh.vertices);
873
874
                // MakeCone() returns tris
875
1
                curMesh.faces.resize(curMesh.vertices.size() / 3, 3);
876
877
                // generate a name for the mesh. 'cone' if it a cone,
878
                // 'cylinder' if it is a cylinder. Funny, isn't it?
879
1
                if (radius1 != radius2) {
880
1
                    ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "cone_%i", cone++);
881
1
                } else {
882
0
                    ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "cylinder_%i", cylinder++);
883
0
                }
884
1
            }
885
            // 'tess' - tessellation
886
14.2k
            else if (TokenMatch(sz, "tess", 4)) {
887
0
                SkipSpaces(&sz, lineEnd);
888
0
                iTesselation = strtoul10(sz);
889
0
            }
890
            // 'from' - camera position
891
14.2k
            else if (TokenMatch(sz, "from", 4)) {
892
0
                AI_NFF_PARSE_TRIPLE(camPos);
893
0
                hasCam = true;
894
0
            }
895
            // 'at' - camera look-at vector
896
14.2k
            else if (TokenMatch(sz, "at", 2)) {
897
0
                AI_NFF_PARSE_TRIPLE(camLookAt);
898
0
                hasCam = true;
899
0
            }
900
            // 'up' - camera up vector
901
14.2k
            else if (TokenMatch(sz, "up", 2)) {
902
0
                AI_NFF_PARSE_TRIPLE(camUp);
903
0
                hasCam = true;
904
0
            }
905
            // 'angle' - (half?) camera field of view
906
14.2k
            else if (TokenMatch(sz, "angle", 5)) {
907
0
                AI_NFF_PARSE_FLOAT(angle);
908
0
                hasCam = true;
909
0
            }
910
            // 'resolution' - used to compute the screen aspect
911
14.2k
            else if (TokenMatch(sz, "resolution", 10)) {
912
0
                AI_NFF_PARSE_FLOAT(resolution.x);
913
0
                AI_NFF_PARSE_FLOAT(resolution.y);
914
0
                hasCam = true;
915
0
            }
916
            // 'pb' - bezier patch. Not supported yet
917
14.2k
            else if (TokenMatch(sz, "pb", 2)) {
918
0
                ASSIMP_LOG_ERROR("NFF: Encountered unsupported ID: bezier patch");
919
0
            }
920
            // 'pn' - NURBS. Not supported yet
921
14.2k
            else if (TokenMatch(sz, "pn", 2) || TokenMatch(sz, "pnn", 3)) {
922
0
                ASSIMP_LOG_ERROR("NFF: Encountered unsupported ID: NURBS");
923
0
            }
924
            // '' - comment
925
14.2k
            else if ('#' == line[0]) {
926
0
                const char *space;
927
0
                SkipSpaces(&line[1], &space, lineEnd);
928
0
                if (!IsLineEnd(*space)) {
929
0
                    ASSIMP_LOG_INFO(space);
930
0
                }
931
0
            }
932
15.8k
        }
933
11
    }
934
935
    // copy all arrays into one large
936
11
    meshes.reserve(meshes.size() + meshesLocked.size() + meshesWithNormals.size() + meshesWithUVCoords.size());
937
11
    meshes.insert(meshes.end(), meshesLocked.begin(), meshesLocked.end());
938
11
    meshes.insert(meshes.end(), meshesWithNormals.begin(), meshesWithNormals.end());
939
11
    meshes.insert(meshes.end(), meshesWithUVCoords.begin(), meshesWithUVCoords.end());
940
941
    // now generate output meshes. first find out how many meshes we'll need
942
11
    std::vector<MeshInfo>::const_iterator it = meshes.begin(), end = meshes.end();
943
953
    for (; it != end; ++it) {
944
942
        if (!(*it).faces.empty()) {
945
942
            ++pScene->mNumMeshes;
946
942
            if ((*it).name[0]) ++numNamed;
947
942
        }
948
942
    }
949
950
    // generate a dummy root node - assign all unnamed elements such
951
    // as polygons and polygon patches to the root node and generate
952
    // sub nodes for named objects such as spheres and cones.
953
11
    aiNode *const root = new aiNode();
954
11
    root->mName.Set("<NFF_Root>");
955
11
    root->mNumChildren = numNamed + (hasCam ? 1 : 0) + (unsigned int)lights.size();
956
11
    root->mNumMeshes = pScene->mNumMeshes - numNamed;
957
958
11
    aiNode **ppcChildren = nullptr;
959
11
    unsigned int *pMeshes = nullptr;
960
11
    if (root->mNumMeshes)
961
6
        pMeshes = root->mMeshes = new unsigned int[root->mNumMeshes];
962
11
    if (root->mNumChildren)
963
10
        ppcChildren = root->mChildren = new aiNode *[root->mNumChildren];
964
965
    // generate the camera
966
11
    if (hasCam) {
967
0
        ai_assert(ppcChildren);
968
0
        aiNode *nd = new aiNode();
969
0
        *ppcChildren = nd;
970
0
        nd->mName.Set("<NFF_Camera>");
971
0
        nd->mParent = root;
972
973
        // allocate the camera in the scene
974
0
        pScene->mNumCameras = 1;
975
0
        pScene->mCameras = new aiCamera *[1];
976
0
        aiCamera *c = pScene->mCameras[0] = new aiCamera;
977
978
0
        c->mName = nd->mName; // make sure the names are identical
979
0
        c->mHorizontalFOV = AI_DEG_TO_RAD(angle);
980
0
        c->mLookAt = camLookAt - camPos;
981
0
        c->mPosition = camPos;
982
0
        c->mUp = camUp;
983
984
        // If the resolution is not specified in the file, we
985
        // need to set 1.0 as aspect.
986
0
        c->mAspect = (!resolution.y ? 0.f : resolution.x / resolution.y);
987
0
        ++ppcChildren;
988
0
    }
989
990
    // generate light sources
991
11
    if (!lights.empty()) {
992
6
        ai_assert(ppcChildren);
993
6
        pScene->mNumLights = (unsigned int)lights.size();
994
6
        pScene->mLights = new aiLight *[pScene->mNumLights];
995
340
        for (unsigned int i = 0; i < pScene->mNumLights; ++i, ++ppcChildren) {
996
334
            const Light &l = lights[i];
997
998
334
            aiNode *nd = new aiNode();
999
334
            *ppcChildren = nd;
1000
334
            nd->mParent = root;
1001
1002
334
            nd->mName.length = ::ai_snprintf(nd->mName.data, 1024, "<NFF_Light%u>", i);
1003
1004
            // allocate the light in the scene data structure
1005
334
            aiLight *out = pScene->mLights[i] = new aiLight();
1006
334
            out->mName = nd->mName; // make sure the names are identical
1007
334
            out->mType = aiLightSource_POINT;
1008
334
            out->mColorDiffuse = out->mColorSpecular = l.color * l.intensity;
1009
334
            out->mPosition = l.position;
1010
334
        }
1011
6
    }
1012
1013
11
    if (!pScene->mNumMeshes) throw DeadlyImportError("NFF: No meshes loaded");
1014
11
    pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
1015
11
    pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials = pScene->mNumMeshes];
1016
11
    unsigned int m = 0;
1017
953
    for (it = meshes.begin(); it != end; ++it) {
1018
942
        if ((*it).faces.empty()) continue;
1019
1020
942
        const MeshInfo &src = *it;
1021
942
        aiMesh *const mesh = pScene->mMeshes[m] = new aiMesh();
1022
942
        mesh->mNumVertices = (unsigned int)src.vertices.size();
1023
942
        mesh->mNumFaces = (unsigned int)src.faces.size();
1024
1025
        // Generate sub nodes for named meshes
1026
942
        if (src.name[0] && nullptr != ppcChildren) {
1027
934
            aiNode *const node = *ppcChildren = new aiNode();
1028
934
            node->mParent = root;
1029
934
            node->mNumMeshes = 1;
1030
934
            node->mMeshes = new unsigned int[1];
1031
934
            node->mMeshes[0] = m;
1032
934
            node->mName.Set(src.name);
1033
1034
            // setup the transformation matrix of the node
1035
934
            aiMatrix4x4::FromToMatrix(aiVector3D(0.f, 1.f, 0.f),
1036
934
                    src.dir, node->mTransformation);
1037
1038
934
            aiMatrix4x4 &mat = node->mTransformation;
1039
934
            mat.a1 *= src.radius.x;
1040
934
            mat.b1 *= src.radius.x;
1041
934
            mat.c1 *= src.radius.x;
1042
934
            mat.a2 *= src.radius.y;
1043
934
            mat.b2 *= src.radius.y;
1044
934
            mat.c2 *= src.radius.y;
1045
934
            mat.a3 *= src.radius.z;
1046
934
            mat.b3 *= src.radius.z;
1047
934
            mat.c3 *= src.radius.z;
1048
934
            mat.a4 = src.center.x;
1049
934
            mat.b4 = src.center.y;
1050
934
            mat.c4 = src.center.z;
1051
1052
934
            ++ppcChildren;
1053
934
        } else {
1054
8
            *pMeshes++ = m;
1055
8
        }
1056
1057
        // copy vertex positions
1058
942
        mesh->mVertices = new aiVector3D[mesh->mNumVertices];
1059
942
        ::memcpy(mesh->mVertices, &src.vertices[0],
1060
942
                sizeof(aiVector3D) * mesh->mNumVertices);
1061
1062
        // NFF2: there could be vertex colors
1063
942
        if (!src.colors.empty()) {
1064
0
            ai_assert(src.colors.size() == src.vertices.size());
1065
1066
            // copy vertex colors
1067
0
            mesh->mColors[0] = new aiColor4D[mesh->mNumVertices];
1068
0
            ::memcpy(mesh->mColors[0], &src.colors[0],
1069
0
                    sizeof(aiColor4D) * mesh->mNumVertices);
1070
0
        }
1071
1072
942
        if (!src.normals.empty()) {
1073
5
            ai_assert(src.normals.size() == src.vertices.size());
1074
1075
            // copy normal vectors
1076
5
            mesh->mNormals = new aiVector3D[mesh->mNumVertices];
1077
5
            ::memcpy(mesh->mNormals, &src.normals[0],
1078
5
                    sizeof(aiVector3D) * mesh->mNumVertices);
1079
5
        }
1080
1081
942
        if (!src.uvs.empty()) {
1082
5
            ai_assert(src.uvs.size() == src.vertices.size());
1083
1084
            // copy texture coordinates
1085
5
            mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
1086
5
            ::memcpy(mesh->mTextureCoords[0], &src.uvs[0],
1087
5
                    sizeof(aiVector3D) * mesh->mNumVertices);
1088
5
        }
1089
1090
        // generate faces
1091
942
        unsigned int p = 0;
1092
942
        aiFace *pFace = mesh->mFaces = new aiFace[mesh->mNumFaces];
1093
942
        for (std::vector<unsigned int>::const_iterator it2 = src.faces.begin(),
1094
942
                                                       end2 = src.faces.end();
1095
35.8k
                it2 != end2; ++it2, ++pFace) {
1096
34.8k
            pFace->mIndices = new unsigned int[pFace->mNumIndices = *it2];
1097
138k
            for (unsigned int o = 0; o < pFace->mNumIndices; ++o)
1098
104k
                pFace->mIndices[o] = p++;
1099
34.8k
        }
1100
1101
        // generate a material for the mesh
1102
942
        aiMaterial *pcMat = (aiMaterial *)(pScene->mMaterials[m] = new aiMaterial());
1103
1104
942
        mesh->mMaterialIndex = m++;
1105
1106
942
        aiString matName;
1107
942
        matName.Set(AI_DEFAULT_MATERIAL_NAME);
1108
942
        pcMat->AddProperty(&matName, AI_MATKEY_NAME);
1109
1110
        // FIX: Ignore diffuse == 0
1111
942
        aiColor3D c = src.shader.color * (src.shader.diffuse.r ? src.shader.diffuse : aiColor3D(1.f, 1.f, 1.f));
1112
942
        pcMat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE);
1113
942
        c = src.shader.color * src.shader.specular;
1114
942
        pcMat->AddProperty(&c, 1, AI_MATKEY_COLOR_SPECULAR);
1115
1116
        // NFF2 - default values for NFF
1117
942
        pcMat->AddProperty(&src.shader.ambient, 1, AI_MATKEY_COLOR_AMBIENT);
1118
942
        pcMat->AddProperty(&src.shader.emissive, 1, AI_MATKEY_COLOR_EMISSIVE);
1119
942
        pcMat->AddProperty(&src.shader.opacity, 1, AI_MATKEY_OPACITY);
1120
1121
        // setup the first texture layer, if existing
1122
942
        if (src.shader.texFile.length()) {
1123
0
            matName.Set(src.shader.texFile);
1124
0
            pcMat->AddProperty(&matName, AI_MATKEY_TEXTURE_DIFFUSE(0));
1125
1126
0
            if (aiTextureMapping_UV != src.shader.mapping) {
1127
1128
0
                aiVector3D v(0.f, -1.f, 0.f);
1129
0
                pcMat->AddProperty(&v, 1, AI_MATKEY_TEXMAP_AXIS_DIFFUSE(0));
1130
0
                pcMat->AddProperty((int *)&src.shader.mapping, 1, AI_MATKEY_MAPPING_DIFFUSE(0));
1131
0
            }
1132
0
        }
1133
1134
        // setup the name of the material
1135
942
        if (src.shader.name.length()) {
1136
0
            matName.Set(src.shader.texFile);
1137
0
            pcMat->AddProperty(&matName, AI_MATKEY_NAME);
1138
0
        }
1139
1140
        // setup some more material properties that are specific to NFF2
1141
942
        int i;
1142
942
        if (src.shader.twoSided) {
1143
0
            i = 1;
1144
0
            pcMat->AddProperty(&i, 1, AI_MATKEY_TWOSIDED);
1145
0
        }
1146
942
        i = (src.shader.shaded ? aiShadingMode_Gouraud : aiShadingMode_NoShading);
1147
942
        if (src.shader.shininess) {
1148
0
            i = aiShadingMode_Phong;
1149
0
            pcMat->AddProperty(&src.shader.shininess, 1, AI_MATKEY_SHININESS);
1150
0
        }
1151
        pcMat->AddProperty(&i, 1, AI_MATKEY_SHADING_MODEL);
1152
942
    }
1153
11
    pScene->mRootNode = root;
1154
11
}
1155
1156
} // namespace Assimp
1157
1158
#endif // !! ASSIMP_BUILD_NO_NFF_IMPORTER