Coverage Report

Created: 2026-06-15 06:20

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-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 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
68
bool NFFImporter::CanRead(const std::string & pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const {
77
68
    return SimpleExtensionCheck(pFile, "nff", "enff");
78
68
}
79
80
// ------------------------------------------------------------------------------------------------
81
// Get the list of all supported file extensions
82
63.6k
const aiImporterDesc *NFFImporter::GetInfo() const {
83
63.6k
    return &desc;
84
63.6k
}
85
86
// ------------------------------------------------------------------------------------------------
87
#define AI_NFF_PARSE_FLOAT(f) \
88
0
    SkipSpaces(&sz, lineEnd);          \
89
0
    if (!IsLineEnd(*sz)) sz = fast_atoreal_move(sz, (ai_real &)f);
90
91
// ------------------------------------------------------------------------------------------------
92
#define AI_NFF_PARSE_TRIPLE(v) \
93
0
    AI_NFF_PARSE_FLOAT(v[0])   \
94
0
    AI_NFF_PARSE_FLOAT(v[1])   \
95
0
    AI_NFF_PARSE_FLOAT(v[2])
96
97
// ------------------------------------------------------------------------------------------------
98
#define AI_NFF_PARSE_SHAPE_INFORMATION()                     \
99
0
    aiVector3D center, radius(1.0f, get_qnan(), get_qnan()); \
100
0
    AI_NFF_PARSE_TRIPLE(center);                             \
101
0
    AI_NFF_PARSE_TRIPLE(radius);                             \
102
0
    if (is_qnan(radius.z)) radius.z = radius.x;              \
103
0
    if (is_qnan(radius.y)) radius.y = radius.x;              \
104
0
    curMesh.radius = radius;                                 \
105
0
    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
0
void NFFImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSystem *pIOHandler) {
202
0
    std::unique_ptr<IOStream> stream(pIOHandler->Open(file, "rb"));
203
0
    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
0
    std::vector<char> mBuffer2;
210
0
    TextFileToBuffer(stream.get(), mBuffer2);
211
0
    const char *buffer = &mBuffer2[0];
212
213
    // mesh arrays - separate here to make the handling of the pointers below easier.
214
0
    std::vector<MeshInfo> meshes;
215
0
    std::vector<MeshInfo> meshesWithNormals;
216
0
    std::vector<MeshInfo> meshesWithUVCoords;
217
0
    std::vector<MeshInfo> meshesLocked;
218
219
0
    char line[4096];
220
0
    const char *lineEnd = &line[4096];
221
0
    const char *sz;
222
223
224
    // camera parameters
225
0
    aiVector3D camPos, camUp(0.f, 1.f, 0.f), camLookAt(0.f, 0.f, 1.f);
226
0
    ai_real angle = 45.f;
227
0
    aiVector2D resolution;
228
229
0
    bool hasCam = false;
230
231
0
    MeshInfo *currentMeshWithNormals = nullptr;
232
0
    MeshInfo *currentMesh = nullptr;
233
0
    MeshInfo *currentMeshWithUVCoords = nullptr;
234
235
0
    ShadingInfo s; // current material info
236
237
    // degree of tessellation
238
0
    unsigned int iTesselation = 4;
239
240
    // some temporary variables we need to parse the file
241
0
    unsigned int sphere = 0,
242
0
                 cylinder = 0,
243
0
                 cone = 0,
244
0
                 numNamed = 0,
245
0
                 dodecahedron = 0,
246
0
                 octahedron = 0,
247
0
                 tetrahedron = 0,
248
0
                 hexahedron = 0;
249
250
    // lights imported from the file
251
0
    std::vector<Light> lights;
252
253
    // check whether this is the NFF2 file format
254
0
    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
                            const size_t copyLen = objectName.length() < (sizeof(mesh->name) - 1u) ? objectName.length() : (sizeof(mesh->name) - 1u);
552
0
                            ::memcpy(mesh->name, objectName.c_str(), copyLen);
553
0
                            mesh->name[copyLen] = '\0';
554
0
                            if (copyLen < sizeof(mesh->name) - 1u) {
555
0
                                ASSIMP_itoa10(&mesh->name[copyLen], static_cast<unsigned int>(sizeof(mesh->name) - copyLen), subMeshIdx++);
556
0
                            }
557
0
                        }
