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 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
57using namespace Assimp;
58
59static 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
74TerragenImporter::TerragenImporter()
75: configComputeUVs (false)
76{}
77
78// ------------------------------------------------------------------------------------------------
79// Destructor, private as well
80TerragenImporter::~TerragenImporter()
81{}
82
83// ------------------------------------------------------------------------------------------------
84// Returns whether the class can handle the format of the given file.
85bool 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
107const aiImporterDesc* TerragenImporter::GetInfo () const
108{
109 return &desc;
110}
111
112// ------------------------------------------------------------------------------------------------
113// Setup import properties
114void 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.
122void 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