/src/assimp/code/AssetLib/Unreal/UnrealLoader.cpp
Line | Count | Source (jump to first uncovered line) |
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 UnrealLoader.cpp |
43 | | * @brief Implementation of the UNREAL (*.3D) importer class |
44 | | * |
45 | | * Sources: |
46 | | * http://local.wasp.uwa.edu.au/~pbourke/dataformats/unreal/ |
47 | | */ |
48 | | |
49 | | #ifndef ASSIMP_BUILD_NO_3D_IMPORTER |
50 | | |
51 | | #include "AssetLib/Unreal/UnrealLoader.h" |
52 | | #include "PostProcessing/ConvertToLHProcess.h" |
53 | | |
54 | | #include <assimp/ParsingUtils.h> |
55 | | #include <assimp/StreamReader.h> |
56 | | #include <assimp/fast_atof.h> |
57 | | #include <assimp/importerdesc.h> |
58 | | #include <assimp/scene.h> |
59 | | #include <assimp/DefaultLogger.hpp> |
60 | | #include <assimp/IOSystem.hpp> |
61 | | #include <assimp/Importer.hpp> |
62 | | |
63 | | #include <cstdint> |
64 | | #include <memory> |
65 | | |
66 | | namespace Assimp { |
67 | | |
68 | | namespace Unreal { |
69 | | |
70 | | // Mesh-specific fags. |
71 | | enum MeshFlags { |
72 | | MF_INVALID = -1, // Not set |
73 | | MF_NORMAL_OS = 0, // Normal one-sided |
74 | | MF_NORMAL_TS = 1, // Normal two-sided |
75 | | MF_NORMAL_TRANS_TS = 2, // Translucent two-sided |
76 | | MF_NORMAL_MASKED_TS = 3, // Masked two-sided |
77 | | MF_NORMAL_MOD_TS = 4, // Modulation blended two-sided |
78 | | MF_WEAPON_PLACEHOLDER = 8 // Placeholder triangle for weapon positioning (invisible) |
79 | | }; |
80 | | |
81 | | // a single triangle |
82 | | struct Triangle { |
83 | | uint16_t mVertex[3]; // Vertex indices |
84 | | char mType; // James' Mesh Type |
85 | | char mColor; // Color for flat and Gourand Shaded |
86 | | unsigned char mTex[3][2]; // Texture UV coordinates |
87 | | unsigned char mTextureNum; // Source texture offset |
88 | | char mFlags; // Unreal Mesh Flags (unused) |
89 | | unsigned int matIndex; // Material index |
90 | | }; |
91 | | |
92 | | // temporary representation for a material |
93 | | struct TempMat { |
94 | | TempMat() : |
95 | 0 | type(MF_NORMAL_OS), tex(), numFaces(0) {} |
96 | | |
97 | | explicit TempMat(const Triangle &in) : |
98 | 0 | type((Unreal::MeshFlags)in.mType), tex(in.mTextureNum), numFaces(0) {} |
99 | | |
100 | | // type of mesh |
101 | | Unreal::MeshFlags type; |
102 | | |
103 | | // index of texture |
104 | | unsigned int tex; |
105 | | |
106 | | // number of faces using us |
107 | | unsigned int numFaces; |
108 | | |
109 | | // for std::find |
110 | 0 | bool operator==(const TempMat &o) { |
111 | 0 | return (tex == o.tex && type == o.type); |
112 | 0 | } |
113 | | }; |
114 | | |
115 | | // A single vertex in an unsigned int 32 bit |
116 | | struct Vertex { |
117 | | int32_t X : 11; |
118 | | int32_t Y : 11; |
119 | | int32_t Z : 10; |
120 | | }; |
121 | | |
122 | | // UNREAL vertex compression |
123 | 0 | inline void CompressVertex(const aiVector3D &v, uint32_t &out) { |
124 | 0 | union { |
125 | 0 | Vertex n; |
126 | 0 | int32_t t; |
127 | 0 | }; |
128 | 0 | t = 0; |
129 | 0 | n.X = (int32_t)v.x; |
130 | 0 | n.Y = (int32_t)v.y; |
131 | 0 | n.Z = (int32_t)v.z; |
132 | 0 | ::memcpy(&out, &t, sizeof(int32_t)); |
133 | 0 | } |
134 | | |
135 | | // UNREAL vertex decompression |
136 | 0 | inline void DecompressVertex(aiVector3D &v, int32_t in) { |
137 | 0 | union { |
138 | 0 | Vertex n; |
139 | 0 | int32_t i; |
140 | 0 | }; |
141 | 0 | i = in; |
142 | |
|
143 | 0 | v.x = (float)n.X; |
144 | 0 | v.y = (float)n.Y; |
145 | 0 | v.z = (float)n.Z; |
146 | 0 | } |
147 | | |
148 | | } // end namespace Unreal |
149 | | |
150 | | static constexpr aiImporterDesc desc = { |
151 | | "Unreal Mesh Importer", |
152 | | "", |
153 | | "", |
154 | | "", |
155 | | aiImporterFlags_SupportTextFlavour, |
156 | | 0, |
157 | | 0, |
158 | | 0, |
159 | | 0, |
160 | | "3d uc" |
161 | | }; |
162 | | |
163 | | // ------------------------------------------------------------------------------------------------ |
164 | | // Constructor to be privately used by Importer |
165 | | UnrealImporter::UnrealImporter() : |
166 | 1.50k | mConfigFrameID(0), mConfigHandleFlags(true) { |
167 | | // empty |
168 | 1.50k | } |
169 | | |
170 | | // ------------------------------------------------------------------------------------------------ |
171 | | // Returns whether the class can handle the format of the given file. |
172 | 758 | bool UnrealImporter::CanRead(const std::string &filename, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const { |
173 | 758 | return SimpleExtensionCheck(filename, "3d", "uc"); |
174 | 758 | } |
175 | | |
176 | | // ------------------------------------------------------------------------------------------------ |
177 | | // Build a string of all file extensions supported |
178 | 2.38k | const aiImporterDesc *UnrealImporter::GetInfo() const { |
179 | 2.38k | return &desc; |
180 | 2.38k | } |
181 | | |
182 | | // ------------------------------------------------------------------------------------------------ |
183 | | // Setup configuration properties for the loader |
184 | 29 | void UnrealImporter::SetupProperties(const Importer *pImp) { |
185 | | // The |
186 | | // AI_CONFIG_IMPORT_UNREAL_KEYFRAME option overrides the |
187 | | // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. |
188 | 29 | mConfigFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_UNREAL_KEYFRAME, -1); |
189 | 29 | if (static_cast<unsigned int>(-1) == mConfigFrameID) { |
190 | 29 | mConfigFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME, 0); |
191 | 29 | } |
192 | | |
193 | | // AI_CONFIG_IMPORT_UNREAL_HANDLE_FLAGS, default is true |
194 | 29 | mConfigHandleFlags = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_UNREAL_HANDLE_FLAGS, 1)); |
195 | 29 | } |
196 | | |
197 | | // ------------------------------------------------------------------------------------------------ |
198 | | // Imports the given file into the given scene structure. |
199 | | void UnrealImporter::InternReadFile(const std::string &pFile, |
200 | 29 | aiScene *pScene, IOSystem *pIOHandler) { |
201 | | // For any of the 3 files being passed get the three correct paths |
202 | | // First of all, determine file extension |
203 | 29 | std::string::size_type pos = pFile.find_last_of('.'); |
204 | 29 | std::string extension = GetExtension(pFile); |
205 | | |
206 | 29 | std::string d_path, a_path, uc_path; |
207 | 29 | if (extension == "3d") { |
208 | | // jjjj_d.3d |
209 | | // jjjj_a.3d |
210 | 26 | pos = pFile.find_last_of('_'); |
211 | 26 | if (std::string::npos == pos) { |
212 | 0 | throw DeadlyImportError("UNREAL: Unexpected naming scheme"); |
213 | 0 | } |
214 | 26 | extension = pFile.substr(0, pos); |
215 | 26 | } else { |
216 | 3 | extension = pFile.substr(0, pos); |
217 | 3 | } |
218 | | |
219 | | // build proper paths |
220 | 29 | d_path = extension + "_d.3d"; |
221 | 29 | a_path = extension + "_a.3d"; |
222 | 29 | uc_path = extension + ".uc"; |
223 | | |
224 | 29 | ASSIMP_LOG_DEBUG("UNREAL: data file is ", d_path); |
225 | 29 | ASSIMP_LOG_DEBUG("UNREAL: aniv file is ", a_path); |
226 | 29 | ASSIMP_LOG_DEBUG("UNREAL: uc file is ", uc_path); |
227 | | |
228 | | // and open the files ... we can't live without them |
229 | 29 | std::unique_ptr<IOStream> p(pIOHandler->Open(d_path)); |
230 | 29 | if (!p) |
231 | 1 | throw DeadlyImportError("UNREAL: Unable to open _d file"); |
232 | 28 | StreamReaderLE d_reader(pIOHandler->Open(d_path)); |
233 | | |
234 | 28 | const uint16_t numTris = d_reader.GetI2(); |
235 | 28 | const uint16_t numVert = d_reader.GetI2(); |
236 | 28 | d_reader.IncPtr(44); |
237 | 28 | if (!numTris || numVert < 3) { |
238 | 0 | throw DeadlyImportError("UNREAL: Invalid number of vertices/triangles"); |
239 | 0 | } |
240 | | |
241 | | // maximum texture index |
242 | 28 | unsigned int maxTexIdx = 0; |
243 | | |
244 | | // collect triangles |
245 | 28 | std::vector<Unreal::Triangle> triangles(numTris); |
246 | 49.0k | for (auto &tri : triangles) { |
247 | 196k | for (unsigned int i = 0; i < 3; ++i) { |
248 | 147k | tri.mVertex[i] = d_reader.GetI2(); |
249 | 147k | if (tri.mVertex[i] >= numTris) { |
250 | 90.0k | ASSIMP_LOG_WARN("UNREAL: vertex index out of range"); |
251 | 90.0k | tri.mVertex[i] = 0; |
252 | 90.0k | } |
253 | 147k | } |
254 | 49.0k | tri.mType = d_reader.GetI1(); |
255 | | |
256 | | // handle mesh flagss? |
257 | 49.0k | if (mConfigHandleFlags) { |
258 | 49.0k | tri.mType = Unreal::MF_NORMAL_OS; |
259 | 49.0k | } else { |
260 | | // ignore MOD and MASKED for the moment, treat them as two-sided |
261 | 12 | if (tri.mType == Unreal::MF_NORMAL_MOD_TS || tri.mType == Unreal::MF_NORMAL_MASKED_TS) |
262 | 0 | tri.mType = Unreal::MF_NORMAL_TS; |
263 | 12 | } |
264 | 49.0k | d_reader.IncPtr(1); |
265 | | |
266 | 196k | for (unsigned int i = 0; i < 3; ++i) { |
267 | 441k | for (unsigned int i2 = 0; i2 < 2; ++i2) { |
268 | 294k | tri.mTex[i][i2] = d_reader.GetI1(); |
269 | 294k | } |
270 | 147k | } |
271 | | |
272 | 49.0k | tri.mTextureNum = d_reader.GetI1(); |
273 | 49.0k | maxTexIdx = std::max(maxTexIdx, (unsigned int)tri.mTextureNum); |
274 | 49.0k | d_reader.IncPtr(1); |
275 | 49.0k | } |
276 | | |
277 | 28 | p.reset(pIOHandler->Open(a_path)); |
278 | 28 | if (!p) { |
279 | 0 | throw DeadlyImportError("UNREAL: Unable to open _a file"); |
280 | 0 | } |
281 | 28 | StreamReaderLE a_reader(pIOHandler->Open(a_path)); |
282 | | |
283 | | // read number of frames |
284 | 28 | const uint32_t numFrames = a_reader.GetI2(); |
285 | 28 | if (mConfigFrameID >= numFrames) { |
286 | 0 | throw DeadlyImportError("UNREAL: The requested frame does not exist"); |
287 | 0 | } |
288 | | |
289 | | // read aniv file length |
290 | 28 | if (uint32_t st = a_reader.GetI2(); st != numVert * 4u) { |
291 | 0 | throw DeadlyImportError("UNREAL: Unexpected aniv file length"); |
292 | 0 | } |
293 | | |
294 | | // skip to our frame |
295 | 28 | a_reader.IncPtr(mConfigFrameID * numVert * 4); |
296 | | |
297 | | // collect vertices |
298 | 28 | std::vector<aiVector3D> vertices(numVert); |
299 | 28 | for (auto &vertex : vertices) { |
300 | 0 | int32_t val = a_reader.GetI4(); |
301 | 0 | Unreal::DecompressVertex(vertex, val); |
302 | 0 | } |
303 | | |
304 | | // list of textures. |
305 | 28 | std::vector<std::pair<unsigned int, std::string>> textures; |
306 | | |
307 | | // allocate the output scene |
308 | 28 | aiNode *nd = pScene->mRootNode = new aiNode(); |
309 | 28 | nd->mName.Set("<UnrealRoot>"); |
310 | | |
311 | | // we can live without the uc file if necessary |
312 | 28 | std::unique_ptr<IOStream> pb(pIOHandler->Open(uc_path)); |
313 | 28 | if (pb) { |
314 | |
|
315 | 0 | std::vector<char> _data; |
316 | 0 | TextFileToBuffer(pb.get(), _data); |
317 | 0 | const char *data = &_data[0]; |
318 | 0 | const char *end = &_data[_data.size() - 1] + 1; |
319 | |
|
320 | 0 | std::vector<std::pair<std::string, std::string>> tempTextures; |
321 | | |
322 | | // do a quick search in the UC file for some known, usually texture-related, tags |
323 | 0 | for (; *data; ++data) { |
324 | 0 | if (TokenMatchI(data, "#exec", 5)) { |
325 | 0 | SkipSpacesAndLineEnd(&data, end); |
326 | | |
327 | | // #exec TEXTURE IMPORT [...] NAME=jjjjj [...] FILE=jjjj.pcx [...] |
328 | 0 | if (TokenMatchI(data, "TEXTURE", 7)) { |
329 | 0 | SkipSpacesAndLineEnd(&data, end); |
330 | |
|
331 | 0 | if (TokenMatchI(data, "IMPORT", 6)) { |
332 | 0 | tempTextures.emplace_back(); |
333 | 0 | std::pair<std::string, std::string> &me = tempTextures.back(); |
334 | 0 | for (; !IsLineEnd(*data); ++data) { |
335 | 0 | if (!ASSIMP_strincmp(data, "NAME=", 5)) { |
336 | 0 | const char *d = data += 5; |
337 | 0 | for (; !IsSpaceOrNewLine(*data); ++data) |
338 | 0 | ; |
339 | 0 | me.first = std::string(d, (size_t)(data - d)); |
340 | 0 | } else if (!ASSIMP_strincmp(data, "FILE=", 5)) { |
341 | 0 | const char *d = data += 5; |
342 | 0 | for (; !IsSpaceOrNewLine(*data); ++data) |
343 | 0 | ; |
344 | 0 | me.second = std::string(d, (size_t)(data - d)); |
345 | 0 | } |
346 | 0 | } |
347 | 0 | if (!me.first.length() || !me.second.length()) { |
348 | 0 | tempTextures.pop_back(); |
349 | 0 | } |
350 | 0 | } |
351 | 0 | } |
352 | | // #exec MESHMAP SETTEXTURE MESHMAP=box NUM=1 TEXTURE=Jtex1 |
353 | | // #exec MESHMAP SCALE MESHMAP=box X=0.1 Y=0.1 Z=0.2 |
354 | 0 | else if (TokenMatchI(data, "MESHMAP", 7)) { |
355 | 0 | SkipSpacesAndLineEnd(&data, end); |
356 | |
|
357 | 0 | if (TokenMatchI(data, "SETTEXTURE", 10)) { |
358 | |
|
359 | 0 | textures.emplace_back(); |
360 | 0 | std::pair<unsigned int, std::string> &me = textures.back(); |
361 | |
|
362 | 0 | for (; !IsLineEnd(*data); ++data) { |
363 | 0 | if (!ASSIMP_strincmp(data, "NUM=", 4)) { |
364 | 0 | data += 4; |
365 | 0 | me.first = strtoul10(data, &data); |
366 | 0 | } else if (!ASSIMP_strincmp(data, "TEXTURE=", 8)) { |
367 | 0 | data += 8; |
368 | 0 | const char *d = data; |
369 | 0 | for (; !IsSpaceOrNewLine(*data); ++data); |
370 | 0 | me.second = std::string(d, (size_t)(data - d)); |
371 | | |
372 | | // try to find matching path names, doesn't care if we don't find them |
373 | 0 | for (std::vector<std::pair<std::string, std::string>>::const_iterator it = tempTextures.begin(); |
374 | 0 | it != tempTextures.end(); ++it) { |
375 | 0 | if ((*it).first == me.second) { |
376 | 0 | me.second = (*it).second; |
377 | 0 | break; |
378 | 0 | } |
379 | 0 | } |
380 | 0 | } |
381 | 0 | } |
382 | 0 | } else if (TokenMatchI(data, "SCALE", 5)) { |
383 | |
|
384 | 0 | for (; !IsLineEnd(*data); ++data) { |
385 | 0 | if (data[0] == 'X' && data[1] == '=') { |
386 | 0 | data = fast_atoreal_move<float>(data + 2, (float &)nd->mTransformation.a1); |
387 | 0 | } else if (data[0] == 'Y' && data[1] == '=') { |
388 | 0 | data = fast_atoreal_move<float>(data + 2, (float &)nd->mTransformation.b2); |
389 | 0 | } else if (data[0] == 'Z' && data[1] == '=') { |
390 | 0 | data = fast_atoreal_move<float>(data + 2, (float &)nd->mTransformation.c3); |
391 | 0 | } |
392 | 0 | } |
393 | 0 | } |
394 | 0 | } |
395 | 0 | } |
396 | 0 | } |
397 | 28 | } else { |
398 | 28 | ASSIMP_LOG_ERROR("Unable to open .uc file"); |
399 | 28 | } |
400 | | |
401 | 28 | std::vector<Unreal::TempMat> materials; |
402 | 28 | materials.reserve(textures.size() * 2 + 5); |
403 | | |
404 | | // find out how many output meshes and materials we'll have and build material indices |
405 | 28 | for (auto &tri : triangles) { |
406 | 0 | Unreal::TempMat mat(tri); |
407 | 0 | auto nt = std::find(materials.begin(), materials.end(), mat); |
408 | 0 | if (nt == materials.end()) { |
409 | | // add material |
410 | 0 | tri.matIndex = static_cast<unsigned int>(materials.size()); |
411 | 0 | mat.numFaces = 1; |
412 | 0 | materials.push_back(mat); |
413 | |
|
414 | 0 | ++pScene->mNumMeshes; |
415 | 0 | } else { |
416 | 0 | tri.matIndex = static_cast<unsigned int>(nt - materials.begin()); |
417 | 0 | ++nt->numFaces; |
418 | 0 | } |
419 | 0 | } |
420 | | |
421 | 28 | if (!pScene->mNumMeshes) { |
422 | 0 | throw DeadlyImportError("UNREAL: Unable to find valid mesh data"); |
423 | 0 | } |
424 | | |
425 | | // allocate meshes and bind them to the node graph |
426 | 28 | pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]; |
427 | 28 | pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials = pScene->mNumMeshes]; |
428 | | |
429 | 28 | nd->mNumMeshes = pScene->mNumMeshes; |
430 | 28 | nd->mMeshes = new unsigned int[nd->mNumMeshes]; |
431 | 28 | for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { |
432 | 0 | aiMesh *m = pScene->mMeshes[i] = new aiMesh(); |
433 | 0 | m->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; |
434 | |
|
435 | 0 | const unsigned int num = materials[i].numFaces; |
436 | 0 | m->mFaces = new aiFace[num]; |
437 | 0 | m->mVertices = new aiVector3D[num * 3]; |
438 | 0 | m->mTextureCoords[0] = new aiVector3D[num * 3]; |
439 | |
|
440 | 0 | nd->mMeshes[i] = i; |
441 | | |
442 | | // create materials, too |
443 | 0 | aiMaterial *mat = new aiMaterial(); |
444 | 0 | pScene->mMaterials[i] = mat; |
445 | | |
446 | | // all white by default - texture rulez |
447 | 0 | aiColor3D color(1.f, 1.f, 1.f); |
448 | |
|
449 | 0 | aiString s; |
450 | 0 | ::ai_snprintf(s.data, AI_MAXLEN, "mat%u_tx%u_", i, materials[i].tex); |
451 | | |
452 | | // set the two-sided flag |
453 | 0 | if (materials[i].type == Unreal::MF_NORMAL_TS) { |
454 | 0 | const int twosided = 1; |
455 | 0 | mat->AddProperty(&twosided, 1, AI_MATKEY_TWOSIDED); |
456 | 0 | ::strcat(s.data, "ts_"); |
457 | 0 | } else |
458 | 0 | ::strcat(s.data, "os_"); |
459 | | |
460 | | // make TRANS faces 90% opaque that RemRedundantMaterials won't catch us |
461 | 0 | if (materials[i].type == Unreal::MF_NORMAL_TRANS_TS) { |
462 | 0 | const float opac = 0.9f; |
463 | 0 | mat->AddProperty(&opac, 1, AI_MATKEY_OPACITY); |
464 | 0 | ::strcat(s.data, "tran_"); |
465 | 0 | } else |
466 | 0 | ::strcat(s.data, "opaq_"); |
467 | | |
468 | | // a special name for the weapon attachment point |
469 | 0 | if (materials[i].type == Unreal::MF_WEAPON_PLACEHOLDER) { |
470 | 0 | s.length = ::ai_snprintf(s.data, AI_MAXLEN, "$WeaponTag$"); |
471 | 0 | color = aiColor3D(0.f, 0.f, 0.f); |
472 | 0 | } |
473 | | |
474 | | // set color and name |
475 | 0 | mat->AddProperty(&color, 1, AI_MATKEY_COLOR_DIFFUSE); |
476 | 0 | s.length = static_cast<ai_uint32>(::strlen(s.data)); |
477 | 0 | mat->AddProperty(&s, AI_MATKEY_NAME); |
478 | | |
479 | | // set texture, if any |
480 | 0 | const unsigned int tex = materials[i].tex; |
481 | 0 | for (auto it = textures.begin(); it != textures.end(); ++it) { |
482 | 0 | if ((*it).first == tex) { |
483 | 0 | s.Set((*it).second); |
484 | 0 | mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0)); |
485 | 0 | break; |
486 | 0 | } |
487 | 0 | } |
488 | 0 | } |
489 | | |
490 | | // fill them. |
491 | 28 | for (const Unreal::Triangle &tri : triangles) { |
492 | 0 | Unreal::TempMat mat(tri); |
493 | 0 | auto nt = std::find(materials.begin(), materials.end(), mat); |
494 | |
|
495 | 0 | aiMesh *mesh = pScene->mMeshes[nt - materials.begin()]; |
496 | 0 | aiFace &f = mesh->mFaces[mesh->mNumFaces++]; |
497 | 0 | f.mIndices = new unsigned int[f.mNumIndices = 3]; |
498 | |
|
499 | 0 | for (unsigned int i = 0; i < 3; ++i, mesh->mNumVertices++) { |
500 | 0 | f.mIndices[i] = mesh->mNumVertices; |
501 | |
|
502 | 0 | mesh->mVertices[mesh->mNumVertices] = vertices[tri.mVertex[i]]; |
503 | 0 | mesh->mTextureCoords[0][mesh->mNumVertices] = aiVector3D(tri.mTex[i][0] / 255.f, 1.f - tri.mTex[i][1] / 255.f, 0.f); |
504 | 0 | } |
505 | 0 | } |
506 | | |
507 | | // convert to RH |
508 | 28 | MakeLeftHandedProcess hero; |
509 | 28 | hero.Execute(pScene); |
510 | | |
511 | 28 | FlipWindingOrderProcess flipper; |
512 | 28 | flipper.Execute(pScene); |
513 | 28 | } |
514 | | |
515 | | } // namespace Assimp |
516 | | |
517 | | #endif // !! ASSIMP_BUILD_NO_3D_IMPORTER |