558
559
                        // copy the shader to the mesh.
560
0
                        mesh->shader = shader;
561
0
                    }
562
563
                    // fill the mesh with data
564
0
                    if (!tempIdx.empty()) {
565
0
                        mesh->faces.push_back((unsigned int)tempIdx.size());
566
0
                        for (std::vector<unsigned int>::const_iterator it = tempIdx.begin(), end = tempIdx.end();
567
0
                                it != end; ++it) {
568
0
                            unsigned int m = *it;
569
570
                            // copy colors -vertex color specifications override polygon color specifications
571
0
                            if (hasColor) {
572
0
                                const aiColor4D &clr = tempColors[m];
573
0
                                mesh->colors.push_back((is_qnan(clr.r) ? c : clr));
574
0
                            }
575
576
                            // positions should always be there
577
0
                            mesh->vertices.push_back(tempPositions[m]);
578
579
                            // copy normal vectors
580
0
                            if (hasNormals)
581
0
                                mesh->normals.push_back(tempNormals[m]);
582
583
                            // copy texture coordinates
584
0
                            if (hasUVs)
585
0
                                mesh->uvs.push_back(tempTextureCoords[m]);
586
0
                        }
587
0
                    }
588
0
                }
589
0
                if (!num) throw DeadlyImportError("NFF2: There are zero faces");
590
0
            }
591
0
        }
592
0
        camLookAt = camLookAt + camPos;
593
0
    } else // "Normal" Neutral file format that is quite more common
