1/*
2---------------------------------------------------------------------------
3Open Asset Import Library (assimp)
4---------------------------------------------------------------------------
5
6Copyright (c) 2006-2017, assimp team
7
8
9All rights reserved.
10
11Redistribution and use of this software in source and binary forms,
12with or without modification, are permitted provided that the following
13conditions are met:
14
15* Redistributions of source code must retain the above
16 copyright notice, this list of conditions and the
17 following disclaimer.
18
19* Redistributions in binary form must reproduce the above
20 copyright notice, this list of conditions and the
21 following disclaimer in the documentation and/or other
22 materials provided with the distribution.
23
24* Neither the name of the assimp team, nor the names of its
25 contributors may be used to endorse or promote products
26 derived from this software without specific prior
27 written permission of the assimp team.
28
29THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40---------------------------------------------------------------------------
41*/
42
43/** @file NDOLoader.cpp
44 * Implementation of the NDO importer class.
45 */
46
47
48#ifndef ASSIMP_BUILD_NO_NDO_IMPORTER
49#include "NDOLoader.h"
50#include <assimp/DefaultLogger.hpp>
51#include <assimp/IOSystem.hpp>
52#include <assimp/scene.h>
53#include <assimp/importerdesc.h>
54#include "StreamReader.h"
55#include <map>
56
57using namespace Assimp;
58
59static const aiImporterDesc desc = {
60 "Nendo Mesh Importer",
61 "",
62 "",
63 "http://www.izware.com/nendo/index.htm",
64 aiImporterFlags_SupportBinaryFlavour,
65 0,
66 0,
67 0,
68 0,
69 "ndo"
70};
71
72// ------------------------------------------------------------------------------------------------
73// Constructor to be privately used by Importer
74NDOImporter::NDOImporter()
75{}
76
77// ------------------------------------------------------------------------------------------------
78// Destructor, private as well
79NDOImporter::~NDOImporter()
80{}
81
82// ------------------------------------------------------------------------------------------------
83// Returns whether the class can handle the format of the given file.
84bool NDOImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
85{
86 // check file extension
87 const std::string extension = GetExtension(pFile);
88
89 if( extension == "ndo")
90 return true;
91
92 if ((checkSig || !extension.length()) && pIOHandler) {
93 const char* tokens[] = {"nendo"};
94 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1,5);
95 }
96 return false;
97}
98
99// ------------------------------------------------------------------------------------------------
100// Build a string of all file extensions supported
101const aiImporterDesc* NDOImporter::GetInfo () const
102{
103 return &desc;
104}
105
106// ------------------------------------------------------------------------------------------------
107// Setup configuration properties for the loader
108void NDOImporter::SetupProperties(const Importer* /*pImp*/)
109{
110 // nothing to be done for the moment
111}
112
113// ------------------------------------------------------------------------------------------------
114// Imports the given file into the given scene structure.
115void NDOImporter::InternReadFile( const std::string& pFile,
116 aiScene* pScene, IOSystem* pIOHandler)
117{
118 StreamReaderBE reader(pIOHandler->Open( pFile, "rb"));
119
120 // first 9 bytes are nendo file format ("nendo 1.n")
121 const char* head = (const char*)reader.GetPtr();
122 reader.IncPtr(9);
123
124 if (strncmp("nendo ",head,6)) {
125 throw DeadlyImportError("Not a Nendo file; magic signature missing");
126 }
127 // check if this is a supported version. if not, continue, too -- users,
128 // please don't complain if it doesn't work then ...
129 unsigned int file_format = 12;
130 if (!strncmp("1.0",head+6,3)) {
131 file_format = 10;
132 DefaultLogger::get()->info("NDO file format is 1.0");
133 }
134 else if (!strncmp("1.1",head+6,3)) {
135 file_format = 11;
136 DefaultLogger::get()->info("NDO file format is 1.1");
137 }
138 else if (!strncmp("1.2",head+6,3)) {
139 file_format = 12;
140 DefaultLogger::get()->info("NDO file format is 1.2");
141 }
142 else {
143 DefaultLogger::get()->warn(std::string("Unrecognized nendo file format version, continuing happily ... :") + (head+6));
144 }
145
146 reader.IncPtr(2); /* skip flags */
147 if (file_format >= 12) {
148 reader.IncPtr(2);
149 }
150 unsigned int temp = reader.GetU1();
151
152 std::vector<Object> objects(temp); /* buffer to store all the loaded objects in */
153
154 // read all objects
155 for (unsigned int o = 0; o < objects.size(); ++o) {
156
157// if (file_format < 12) {
158 if (!reader.GetI1()) {
159 continue; /* skip over empty object */
160 }
161 // reader.GetI2();
162// }
163 Object& obj = objects[o];
164
165 temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
166 head = (const char*)reader.GetPtr();
167 reader.IncPtr(temp + 76); /* skip unknown stuff */
168
169 obj.name = std::string(head, temp);
170
171 // read edge table
172 temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
173 obj.edges.reserve(temp);
174 for (unsigned int e = 0; e < temp; ++e) {
175
176 obj.edges.push_back(Edge());
177 Edge& edge = obj.edges.back();
178
179 for (unsigned int i = 0; i< 8; ++i) {
180 edge.edge[i] = file_format >= 12 ? reader.GetU4() : reader.GetU2();
181 }
182 edge.hard = file_format >= 11 ? reader.GetU1() : 0;
183 for (unsigned int i = 0; i< 8; ++i) {
184 edge.color[i] = reader.GetU1();
185 }
186 }
187
188 // read face table
189 temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
190 obj.faces.reserve(temp);
191 for (unsigned int e = 0; e < temp; ++e) {
192
193 obj.faces.push_back(Face());
194 Face& face = obj.faces.back();
195
196 face.elem = file_format >= 12 ? reader.GetU4() : reader.GetU2();
197 }
198
199 // read vertex table
200 temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
201 obj.vertices.reserve(temp);
202 for (unsigned int e = 0; e < temp; ++e) {
203
204 obj.vertices.push_back(Vertex());
205 Vertex& v = obj.vertices.back();
206
207 v.num = file_format >= 12 ? reader.GetU4() : reader.GetU2();
208 v.val.x = reader.GetF4();
209 v.val.y = reader.GetF4();
210 v.val.z = reader.GetF4();
211 }
212
213 // read UVs
214 temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
215 for (unsigned int e = 0; e < temp; ++e) {
216 file_format >= 12 ? reader.GetU4() : reader.GetU2();
217 }
218
219 temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
220 for (unsigned int e = 0; e < temp; ++e) {
221 file_format >= 12 ? reader.GetU4() : reader.GetU2();
222 }
223
224 if (reader.GetU1()) {
225 const unsigned int x = reader.GetU2(), y = reader.GetU2();
226 temp = 0;
227 while (temp < x*y) {
228 unsigned int repeat = reader.GetU1();
229 reader.GetU1();
230 reader.GetU1();
231 reader.GetU1();
232 temp += repeat;
233 }
234 }
235 }
236
237 // construct a dummy node graph and add all named objects as child nodes
238 aiNode* root = pScene->mRootNode = new aiNode("$NDODummyRoot");
239 aiNode** cc = root->mChildren = new aiNode* [ root->mNumChildren = static_cast<unsigned int>( objects.size()) ] ();
240 pScene->mMeshes = new aiMesh* [ root->mNumChildren] ();
241
242 std::vector<aiVector3D> vertices;
243 std::vector<unsigned int> indices;
244
245 for(const Object& obj : objects) {
246 aiNode* nd = *cc++ = new aiNode(obj.name);
247 nd->mParent = root;
248
249 // translated from a python dict() - a vector might be sufficient as well
250 typedef std::map<unsigned int, unsigned int> FaceTable;
251 FaceTable face_table;
252
253 unsigned int n = 0;
254 for(const Edge& edge : obj.edges) {
255
256 face_table[edge.edge[2]] = n;
257 face_table[edge.edge[3]] = n;
258
259 ++n;
260 }
261
262 aiMesh* mesh = new aiMesh();
263 mesh->mNumFaces=static_cast<unsigned int>(face_table.size());
264 aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces];
265
266 vertices.clear();
267 vertices.reserve(4 * face_table.size()); // arbitrarily chosen
268 for(FaceTable::value_type& v : face_table) {
269 indices.clear();
270
271 aiFace& f = *faces++;
272
273 const unsigned int key = v.first;
274 unsigned int cur_edge = v.second;
275 while (1) {
276 unsigned int next_edge, next_vert;
277 if (key == obj.edges[cur_edge].edge[3]) {
278 next_edge = obj.edges[cur_edge].edge[5];
279 next_vert = obj.edges[cur_edge].edge[1];
280 }
281 else {
282 next_edge = obj.edges[cur_edge].edge[4];
283 next_vert = obj.edges[cur_edge].edge[0];
284 }
285 indices.push_back( static_cast<unsigned int>(vertices.size()) );
286 vertices.push_back(obj.vertices[ next_vert ].val);
287
288 cur_edge = next_edge;
289 if (cur_edge == v.second) {
290 break;
291 }
292 }
293
294 f.mIndices = new unsigned int[f.mNumIndices = static_cast<unsigned int>(indices.size())];
295 std::copy(indices.begin(),indices.end(),f.mIndices);
296 }
297
298 mesh->mVertices = new aiVector3D[mesh->mNumVertices = static_cast<unsigned int>(vertices.size())];
299 std::copy(vertices.begin(),vertices.end(),mesh->mVertices);
300
301 if (mesh->mNumVertices) {
302 pScene->mMeshes[pScene->mNumMeshes] = mesh;
303
304 (nd->mMeshes = new unsigned int[nd->mNumMeshes=1])[0]=pScene->mNumMeshes++;
305 }else
306 delete mesh;
307 }
308}
309
310#endif
311