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