594
0
    {
595
0
        while (GetNextLine(buffer, line)) {
596
0
            sz = line;
597
0
            if ('p' == line[0] || TokenMatch(sz, "tpp", 3)) {
598
0
                MeshInfo *out = nullptr;
599
600
                // 'tpp' - texture polygon patch primitive
601
0
                if ('t' == line[0]) {
602
0
                    currentMeshWithUVCoords = nullptr;
603
0
                    for (auto &mesh : meshesWithUVCoords) {
604
0
                        if (mesh.shader == s) {
605
0
                            currentMeshWithUVCoords = &mesh;
606
0
                            break;
607
0
                        }
608
0
                    }
609
610
0
                    if (!currentMeshWithUVCoords) {
611
0
                        meshesWithUVCoords.emplace_back(PatchType_UVAndNormals);
612
0
                        currentMeshWithUVCoords = &meshesWithUVCoords.back();
613
0
                        currentMeshWithUVCoords->shader = s;
614
0
                    }
615
0
                    out = currentMeshWithUVCoords;
616
0
                }
617
                // 'pp' - polygon patch primitive
618
0
                else if ('p' == line[1]) {
619
0
                    currentMeshWithNormals = nullptr;
620
0
                    for (auto &mesh : meshesWithNormals) {
621
0
                        if (mesh.shader == s) {
622
0
                            currentMeshWithNormals = &mesh;
623
0
                            break;
624
0
                        }
625
0
                    }
626
627
0
                    if (!currentMeshWithNormals) {
628
0
                        meshesWithNormals.emplace_back(PatchType_Normals);
629
0
                        currentMeshWithNormals = &meshesWithNormals.back();
630
0
                        currentMeshWithNormals->shader = s;
631
0
                    }
632
0
                    sz = &line[2];
633
0
                    out = currentMeshWithNormals;
634
0
                }
635
                // 'p' - polygon primitive
636
0
                else {
637
0
                    currentMesh = nullptr;
638
0
                    for (auto &mesh : meshes) {
639
0
                        if (mesh.shader == s) {
640
0
                            currentMesh = &mesh;
641
0
                            break;
642
0
                        }
643
0
                    }
644
645
0
                    if (!currentMesh) {
646
0
                        meshes.emplace_back(PatchType_Simple);
647
0
                        currentMesh = &meshes.back();
648
0
                        currentMesh->shader = s;
649
0
                    }
650
0
                    sz = &line[1];
651
0
                    out = currentMesh;
652
0
                }
653
0
                SkipSpaces(sz, &sz, lineEnd);
654
0
                unsigned int m = strtoul10(sz);
655
656
                // ---- flip the face order
657
0
                out->vertices.resize(out->vertices.size() + m);
658
0
                if (out != currentMesh) {
659
0
                    out->normals.resize(out->vertices.size());
660
0
                }
661
0
                if (out == currentMeshWithUVCoords) {
662
0
                    out->uvs.resize(out->vertices.size());
663
0
                }
664
0
                for (unsigned int n = 0; n < m; ++n) {
665
0
                    if (!GetNextLine(buffer, line)) {
666
0
                        ASSIMP_LOG_ERROR("NFF: Unexpected EOF was encountered. Patch definition incomplete");
667
0
                        continue;
668
0
                    }
669
670
0
                    aiVector3D v;
671
0
                    sz = &line[0];
672
0
                    AI_NFF_PARSE_TRIPLE(v);
673
0
                    out->vertices[out->vertices.size() - n - 1] = v;
674
675
0
                    if (out != currentMesh) {
676
0
                        AI_NFF_PARSE_TRIPLE(v);
677
0
                        out->normals[out->vertices.size() - n - 1] = v;
678
0
                    }
679
0
                    if (out == currentMeshWithUVCoords) {
680
                        // FIX: in one test file this wraps over multiple lines
681
0
                        SkipSpaces(&sz, lineEnd);
682
0
                        if (IsLineEnd(*sz)) {
683
0
                            GetNextLine(buffer, line);
684
0
                            sz = line;
685
0
                        }
686
0
                        AI_NFF_PARSE_FLOAT(v.x);
687
0
                        SkipSpaces(&sz, lineEnd);
688
0
                        if (IsLineEnd(*sz)) {
689
0
                            GetNextLine(buffer, line);
690
0
                            sz = line;
691
0
                        }
692
0
                        AI_NFF_PARSE_FLOAT(v.y);
693
0
                        v.y = 1.f - v.y;
694
0
                        out->uvs[out->vertices.size() - n - 1] = v;
695
0
                    }
696
0
                }
697
0
                out->faces.push_back(m);
698
0
            }
699
            // 'f' - shading information block
700
0
            else if (TokenMatch(sz, "f", 1)) {
701
0
                ai_real d;
702
703
                // read the RGB colors
704
0
                AI_NFF_PARSE_TRIPLE(s.color);
705
706
                // read the other properties
707
0
                AI_NFF_PARSE_FLOAT(s.diffuse.r);
708
0
                AI_NFF_PARSE_FLOAT(s.specular.r);
709
0
                AI_NFF_PARSE_FLOAT(d); // skip shininess and transmittance
710
0
                AI_NFF_PARSE_FLOAT(d);
711
0
                AI_NFF_PARSE_FLOAT(s.refracti);
712
713
                // NFF2 uses full colors here so we need to use them too
714
                // although NFF uses simple scaling factors
715
0
                s.diffuse.g = s.diffuse.b = s.diffuse.r;
716
0
                s.specular.g = s.specular.b = s.specular.r;
717
718
                // if the next one is NOT a number we assume it is a texture file name
719
                // this feature is used by some NFF files on the internet and it has
720
                // been implemented as it can be really useful
721
0
                SkipSpaces(&sz, lineEnd);
722
0
                if (!IsNumeric(*sz)) {
723
                    // TODO: Support full file names with spaces and quotation marks ...
724
0
                    const char *p = sz;
725
0
                    while (!IsSpaceOrNewLine(*sz))
726
0
                        ++sz;
727
728
0
                    unsigned int diff = (unsigned int)(sz - p);
729
0
                    if (diff) {
730
0
                        s.texFile = std::string(p, diff);
731
0
                    }
732
0
                } else {
733
0
                    AI_NFF_PARSE_FLOAT(s.ambient); // optional
734
0
                }
735
0
            } else if (TokenMatch(sz, "shader", 6)) { // 'shader' - other way to specify a texture
736
0
                SkipSpaces(&sz, lineEnd);
737
0
                const char *old = sz;
738
0
                while (!IsSpaceOrNewLine(*sz))
739
0
                    ++sz;
740
0
                s.texFile = std::string(old, (uintptr_t)sz - (uintptr_t)old);
741
0
            }
742
            // 'l' - light source
743
0
            else if (TokenMatch(sz, "l", 1)) {
744
0
                lights.emplace_back();
745
0
                Light &light = lights.back();
746
747
0
                AI_NFF_PARSE_TRIPLE(light.position);
748
0
                AI_NFF_PARSE_FLOAT(light.intensity);
749
0
                AI_NFF_PARSE_TRIPLE(light.color);
750
0
            }
751
            // 's' - sphere
752
0
            else if (TokenMatch(sz, "s", 1)) {
753
0
                meshesLocked.emplace_back(PatchType_Simple, true);
754
0
                MeshInfo &curMesh = meshesLocked.back();
755
0
                curMesh.shader = s;
756
0
                curMesh.shader.mapping = aiTextureMapping_SPHERE;
757
758
0
                AI_NFF_PARSE_SHAPE_INFORMATION();
759
760
                // we don't need scaling or translation here - we do it in the node's transform
761
0
                StandardShapes::MakeSphere(iTesselation, curMesh.vertices);
762
0
                curMesh.faces.resize(curMesh.vertices.size() / 3, 3);
763
764
                // generate a name for the mesh
765
0
                ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "sphere_%i", sphere++);
766
0
            }
767
            // 'dod' - dodecahedron
768
0
            else if (TokenMatch(sz, "dod", 3)) {
769
0
                meshesLocked.emplace_back(PatchType_Simple, true);
770
0
                MeshInfo &curMesh = meshesLocked.back();
771
0
                curMesh.shader = s;
772
0
                curMesh.shader.mapping = aiTextureMapping_SPHERE;
773
774
0
                AI_NFF_PARSE_SHAPE_INFORMATION();
775
776
                // we don't need scaling or translation here - we do it in the node's transform
777
0
                StandardShapes::MakeDodecahedron(curMesh.vertices);
778
0
                curMesh.faces.resize(curMesh.vertices.size() / 3, 3);
779
780
                // generate a name for the mesh
781
0
                ::ai_snprintf(curMesh.name, 128, "dodecahedron_%i", dodecahedron++);
782
0
            }
783
784
            // 'oct' - octahedron
785
0
            else if (TokenMatch(sz, "oct", 3)) {
786
0
                meshesLocked.emplace_back(PatchType_Simple, true);
787
0
                MeshInfo &curMesh = meshesLocked.back();
788
0
                curMesh.shader = s;
789
0
                curMesh.shader.mapping = aiTextureMapping_SPHERE;
790
791
0
                AI_NFF_PARSE_SHAPE_INFORMATION();
792
793
                // we don't need scaling or translation here - we do it in the node's transform
794
0
                StandardShapes::MakeOctahedron(curMesh.vertices);
795
0
                curMesh.faces.resize(curMesh.vertices.size() / 3, 3);
796
797
                // generate a name for the mesh
798
0
                ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "octahedron_%i", octahedron++);
799
0
            }
