1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5
6 Copyright (c) 2006-2012, 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 Implementation of the Terragen importer class */
43
44 #include "AssimpPCH.h"
45
46 #ifndef ASSIMP_BUILD_NO_TERRAGEN_IMPORTER
47 #include "TerragenLoader.h"
48
49 using namespace Assimp;
50
51 // ------------------------------------------------------------------------------------------------
52 // Constructor to be privately used by Importer
TerragenImporter()53 TerragenImporter::TerragenImporter()
54 : configComputeUVs (false)
55 {}
56
57 // ------------------------------------------------------------------------------------------------
58 // Destructor, private as well
~TerragenImporter()59 TerragenImporter::~TerragenImporter()
60 {}
61
62 // ------------------------------------------------------------------------------------------------
63 // Returns whether the class can handle the format of the given file.
CanRead(const std::string & pFile,IOSystem * pIOHandler,bool checkSig) const64 bool TerragenImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
65 {
66 // check file extension
67 std::string extension = GetExtension(pFile);
68
69 if( extension == "ter")
70 return true;
71
72 if( !extension.length() || checkSig) {
73 /* If CanRead() is called in order to check whether we
74 * support a specific file extension in general pIOHandler
75 * might be NULL and it's our duty to return true here.
76 */
77 if (!pIOHandler)return true;
78 const char* tokens[] = {"terragen"};
79 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
80 }
81 return false;
82 }
83
84 // ------------------------------------------------------------------------------------------------
85 // Build a string of all file extensions supported
GetExtensionList(std::set<std::string> & extensions)86 void TerragenImporter::GetExtensionList(std::set<std::string>& extensions)
87 {
88 extensions.insert("ter");
89 }
90
91 // ------------------------------------------------------------------------------------------------
92 // Setup import properties
SetupProperties(const Importer * pImp)93 void TerragenImporter::SetupProperties(const Importer* pImp)
94 {
95 // AI_CONFIG_IMPORT_TER_MAKE_UVS
96 configComputeUVs = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_TER_MAKE_UVS,0) );
97 }
98
99 // ------------------------------------------------------------------------------------------------
100 // Imports the given file into the given scene structure.
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)101 void TerragenImporter::InternReadFile( const std::string& pFile,
102 aiScene* pScene, IOSystem* pIOHandler)
103 {
104 IOStream* file = pIOHandler->Open( pFile, "rb");
105
106 // Check whether we can read from the file
107 if( file == NULL)
108 throw DeadlyImportError( "Failed to open TERRAGEN TERRAIN file " + pFile + ".");
109
110 // Construct a stream reader to read all data in the correct endianess
111 StreamReaderLE reader(file);
112 if(reader.GetRemainingSize() < 16)
113 throw DeadlyImportError( "TER: file is too small" );
114
115 // Check for the existence of the two magic strings 'TERRAGEN' and 'TERRAIN '
116 if (::strncmp((const char*)reader.GetPtr(),AI_TERR_BASE_STRING,8))
117 throw DeadlyImportError( "TER: Magic string \'TERRAGEN\' not found" );
118
119 if (::strncmp((const char*)reader.GetPtr()+8,AI_TERR_TERRAIN_STRING,8))
120 throw DeadlyImportError( "TER: Magic string \'TERRAIN\' not found" );
121
122 unsigned int x = 0,y = 0,mode = 0;
123 float rad = 6370.f;
124 (void)rad;
125
126
127 aiNode* root = pScene->mRootNode = new aiNode();
128 root->mName.Set("<TERRAGEN.TERRAIN>");
129
130 // Default scaling is 30
131 root->mTransformation.a1 = root->mTransformation.b2 = root->mTransformation.c3 = 30.f;
132
133 // Now read all chunks until we're finished or an EOF marker is encountered
134 reader.IncPtr(16);
135 while (reader.GetRemainingSize() >= 4)
136 {
137 const char* head = (const char*)reader.GetPtr();
138 reader.IncPtr(4);
139
140 // EOF, break in every case
141 if (!::strncmp(head,AI_TERR_EOF_STRING,4))
142 break;
143
144 // Number of x-data points
145 if (!::strncmp(head,AI_TERR_CHUNK_XPTS,4))
146 {
147 x = (uint16_t)reader.GetI2();
148 }
149 // Number of y-data points
150 else if (!::strncmp(head,AI_TERR_CHUNK_YPTS,4))
151 {
152 y = (uint16_t)reader.GetI2();
153 }
154 // Squared terrains width-1.
155 else if (!::strncmp(head,AI_TERR_CHUNK_SIZE,4))
156 {
157 x = y = (uint16_t)reader.GetI2()+1;
158 }
159 // terrain scaling
160 else if (!::strncmp(head,AI_TERR_CHUNK_SCAL,4))
161 {
162 root->mTransformation.a1 = reader.GetF4();
163 root->mTransformation.b2 = reader.GetF4();
164 root->mTransformation.c3 = reader.GetF4();
165 }
166 // mapping == 1: earth radius
167 else if (!::strncmp(head,AI_TERR_CHUNK_CRAD,4))
168 {
169 rad = reader.GetF4();
170 }
171 // mapping mode
172 else if (!::strncmp(head,AI_TERR_CHUNK_CRVM,4))
173 {
174 mode = reader.GetI1();
175 if (0 != mode)
176 DefaultLogger::get()->error("TER: Unsupported mapping mode, a flat terrain is returned");
177 }
178 // actual terrain data
179 else if (!::strncmp(head,AI_TERR_CHUNK_ALTW,4))
180 {
181 float hscale = (float)reader.GetI2() / 65536;
182 float bheight = (float)reader.GetI2();
183
184 if (!hscale)hscale = 1;
185
186 // Ensure we have enough data
187 if (reader.GetRemainingSize() < x*y*2)
188 throw DeadlyImportError("TER: ALTW chunk is too small");
189
190 if (x <= 1 || y <= 1)
191 throw DeadlyImportError("TER: Invalid terrain size");
192
193 // Allocate the output mesh
194 pScene->mMeshes = new aiMesh*[pScene->mNumMeshes = 1];
195 aiMesh* m = pScene->mMeshes[0] = new aiMesh();
196
197 // We return quads
198 aiFace* f = m->mFaces = new aiFace[m->mNumFaces = (x-1)*(y-1)];
199 aiVector3D* pv = m->mVertices = new aiVector3D[m->mNumVertices = m->mNumFaces*4];
200
201 aiVector3D *uv( NULL );
202 float step_y( 0.0f ), step_x( 0.0f );
203 if (configComputeUVs) {
204 uv = m->mTextureCoords[0] = new aiVector3D[m->mNumVertices];
205 step_y = 1.f/y;
206 step_x = 1.f/x;
207 }
208 const int16_t* data = (const int16_t*)reader.GetPtr();
209
210 for (unsigned int yy = 0, t = 0; yy < y-1;++yy) {
211 for (unsigned int xx = 0; xx < x-1;++xx,++f) {
212
213 // make verts
214 const float fy = (float)yy, fx = (float)xx;
215 register unsigned tmp,tmp2;
216 *pv++ = aiVector3D(fx,fy, (float)data[(tmp2=x*yy) + xx] * hscale + bheight);
217 *pv++ = aiVector3D(fx,fy+1, (float)data[(tmp=x*(yy+1)) + xx] * hscale + bheight);
218 *pv++ = aiVector3D(fx+1,fy+1,(float)data[tmp + xx+1] * hscale + bheight);
219 *pv++ = aiVector3D(fx+1,fy, (float)data[tmp2 + xx+1] * hscale + bheight);
220
221 // also make texture coordinates, if necessary
222 if (configComputeUVs) {
223 *uv++ = aiVector3D( step_x*xx, step_y*yy, 0.f );
224 *uv++ = aiVector3D( step_x*xx, step_y*(yy+1), 0.f );
225 *uv++ = aiVector3D( step_x*(xx+1), step_y*(yy+1), 0.f );
226 *uv++ = aiVector3D( step_x*(xx+1), step_y*yy, 0.f );
227 }
228
229 // make indices
230 f->mIndices = new unsigned int[f->mNumIndices = 4];
231 for (unsigned int i = 0; i < 4;++i)
232 f->mIndices[i] = t++;
233 }
234 }
235
236 // Add the mesh to the root node
237 root->mMeshes = new unsigned int[root->mNumMeshes = 1];
238 root->mMeshes[0] = 0;
239 }
240
241 // Get to the next chunk (4 byte aligned)
242 unsigned dtt;
243 if ((dtt = reader.GetCurrentPos() & 0x3))
244 reader.IncPtr(4-dtt);
245 }
246
247 // Check whether we have a mesh now
248 if (pScene->mNumMeshes != 1)
249 throw DeadlyImportError("TER: Unable to load terrain");
250
251 // Set the AI_SCENE_FLAGS_TERRAIN bit
252 pScene->mFlags |= AI_SCENE_FLAGS_TERRAIN;
253 }
254
255 #endif // !! ASSIMP_BUILD_NO_TERRAGEN_IMPORTER
256