1 | /* |
2 | --------------------------------------------------------------------------- |
3 | Open Asset Import Library (assimp) |
4 | --------------------------------------------------------------------------- |
5 | |
6 | Copyright (c) 2006-2017, assimp team |
7 | |
8 | |
9 | All rights reserved. |
10 | |
11 | Redistribution and use of this software in source and binary forms, |
12 | with or without modification, are permitted provided that the following |
13 | conditions 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 | |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
39 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
40 | --------------------------------------------------------------------------- |
41 | */ |
42 | |
43 | /** @file Implementation of the Terragen importer class */ |
44 | |
45 | |
46 | |
47 | #ifndef ASSIMP_BUILD_NO_TERRAGEN_IMPORTER |
48 | |
49 | #include "TerragenLoader.h" |
50 | #include "StreamReader.h" |
51 | #include <assimp/Importer.hpp> |
52 | #include <assimp/IOSystem.hpp> |
53 | #include <assimp/scene.h> |
54 | #include <assimp/DefaultLogger.hpp> |
55 | #include <assimp/importerdesc.h> |
56 | |
57 | using namespace Assimp; |
58 | |
59 | static const aiImporterDesc desc = { |
60 | "Terragen Heightmap Importer" , |
61 | "" , |
62 | "" , |
63 | "http://www.planetside.co.uk/" , |
64 | aiImporterFlags_SupportBinaryFlavour, |
65 | 0, |
66 | 0, |
67 | 0, |
68 | 0, |
69 | "ter" |
70 | }; |
71 | |
72 | // ------------------------------------------------------------------------------------------------ |
73 | // Constructor to be privately used by Importer |
74 | TerragenImporter::TerragenImporter() |
75 | : configComputeUVs (false) |
76 | {} |
77 | |
78 | // ------------------------------------------------------------------------------------------------ |
79 | // Destructor, private as well |
80 | TerragenImporter::~TerragenImporter() |
81 | {} |
82 | |
83 | // ------------------------------------------------------------------------------------------------ |
84 | // Returns whether the class can handle the format of the given file. |
85 | bool TerragenImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const |
86 | { |
87 | // check file extension |
88 | std::string extension = GetExtension(pFile); |
89 | |
90 | if( extension == "ter" ) |
91 | return true; |
92 | |
93 | if( !extension.length() || checkSig) { |
94 | /* If CanRead() is called in order to check whether we |
95 | * support a specific file extension in general pIOHandler |
96 | * might be NULL and it's our duty to return true here. |
97 | */ |
98 | if (!pIOHandler)return true; |
99 | const char* tokens[] = {"terragen" }; |
100 | return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); |
101 | } |
102 | return false; |
103 | } |
104 | |
105 | // ------------------------------------------------------------------------------------------------ |
106 | // Build a string of all file extensions supported |
107 | const aiImporterDesc* TerragenImporter::GetInfo () const |
108 | { |
109 | return &desc; |
110 | } |
111 | |
112 | // ------------------------------------------------------------------------------------------------ |
113 | // Setup import properties |
114 | void TerragenImporter::SetupProperties(const Importer* pImp) |
115 | { |
116 | // AI_CONFIG_IMPORT_TER_MAKE_UVS |
117 | configComputeUVs = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_TER_MAKE_UVS,0) ); |
118 | } |
119 | |
120 | // ------------------------------------------------------------------------------------------------ |
121 | // Imports the given file into the given scene structure. |
122 | void TerragenImporter::InternReadFile( const std::string& pFile, |
123 | aiScene* pScene, IOSystem* pIOHandler) |
124 | { |
125 | IOStream* file = pIOHandler->Open( pFile, "rb" ); |
126 | |
127 | // Check whether we can read from the file |
128 | if( file == NULL) |
129 | throw DeadlyImportError( "Failed to open TERRAGEN TERRAIN file " + pFile + "." ); |
130 | |
131 | // Construct a stream reader to read all data in the correct endianness |
132 | StreamReaderLE reader(file); |
133 | if(reader.GetRemainingSize() < 16) |
134 | throw DeadlyImportError( "TER: file is too small" ); |
135 | |
136 | // Check for the existence of the two magic strings 'TERRAGEN' and 'TERRAIN ' |
137 | if (::strncmp((const char*)reader.GetPtr(),AI_TERR_BASE_STRING,8)) |
138 | throw DeadlyImportError( "TER: Magic string \'TERRAGEN\' not found" ); |
139 | |
140 | if (::strncmp((const char*)reader.GetPtr()+8,AI_TERR_TERRAIN_STRING,8)) |
141 | throw DeadlyImportError( "TER: Magic string \'TERRAIN\' not found" ); |
142 | |
143 | unsigned int x = 0,y = 0,mode = 0; |
144 | |
145 | aiNode* root = pScene->mRootNode = new aiNode(); |
146 | root->mName.Set("<TERRAGEN.TERRAIN>" ); |
147 | |
148 | // Default scaling is 30 |
149 | root->mTransformation.a1 = root->mTransformation.b2 = root->mTransformation.c3 = 30.f; |
150 | |
151 | // Now read all chunks until we're finished or an EOF marker is encountered |
152 | reader.IncPtr(16); |
153 | while (reader.GetRemainingSize() >= 4) |
154 | { |
155 | const char* head = (const char*)reader.GetPtr(); |
156 | reader.IncPtr(4); |
157 | |
158 | // EOF, break in every case |
159 | if (!::strncmp(head,AI_TERR_EOF_STRING,4)) |
160 | break; |
161 | |
162 | // Number of x-data points |
163 | if (!::strncmp(head,AI_TERR_CHUNK_XPTS,4)) |
164 | { |
165 | x = (uint16_t)reader.GetI2(); |
166 | } |
167 | // Number of y-data points |
168 | else if (!::strncmp(head,AI_TERR_CHUNK_YPTS,4)) |
169 | { |
170 | y = (uint16_t)reader.GetI2(); |
171 | } |
172 | // Squared terrains width-1. |
173 | else if (!::strncmp(head,AI_TERR_CHUNK_SIZE,4)) |
174 | { |
175 | x = y = (uint16_t)reader.GetI2()+1; |
176 | } |
177 | // terrain scaling |
178 | else if (!::strncmp(head,AI_TERR_CHUNK_SCAL,4)) |
179 | { |
180 | root->mTransformation.a1 = reader.GetF4(); |
181 | root->mTransformation.b2 = reader.GetF4(); |
182 | root->mTransformation.c3 = reader.GetF4(); |
183 | } |
184 | // mapping == 1: earth radius |
185 | else if (!::strncmp(head,AI_TERR_CHUNK_CRAD,4)) |
186 | { |
187 | reader.GetF4(); |
188 | } |
189 | // mapping mode |
190 | else if (!::strncmp(head,AI_TERR_CHUNK_CRVM,4)) |
191 | { |
192 | mode = reader.GetI1(); |
193 | if (0 != mode) |
194 | DefaultLogger::get()->error("TER: Unsupported mapping mode, a flat terrain is returned" ); |
195 | } |
196 | // actual terrain data |
197 | else if (!::strncmp(head,AI_TERR_CHUNK_ALTW,4)) |
198 | { |
199 | float hscale = (float)reader.GetI2() / 65536; |
200 | float bheight = (float)reader.GetI2(); |
201 | |
202 | if (!hscale)hscale = 1; |
203 | |
204 | // Ensure we have enough data |
205 | if (reader.GetRemainingSize() < x*y*2) |
206 | throw DeadlyImportError("TER: ALTW chunk is too small" ); |
207 | |
208 | if (x <= 1 || y <= 1) |
209 | throw DeadlyImportError("TER: Invalid terrain size" ); |
210 | |
211 | // Allocate the output mesh |
212 | pScene->mMeshes = new aiMesh*[pScene->mNumMeshes = 1]; |
213 | aiMesh* m = pScene->mMeshes[0] = new aiMesh(); |
214 | |
215 | // We return quads |
216 | aiFace* f = m->mFaces = new aiFace[m->mNumFaces = (x-1)*(y-1)]; |
217 | aiVector3D* pv = m->mVertices = new aiVector3D[m->mNumVertices = m->mNumFaces*4]; |
218 | |
219 | aiVector3D *uv( NULL ); |
220 | float step_y( 0.0f ), step_x( 0.0f ); |
221 | if (configComputeUVs) { |
222 | uv = m->mTextureCoords[0] = new aiVector3D[m->mNumVertices]; |
223 | step_y = 1.f/y; |
224 | step_x = 1.f/x; |
225 | } |
226 | const int16_t* data = (const int16_t*)reader.GetPtr(); |
227 | |
228 | for (unsigned int yy = 0, t = 0; yy < y-1;++yy) { |
229 | for (unsigned int xx = 0; xx < x-1;++xx,++f) { |
230 | |
231 | // make verts |
232 | const float fy = (float)yy, fx = (float)xx; |
233 | unsigned tmp,tmp2; |
234 | *pv++ = aiVector3D(fx,fy, (float)data[(tmp2=x*yy) + xx] * hscale + bheight); |
235 | *pv++ = aiVector3D(fx,fy+1, (float)data[(tmp=x*(yy+1)) + xx] * hscale + bheight); |
236 | *pv++ = aiVector3D(fx+1,fy+1,(float)data[tmp + xx+1] * hscale + bheight); |
237 | *pv++ = aiVector3D(fx+1,fy, (float)data[tmp2 + xx+1] * hscale + bheight); |
238 | |
239 | // also make texture coordinates, if necessary |
240 | if (configComputeUVs) { |
241 | *uv++ = aiVector3D( step_x*xx, step_y*yy, 0.f ); |
242 | *uv++ = aiVector3D( step_x*xx, step_y*(yy+1), 0.f ); |
243 | *uv++ = aiVector3D( step_x*(xx+1), step_y*(yy+1), 0.f ); |
244 | *uv++ = aiVector3D( step_x*(xx+1), step_y*yy, 0.f ); |
245 | } |
246 | |
247 | // make indices |
248 | f->mIndices = new unsigned int[f->mNumIndices = 4]; |
249 | for (unsigned int i = 0; i < 4;++i) |
250 | f->mIndices[i] = t++; |
251 | } |
252 | } |
253 | |
254 | // Add the mesh to the root node |
255 | root->mMeshes = new unsigned int[root->mNumMeshes = 1]; |
256 | root->mMeshes[0] = 0; |
257 | } |
258 | |
259 | // Get to the next chunk (4 byte aligned) |
260 | unsigned dtt; |
261 | if ((dtt = reader.GetCurrentPos() & 0x3)) |
262 | reader.IncPtr(4-dtt); |
263 | } |
264 | |
265 | // Check whether we have a mesh now |
266 | if (pScene->mNumMeshes != 1) |
267 | throw DeadlyImportError("TER: Unable to load terrain" ); |
268 | |
269 | // Set the AI_SCENE_FLAGS_TERRAIN bit |
270 | pScene->mFlags |= AI_SCENE_FLAGS_TERRAIN; |
271 | } |
272 | |
273 | #endif // !! ASSIMP_BUILD_NO_TERRAGEN_IMPORTER |
274 | |