800
801
            // 'tet' - tetrahedron
802
0
            else if (TokenMatch(sz, "tet", 3)) {
803
0
                meshesLocked.emplace_back(PatchType_Simple, true);
804
0
                MeshInfo &curMesh = meshesLocked.back();
805
0
                curMesh.shader = s;
806
0
                curMesh.shader.mapping = aiTextureMapping_SPHERE;
807
808
0
                AI_NFF_PARSE_SHAPE_INFORMATION();
809
810
                // we don't need scaling or translation here - we do it in the node's transform
811
0
                StandardShapes::MakeTetrahedron(curMesh.vertices);
812
0
                curMesh.faces.resize(curMesh.vertices.size() / 3, 3);
813
814
                // generate a name for the mesh
815
0
                ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "tetrahedron_%i", tetrahedron++);
816
0
            }
817
818
            // 'hex' - hexahedron
819
0
            else if (TokenMatch(sz, "hex", 3)) {
820
0
                meshesLocked.emplace_back(PatchType_Simple, true);
821
0
                MeshInfo &curMesh = meshesLocked.back();
822
0
                curMesh.shader = s;
823
0
                curMesh.shader.mapping = aiTextureMapping_BOX;
824
825
0
                AI_NFF_PARSE_SHAPE_INFORMATION();
826
827
                // we don't need scaling or translation here - we do it in the node's transform
828
0
                StandardShapes::MakeHexahedron(curMesh.vertices);
829
0
                curMesh.faces.resize(curMesh.vertices.size() / 3, 3);
830
831
                // generate a name for the mesh
832
0
                ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "hexahedron_%i", hexahedron++);
