1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2016, 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  OFFLoader.cpp
43  *  @brief Implementation of the OFF importer class
44  */
45 
46 
47 #ifndef ASSIMP_BUILD_NO_OFF_IMPORTER
48 
49 // internal headers
50 #include "OFFLoader.h"
51 #include "ParsingUtils.h"
52 #include "fast_atof.h"
53 #include <memory>
54 #include <assimp/IOSystem.hpp>
55 #include <assimp/scene.h>
56 #include <assimp/DefaultLogger.hpp>
57 
58 
59 using namespace Assimp;
60 
61 static const aiImporterDesc desc = {
62     "OFF Importer",
63     "",
64     "",
65     "",
66     aiImporterFlags_SupportBinaryFlavour,
67     0,
68     0,
69     0,
70     0,
71     "off"
72 };
73 
74 // ------------------------------------------------------------------------------------------------
75 // Constructor to be privately used by Importer
OFFImporter()76 OFFImporter::OFFImporter()
77 {}
78 
79 // ------------------------------------------------------------------------------------------------
80 // Destructor, private as well
~OFFImporter()81 OFFImporter::~OFFImporter()
82 {}
83 
84 // ------------------------------------------------------------------------------------------------
85 // Returns whether the class can handle the format of the given file.
CanRead(const std::string & pFile,IOSystem * pIOHandler,bool checkSig) const86 bool OFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
87 {
88     const std::string extension = GetExtension(pFile);
89 
90     if (extension == "off")
91         return true;
92     else if (!extension.length() || checkSig)
93     {
94         if (!pIOHandler)return true;
95         const char* tokens[] = {"off"};
96         return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
97     }
98     return false;
99 }
100 
101 // ------------------------------------------------------------------------------------------------
GetInfo() const102 const aiImporterDesc* OFFImporter::GetInfo () const
103 {
104     return &desc;
105 }
106 
107 // ------------------------------------------------------------------------------------------------
108 // Imports the given file into the given scene structure.
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)109 void OFFImporter::InternReadFile( const std::string& pFile,
110     aiScene* pScene, IOSystem* pIOHandler)
111 {
112     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
113 
114     // Check whether we can read from the file
115     if( file.get() == NULL) {
116         throw DeadlyImportError( "Failed to open OFF file " + pFile + ".");
117     }
118 
119     // allocate storage and copy the contents of the file to a memory buffer
120     std::vector<char> mBuffer2;
121     TextFileToBuffer(file.get(),mBuffer2);
122     const char* buffer = &mBuffer2[0];
123 
124     char line[4096];
125     GetNextLine(buffer,line);
126     if ('O' == line[0]) {
127         GetNextLine(buffer,line); // skip the 'OFF' line
128     }
129 
130     const char* sz = line; SkipSpaces(&sz);
131     const unsigned int numVertices = strtoul10(sz,&sz);SkipSpaces(&sz);
132     const unsigned int numFaces = strtoul10(sz,&sz);
133 
134     if (!numVertices) {
135         throw DeadlyImportError("OFF: There are no valid vertices");
136     }
137     if (!numFaces) {
138         throw DeadlyImportError("OFF: There are no valid faces");
139     }
140 
141     pScene->mNumMeshes = 1;
142     pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes ];
143 
144     aiMesh* mesh = new aiMesh();
145     pScene->mMeshes[0] = mesh;
146 
147     mesh->mNumFaces = numFaces;
148     aiFace* faces = new aiFace [mesh->mNumFaces];
149     mesh->mFaces = faces;
150 
151     std::vector<aiVector3D> tempPositions(numVertices);
152 
153     // now read all vertex lines
154     for (unsigned int i = 0; i< numVertices;++i)
155     {
156         if(!GetNextLine(buffer,line))
157         {
158             DefaultLogger::get()->error("OFF: The number of verts in the header is incorrect");
159             break;
160         }
161         aiVector3D& v = tempPositions[i];
162 
163         sz = line; SkipSpaces(&sz);
164         sz = fast_atoreal_move<float>(sz,(float&)v.x); SkipSpaces(&sz);
165         sz = fast_atoreal_move<float>(sz,(float&)v.y); SkipSpaces(&sz);
166         fast_atoreal_move<float>(sz,(float&)v.z);
167     }
168 
169 
170     // First find out how many vertices we'll need
171     const char* old = buffer;
172     for (unsigned int i = 0; i< mesh->mNumFaces;++i)
173     {
174         if(!GetNextLine(buffer,line))
175         {
176             DefaultLogger::get()->error("OFF: The number of faces in the header is incorrect");
177             break;
178         }
179         sz = line;SkipSpaces(&sz);
180         faces->mNumIndices = strtoul10(sz,&sz);
181         if(!(faces->mNumIndices) || faces->mNumIndices > 9)
182         {
183             DefaultLogger::get()->error("OFF: Faces with zero indices aren't allowed");
184             --mesh->mNumFaces;
185             continue;
186         }
187         mesh->mNumVertices += faces->mNumIndices;
188         ++faces;
189     }
190 
191     if (!mesh->mNumVertices)
192         throw DeadlyImportError("OFF: There are no valid faces");
193 
194     // allocate storage for the output vertices
195     std::vector<aiVector3D> verts;
196     verts.reserve(mesh->mNumVertices);
197 
198     // second: now parse all face indices
199     buffer = old;
200     faces = mesh->mFaces;
201     for (unsigned int i = 0, p = 0; i< mesh->mNumFaces;)
202     {
203         if(!GetNextLine(buffer,line))break;
204 
205         unsigned int idx;
206         sz = line;SkipSpaces(&sz);
207         idx = strtoul10(sz,&sz);
208         if(!(idx) || idx > 9)
209             continue;
210 
211         faces->mIndices = new unsigned int [faces->mNumIndices];
212         for (unsigned int m = 0; m < faces->mNumIndices;++m)
213         {
214             SkipSpaces(&sz);
215             idx = strtoul10(sz,&sz);
216             if ((idx) >= numVertices)
217             {
218                 DefaultLogger::get()->error("OFF: Vertex index is out of range");
219                 idx = numVertices-1;
220             }
221             faces->mIndices[m] = p++;
222             verts.push_back(tempPositions[idx]);
223         }
224         ++i;
225         ++faces;
226     }
227 
228     if (mesh->mNumVertices != verts.size()) {
229         throw DeadlyImportError("OFF: Vertex count mismatch");
230     }
231     mesh->mVertices = new aiVector3D[verts.size()];
232     memcpy(mesh->mVertices, &verts[0], verts.size() * sizeof(aiVector3D));
233     // generate the output node graph
234     pScene->mRootNode = new aiNode();
235     pScene->mRootNode->mName.Set("<OFFRoot>");
236     pScene->mRootNode->mNumMeshes = 1;
237     pScene->mRootNode->mMeshes = new unsigned int [pScene->mRootNode->mNumMeshes];
238     pScene->mRootNode->mMeshes[0] = 0;
239 
240     // generate a default material
241     pScene->mNumMaterials = 1;
242     pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
243     aiMaterial* pcMat = new aiMaterial();
244 
245     aiColor4D clr(0.6f,0.6f,0.6f,1.0f);
246     pcMat->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
247     pScene->mMaterials[0] = pcMat;
248 
249     const int twosided =1;
250     pcMat->AddProperty(&twosided,1,AI_MATKEY_TWOSIDED);
251 }
252 
253 #endif // !! ASSIMP_BUILD_NO_OFF_IMPORTER
254