833
0
            }
834
            // 'c' - cone
835
0
            else if (TokenMatch(sz, "c", 1)) {
836
0
                meshesLocked.emplace_back(PatchType_Simple, true);
837
0
                MeshInfo &curMesh = meshesLocked.back();
838
0
                curMesh.shader = s;
839
0
                curMesh.shader.mapping = aiTextureMapping_CYLINDER;
840
841
0
                if (!GetNextLine(buffer, line)) {
842
0
                    ASSIMP_LOG_ERROR("NFF: Unexpected end of file (cone definition not complete)");
843
0
                    break;
844
0
                }
845
0
                sz = line;
846
847
                // read the two center points and the respective radii
848
0
                aiVector3D center1, center2;
849
0
                ai_real radius1 = 0.f, radius2 = 0.f;
850
0
                AI_NFF_PARSE_TRIPLE(center1);
851
0
                AI_NFF_PARSE_FLOAT(radius1);
852
853
0
                if (!GetNextLine(buffer, line)) {
854
0
                    ASSIMP_LOG_ERROR("NFF: Unexpected end of file (cone definition not complete)");
855
0
                    break;
856
0
                }
857
0
                sz = line;
858
859
0
                AI_NFF_PARSE_TRIPLE(center2);
860
0
                AI_NFF_PARSE_FLOAT(radius2);
861
862
                // compute the center point of the cone/cylinder -
863
                // it is its local transformation origin
864
0
                curMesh.dir = center2 - center1;
865
0
                curMesh.center = center1 + curMesh.dir / (ai_real)2.0;
866
867
0
                ai_real f;
868
0
                if ((f = curMesh.dir.Length()) < 10e-3f) {
869
0
                    ASSIMP_LOG_ERROR("NFF: Cone height is close to zero");
870
0
                    continue;
871
0
                }
872
0
                curMesh.dir /= f; // normalize
873
874
                // generate the cone - it consists of simple triangles
875
0
                StandardShapes::MakeCone(f, radius1, radius2,
876
0
                        integer_pow(4, iTesselation), curMesh.vertices);
877
878
                // MakeCone() returns tris
879
0
                curMesh.faces.resize(curMesh.vertices.size() / 3, 3);
880
881
                // generate a name for the mesh. 'cone' if it a cone,
882
                // 'cylinder' if it is a cylinder. Funny, isn't it?
883
0
                if (radius1 != radius2) {
884
0
                    ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "cone_%i", cone++);
885
0
                } else {
886
0
                    ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "cylinder_%i", cylinder++);
887
0
                }
888
0
            }
889
            // 'tess' - tessellation
890
0
            else if (TokenMatch(sz, "tess", 4)) {
891
0
                SkipSpaces(&sz, lineEnd);
892
0
                iTesselation = strtoul10(sz);
893
0
            }
894
            // 'from' - camera position
895
0
            else if (TokenMatch(sz, "from", 4)) {
896
0
                AI_NFF_PARSE_TRIPLE(camPos);
897
0
                hasCam = true;
898
0
            }
899
            // 'at' - camera look-at vector
900
0
            else if (TokenMatch(sz, "at", 2)) {
901
0
                AI_NFF_PARSE_TRIPLE(camLookAt);
902
0
                hasCam = true;
903
0
            }
904
            // 'up' - camera up vector
905
0
            else if (TokenMatch(sz, "up", 2)) {
906
0
                AI_NFF_PARSE_TRIPLE(camUp);
907
0
                hasCam = true;
908
0
            }
909
            // 'angle' - (half?) camera field of view
910
0
            else if (TokenMatch(sz, "angle", 5)) {
911
0
                AI_NFF_PARSE_FLOAT(angle);
912
0
                hasCam = true;
913
0
            }
914
            // 'resolution' - used to compute the screen aspect
915
0
            else if (TokenMatch(sz, "resolution", 10)) {
916
0
                AI_NFF_PARSE_FLOAT(resolution.x);
917
0
                AI_NFF_PARSE_FLOAT(resolution.y);
918
0
                hasCam = true;
919
0
            }
920
            // 'pb' - bezier patch. Not supported yet
921
0
            else if (TokenMatch(sz, "pb", 2)) {
922
0
                ASSIMP_LOG_ERROR("NFF: Encountered unsupported ID: bezier patch");
923
0
            }
924
            // 'pn' - NURBS. Not supported yet
925
0
            else if (TokenMatch(sz, "pn", 2) || TokenMatch(sz, "pnn", 3)) {
926
0
                ASSIMP_LOG_ERROR("NFF: Encountered unsupported ID: NURBS");
927
0
            }
928
            // '' - comment
929
0
            else if ('#' == line[0]) {
930
0
                const char *space;
931
0
                SkipSpaces(&line[1], &space, lineEnd);
932
0
                if (!IsLineEnd(*space)) {
933
0
                    ASSIMP_LOG_INFO(space);
934
0
                }
935
0
            }
936
0
        }
937
0
    }
938
939
    // copy all arrays into one large
940
0
    meshes.reserve(meshes.size() + meshesLocked.size() + meshesWithNormals.size() + meshesWithUVCoords.size());
941
0
    meshes.insert(meshes.end(), meshesLocked.begin(), meshesLocked.end());
942
0
    meshes.insert(meshes.end(), meshesWithNormals.begin(), meshesWithNormals.end());
943
0
    meshes.insert(meshes.end(), meshesWithUVCoords.begin(), meshesWithUVCoords.end());
944
945
    // now generate output meshes. first find out how many meshes we'll need
946
0
    std::vector<MeshInfo>::const_iterator it = meshes.begin(), end = meshes.end();
947
0
    for (; it != end; ++it) {
948
0
        if (!(*it).faces.empty()) {
949
0
            ++pScene->mNumMeshes;
950
0
            if ((*it).name[0]) ++numNamed;
951
0
        }
952
0
    }
953
954
    // generate a dummy root node - assign all unnamed elements such
955
    // as polygons and polygon patches to the root node and generate
956
    // sub nodes for named objects such as spheres and cones.
957
0
    aiNode *const root = new aiNode();
958
0
    root->mName.Set("<NFF_Root>");
959
0
    root->mNumChildren = numNamed + (hasCam ? 1 : 0) + (unsigned int)lights.size();
960
0
    root->mNumMeshes = pScene->mNumMeshes - numNamed;
961
962
0
    aiNode **ppcChildren = nullptr;
963
0
    unsigned int *pMeshes = nullptr;
964
0
    if (root->mNumMeshes)
965
0
        pMeshes = root->mMeshes = new unsigned int[root->mNumMeshes];
966
0
    if (root->mNumChildren)
967
0
        ppcChildren = root->mChildren = new aiNode *[root->mNumChildren];
968
969
    // generate the camera
970
0
    if (hasCam) {
971
0
        ai_assert(ppcChildren);
972
0
        aiNode *nd = new aiNode();
973
0
        *ppcChildren = nd;
974
0
        nd->mName.Set("<NFF_Camera>");
975
0
        nd->mParent = root;
976
977
        // allocate the camera in the scene
978
0
        pScene->mNumCameras = 1;
979
0
        pScene->mCameras = new aiCamera *[1];
980
0
        aiCamera *c = pScene->mCameras[0] = new aiCamera;
981
982
0
        c->mName = nd->mName; // make sure the names are identical
983
0
        c->mHorizontalFOV = AI_DEG_TO_RAD(angle);
984
0
        c->mLookAt = camLookAt - camPos;
985
0
        c->mPosition = camPos;
986
0
        c->mUp = camUp;
987
988
        // If the resolution is not specified in the file, we
989
        // need to set 1.0 as aspect.
990
0
        c->mAspect = (!resolution.y ? 0.f : resolution.x / resolution.y);
991
0
        ++ppcChildren;
992
0
    }
993
994
    // generate light sources
995
0
    if (!lights.empty()) {
996
0
        ai_assert(ppcChildren);
997
0
        pScene->mNumLights = (unsigned int)lights.size();
998
0
        pScene->mLights = new aiLight *[pScene->mNumLights];
999
0
        for (unsigned int i = 0; i < pScene->mNumLights; ++i, ++ppcChildren) {
1000
0
            const Light &l = lights[i];
1001
1002
0
            aiNode *nd = new aiNode();
1003
0
            *ppcChildren = nd;
1004
0
            nd->mParent = root;
1005
1006
0
            nd->mName.length = ::ai_snprintf(nd->mName.data, 1024, "<NFF_Light%u>", i);
1007
1008
            // allocate the light in the scene data structure
1009
0
            aiLight *out = pScene->mLights[i] = new aiLight();
1010
0
            out->mName = nd->mName; // make sure the names are identical
1011
0
            out->mType = aiLightSource_POINT;
1012
0
            out->mColorDiffuse = out->mColorSpecular = l.color * l.intensity;
1013
0
            out->mPosition = l.position;
1014
0
        }
1015
0
    }
1016
1017
0
    if (!pScene->mNumMeshes) throw DeadlyImportError("NFF: No meshes loaded");
1018
0
    pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
1019
0
    pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials = pScene->mNumMeshes];
1020
0
    unsigned int m = 0;
1021
0
    for (it = meshes.begin(); it != end; ++it) {
1022
0
        if ((*it).faces.empty()) continue;
1023
1024
0
        const MeshInfo &src = *it;
1025
0
        aiMesh *const mesh = pScene->mMeshes[m] = new aiMesh();
1026
0
        mesh->mNumVertices = (unsigned int)src.vertices.size();
1027
0
        mesh->mNumFaces = (unsigned int)src.faces.size();
1028
1029
        // Generate sub nodes for named meshes
1030
0
        if (src.name[0] && nullptr != ppcChildren) {
1031
0
            aiNode *const node = *ppcChildren = new aiNode();
1032
0
            node->mParent = root;
1033
0
            node->mNumMeshes = 1;
1034
0
            node->mMeshes = new unsigned int[1];
1035
0
            node->mMeshes[0] = m;
1036
0
            node->mName.Set(src.name);
1037
1038
            // setup the transformation matrix of the node
1039
0
            aiMatrix4x4::FromToMatrix(aiVector3D(0.f, 1.f, 0.f),
1040
0
                    src.dir, node->mTransformation);
1041
1042
0
            aiMatrix4x4 &mat = node->mTransformation;
1043
0
            mat.a1 *= src.radius.x;
1044
0
            mat.b1 *= src.radius.x;
1045
0
            mat.c1 *= src.radius.x;
1046
0
            mat.a2 *= src.radius.y;
1047
0
            mat.b2 *= src.radius.y;
1048
0
            mat.c2 *= src.radius.y;
1049
0
            mat.a3 *= src.radius.z;
1050
0
            mat.b3 *= src.radius.z;
1051
0
            mat.c3 *= src.radius.z;
1052
0
            mat.a4 = src.center.x;
1053
0
            mat.b4 = src.center.y;
1054
0
            mat.c4 = src.center.z;
1055
1056
0
            ++ppcChildren;
1057
0
        } else {
1058
0
            *pMeshes++ = m;
1059
0
        }
1060
1061
        // copy vertex positions
1062
0
        mesh->mVertices = new aiVector3D[mesh->mNumVertices];
1063
0
        if (!src.vertices.empty()) {
1064
0
            ::memcpy(mesh->mVertices, &src.vertices[0], sizeof(aiVector3D) * mesh->mNumVertices);
1065
0
        }
1066
1067
        // NFF2: there could be vertex colors
1068
0
        if (!src.colors.empty()) {
1069
0
            ai_assert(src.colors.size() == src.vertices.size());
1070
1071
            // copy vertex colors
1072
0
            mesh->mColors[0] = new aiColor4D[mesh->mNumVertices];
1073
0
            ::memcpy(mesh->mColors[0], &src.colors[0],
1074
0
                    sizeof(aiColor4D) * mesh->mNumVertices);
1075
0
        }
1076
1077
0
        if (!src.normals.empty()) {
1078
0
            ai_assert(src.normals.size() == src.vertices.size());
1079
1080
            // copy normal vectors
1081
0
            mesh->mNormals = new aiVector3D[mesh->mNumVertices];
1082
0
            ::memcpy(mesh->mNormals, &src.normals[0],
1083
0
                    sizeof(aiVector3D) * mesh->mNumVertices);
1084
0
        }
1085
1086
0
        if (!src.uvs.empty()) {
1087
0
            ai_assert(src.uvs.size() == src.vertices.size());
1088
1089
            // copy texture coordinates
1090
0
            mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
1091
0
            ::memcpy(mesh->mTextureCoords[0], &src.uvs[0],
1092
0
                    sizeof(aiVector3D) * mesh->mNumVertices);
1093
0
        }
1094
1095
        // generate faces
1096
0
        unsigned int p = 0;
1097
0
        aiFace *pFace = mesh->mFaces = new aiFace[mesh->mNumFaces];
1098
0
        for (std::vector<unsigned int>::const_iterator it2 = src.faces.begin(),
1099
0
                                                       end2 = src.faces.end();
1100
0
                it2 != end2; ++it2, ++pFace) {
1101
0
            pFace->mIndices = new unsigned int[pFace->mNumIndices = *it2];
1102
0
            for (unsigned int o = 0; o < pFace->mNumIndices; ++o)
1103
0
                pFace->mIndices[o] = p++;
1104
0
        }
1105
1106
        // generate a material for the mesh
1107
0
        aiMaterial *pcMat = (aiMaterial *)(pScene->mMaterials[m] = new aiMaterial());
1108
1109
0
        mesh->mMaterialIndex = m++;
1110
1111
0
        aiString matName;
1112
0
        matName.Set(AI_DEFAULT_MATERIAL_NAME);
1113
0
        pcMat->AddProperty(&matName, AI_MATKEY_NAME);
1114
1115
        // FIX: Ignore diffuse == 0
1116
0
        aiColor3D c = src.shader.color * (src.shader.diffuse.r ? src.shader.diffuse : aiColor3D(1.f, 1.f, 1.f));
1117
0
        pcMat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE);
1118
0
        c = src.shader.color * src.shader.specular;
1119
0
        pcMat->AddProperty(&c, 1, AI_MATKEY_COLOR_SPECULAR);
1120
1121
        // NFF2 - default values for NFF
1122
0
        pcMat->AddProperty(&src.shader.ambient, 1, AI_MATKEY_COLOR_AMBIENT);
1123
0
        pcMat->AddProperty(&src.shader.emissive, 1, AI_MATKEY_COLOR_EMISSIVE);
1124
0
        pcMat->AddProperty(&src.shader.opacity, 1, AI_MATKEY_OPACITY);
1125
1126
        // setup the first texture layer, if existing
1127
0
        if (src.shader.texFile.length()) {
1128
0
            matName.Set(src.shader.texFile);
1129
0
            pcMat->AddProperty(&matName, AI_MATKEY_TEXTURE_DIFFUSE(0));
1130
1131
0
            if (aiTextureMapping_UV != src.shader.mapping) {
1132
1133
0
                aiVector3D v(0.f, -1.f, 0.f);
1134
0
                pcMat->AddProperty(&v, 1, AI_MATKEY_TEXMAP_AXIS_DIFFUSE(0));
1135
0
                pcMat->AddProperty((int *)&src.shader.mapping, 1, AI_MATKEY_MAPPING_DIFFUSE(0));
1136
0
            }
1137
0
        }
1138
1139
        // setup the name of the material
1140
0
        if (src.shader.name.length()) {
1141
0
            matName.Set(src.shader.texFile);
1142
0
            pcMat->AddProperty(&matName, AI_MATKEY_NAME);
1143
0
        }
1144
1145
        // setup some more material properties that are specific to NFF2
1146
0
        int i;
1147
0
        if (src.shader.twoSided) {
1148
0
            i = 1;
1149
0
            pcMat->AddProperty(&i, 1, AI_MATKEY_TWOSIDED);
1150
0
        }
1151
0
        i = (src.shader.shaded ? aiShadingMode_Gouraud : aiShadingMode_NoShading);
1152
0
        if (src.shader.shininess) {
1153
0
            i = aiShadingMode_Phong;
1154
0
            pcMat->AddProperty(&src.shader.shininess, 1, AI_MATKEY_SHININESS);
1155
0
        }
1156
        pcMat->AddProperty(&i, 1, AI_MATKEY_SHADING_MODEL);
1157
0
    }
1158
0
    pScene->mRootNode = root;
1159
0
}
1160
1161
} // namespace Assimp
1162
1163
#endif // !! ASSIMP_BUILD_NO_NFF_IMPORTER