1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 
4 #include "System/TdfParser.h"
5 
6 #include <cassert>
7 #include <cstring>
8 #include <deque>
9 
10 #include "TerrainBase.h"
11 #include "TerrainNode.h"
12 #include "Textures.h"
13 #include "Map/SM3/Plane.h"
14 #include "Rendering/Textures/Bitmap.h"
15 #include "System/FileSystem/FileSystem.h"
16 #include "System/float3.h"
17 #if       defined(TERRAIN_USE_IL)
18 #include <IL/il.h>
19 #endif // defined(TERRAIN_USE_IL)
20 
21 namespace terrain {
22 
SetTexCoordGen(float * tgv)23 	void SetTexCoordGen(float* tgv)
24 	{
25 		glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
26 		glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
27 
28 		Plane s(tgv[0], 0, 0, tgv[2]);
29 		glTexGenfv(GL_S, GL_OBJECT_PLANE, (float*)&s);
30 		Plane t(0, 0, tgv[1], tgv[3]);
31 		glTexGenfv(GL_T, GL_OBJECT_PLANE, (float*)&t);
32 
33 		glEnable(GL_TEXTURE_GEN_S);
34 		glEnable(GL_TEXTURE_GEN_T);
35 	}
36 
37 //-----------------------------------------------------------------------
38 // Texturing objects
39 //-----------------------------------------------------------------------
40 
BaseTexture()41 	BaseTexture::BaseTexture()
42 		: id(0)
43 		, tilesize(10.0f, 10.0f)
44 	{
45 	}
46 
~BaseTexture()47 	BaseTexture::~BaseTexture()
48 	{
49 		if (id) {
50 			glDeleteTextures(1, &id);
51 			id = 0;
52 		}
53 	}
54 
CalcTexGenVector(float * v)55 	void BaseTexture::CalcTexGenVector(float* v)
56 	{
57 		v[0] = 1.0f / (SquareSize * tilesize.x);
58 		v[1] = 1.0f / (SquareSize * tilesize.y);
59 		v[2] = v[3] = 0.0f;
60 	}
61 
SetupTexGen()62 	void BaseTexture::SetupTexGen()
63 	{
64 		float tgv[4];
65 		CalcTexGenVector(tgv);
66 
67 		Plane s(tgv[0], 0, 0, tgv[2]);
68 		glTexGenfv(GL_S, GL_OBJECT_PLANE, (float*)&s);
69 		Plane t(0, 0, tgv[1], tgv[3]);
70 		glTexGenfv(GL_T, GL_OBJECT_PLANE, (float*)&t);
71 	}
72 
TiledTexture()73 	TiledTexture::TiledTexture()
74 	{}
75 
~TiledTexture()76 	TiledTexture::~TiledTexture()
77 	{}
78 
Load(const std::string & name,const std::string & section,ILoadCallback * cb,const TdfParser * tdf,bool isBumpmap)79 	void TiledTexture::Load(const std::string& name, const std::string& section, ILoadCallback* cb, const TdfParser* tdf, bool isBumpmap)
80 	{
81 		this->name = name;
82 
83 		tilesize.x = tilesize.y = atof(tdf->SGetValueDef("10", section + "\\Tilesize").c_str());
84 
85 		std::string texture;
86 		if (isBumpmap) {
87 			if (!tdf->SGetValue(texture, section + "\\Bumpmap"))
88 				return;
89 
90 			this->name += "_BM";
91 		} else {
92 			if (!tdf->SGetValue(texture, section + "\\File"))
93 				throw content_error("Texture " + section + " has no image file.");
94 		}
95 
96 		if (cb) cb->PrintMsg("    loading %s %s from %s...", isBumpmap ? "bumpmap" : "texture", name.c_str(), texture.c_str());
97 		id = LoadTexture(texture);
98 	}
99 
CreateFlatBumpmap()100 	TiledTexture* TiledTexture::CreateFlatBumpmap()
101 	{
102 		TiledTexture* tt = new TiledTexture;
103 		glGenTextures(1, &tt->id);
104 		glBindTexture(GL_TEXTURE_2D, tt->id);
105 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
106 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
107 		uchar img[3] = { 127, 127, 255 };
108 		glTexImage2D(GL_TEXTURE_2D,0, 3, 1, 1, 0,GL_RGB, GL_UNSIGNED_BYTE, img);
109 		tt->name = "_flatbm";
110 		return tt;
111 	}
112 
Blendmap()113 	Blendmap::Blendmap()
114 		: generatorInfo(NULL)
115 		, image(NULL)
116 		, curAreaResult(AlphaImage::AREA_ONE)
117 	{
118 	}
119 
~Blendmap()120 	Blendmap::~Blendmap()
121 	{
122 		delete generatorInfo;
123 	}
124 
Load(const std::string & name,const std::string & section,Heightmap * heightmap,ILoadCallback * cb,const TdfParser * tdf)125 	void Blendmap::Load(const std::string& name, const std::string& section, Heightmap* heightmap, ILoadCallback* cb, const TdfParser* tdf)
126 	{
127 		// Create blendmap
128 		this->name = name;
129 		tilesize.x = heightmap->w;
130 		tilesize.y = heightmap->h;
131 
132 		// Is the blendmap generated or loaded from a file?
133 		std::string blendmapImg;
134 		tdf->GetDef(blendmapImg, std::string(), section + "\\File");
135 		bool generateBlendmap = blendmapImg.empty();
136 		if (generateBlendmap) {
137 			// Load blendfactor function parameters
138 			GeneratorInfo* gi = generatorInfo = new GeneratorInfo;
139 			gi->coverage = atof(tdf->SGetValueDef("1", section + "\\Coverage").c_str());
140 			gi->noise = atof(tdf->SGetValueDef("0", section + "\\Noise").c_str());
141 			gi->minSlope = atof(tdf->SGetValueDef("0", section + "\\MinSlope").c_str());
142 			gi->maxSlope = atof(tdf->SGetValueDef("1", section + "\\MaxSlope").c_str());
143 			gi->minSlopeFuzzy = atof(tdf->SGetValueDef("0", section + "\\MinSlopeFuzzy").c_str());
144 			gi->maxSlopeFuzzy = atof(tdf->SGetValueDef("0", section + "\\MaxSlopeFuzzy").c_str());
145 			gi->maxHeight = atof(tdf->SGetValueDef("1.01", section + "\\MaxHeight").c_str());
146 			gi->minHeight = atof(tdf->SGetValueDef("-0.01", section + "\\MinHeight").c_str());
147 			gi->maxHeightFuzzy = atof(tdf->SGetValueDef("0", section + "\\MaxHeightFuzzy").c_str());
148 			gi->minHeightFuzzy = atof(tdf->SGetValueDef("0", section + "\\MinHeightFuzzy").c_str());
149 
150 			int generatorLOD = atoi(tdf->SGetValueDef("0", section + "\\GeneratorLOD").c_str());
151 
152 			float hmScale = atof(tdf->SGetValueDef("1000", "map\\terrain\\HeightScale").c_str());
153 			float hmOffset = atof(tdf->SGetValueDef("0", "map\\terrain\\HeightOffset").c_str());
154 
155 			if (cb) cb->PrintMsg("    generating %s...", name.c_str());
156 			Generate(heightmap, generatorLOD, hmScale, hmOffset);
157 		}
158 		else { // Load blendmap from file
159 			CBitmap bitmap;
160 			if (!bitmap.LoadGrayscale(blendmapImg))
161 				throw std::runtime_error("Failed to load blendmap image " + blendmapImg);
162 
163 			if (cb) cb->PrintMsg("    loading blendmap %s from %s...", name.c_str(), blendmapImg.c_str());
164 
165 			image = new AlphaImage;
166 			image->CopyFromBitmap(bitmap);
167 		}
168 	}
169 
BlendmapFilter(AlphaImage * img)170 	static void BlendmapFilter(AlphaImage* img)
171 	{
172 		float* nd = new float[img->w*img->h];
173 
174 		//3.95f*(x-.5f)^3+.5
175 
176 		for (int y=0;y<img->h;y++) {
177 			for (int x=0;x<img->w;x++) {
178 				int l=x?x-1:x;
179 				int r=x<img->w-1?x+1:x;
180 				int t=y?y-1:y;
181 				int b=y<img->h-1?y+1:y;
182 
183 				float result = 0.0f;
184 
185 				result += img->at(l,t);
186 				result += img->at(x,t);
187 				result += img->at(r,t);
188 
189 				result += img->at(l,y);
190 				//result += img->at(x,y);
191 				result += img->at(r,y);
192 
193 				result += img->at(l,b);
194 				result += img->at(x,b);
195 				result += img->at(r,b);
196 
197 				result *= (1.0f/8.0f);
198 				nd[y*img->w+x] = result;
199 			}
200 		}
201 
202 		memcpy(img->data, nd, sizeof(float)*img->w*img->h);
203 		delete[] nd;
204 	}
205 
206 
Generate(Heightmap * rootHm,int lodLevel,float hmScale,float hmOffset)207 	void Blendmap::Generate(Heightmap* rootHm, int lodLevel, float hmScale, float hmOffset)
208 	{
209 		const Heightmap* heightmap = rootHm->GetLevel(-lodLevel);
210 
211 		// Allocate the blendmap image
212 		AlphaImage* bm = new AlphaImage;
213 		bm->Alloc(heightmap->w-1, heightmap->h-1); // texture dimensions have to be power-of-two
214 
215 		Blendmap::GeneratorInfo* gi = generatorInfo;
216 
217 		// Calculate blend factors using the function parameters and heightmap input:
218 		for (int y=0;y<bm->h;y++)
219 		{
220 			for (int x=0;x<bm->w;x++)
221 			{
222 				const float h = (heightmap->atSynced(x, y) - hmOffset) / hmScale;
223 				float factor=1.0f;
224 
225 				if(h < gi->minHeight - gi->minHeightFuzzy) {
226 					bm->at(x,y) = 0.0f;
227 					continue;
228 				} else if (gi->minHeightFuzzy > 0.0f && h < gi->minHeight + gi->minHeightFuzzy)
229 					factor = (h - (gi->minHeight - gi->minHeightFuzzy)) / (2.0f * gi->minHeightFuzzy);
230 
231 				if(h > gi->maxHeight + gi->maxHeightFuzzy) {
232 					bm->at (x,y) = 0.0f;
233 					continue;
234 				} else if (gi->maxHeightFuzzy > 0.0f && h > gi->maxHeight - gi->maxHeightFuzzy)
235 					factor *= ((gi->maxHeight + gi->maxHeightFuzzy) - h) / (2.0f * gi->maxHeightFuzzy);
236 
237                 float norm_y = 0.0f;
238 				if (heightmap->normalData) {
239 					const uchar* cnorm = heightmap->GetNormal(x, y);
240 					norm_y = cnorm[1] / 255.0f * 2.0f - 1.0f;
241 					if (norm_y > 1.0f) norm_y = 1.0f;
242 				} else {
243 					Vector3 tangent, binormal;
244 					CalculateTangents(heightmap, x,y,tangent,binormal);
245 					Vector3 normal = tangent.cross(binormal);
246 					normal.ANormalize();
247 					norm_y = normal.y;
248 				}
249 
250 				// flatness=dotproduct of surface normal with up vector
251 				float slope = 1.0f - math::fabs(norm_y);
252 
253 				if (slope < gi->minSlope - gi->minSlopeFuzzy) {
254 					bm->at(x,y) = 0.0f;
255 					continue;
256 				} else if (gi->minSlopeFuzzy > 0.0f && slope < gi->minSlope + gi->minSlopeFuzzy)
257 					factor *= (h - (gi->minSlope - gi->minSlopeFuzzy)) / ( 2.0f * gi->minSlopeFuzzy);
258 
259 				if (slope > gi->maxSlope + gi->maxSlopeFuzzy) {
260 					bm->at(x,y) = 0.0f;
261 					continue;
262 				} else if (gi->maxSlopeFuzzy > 0.0f && slope > gi->maxSlope - gi->maxSlopeFuzzy)
263 					factor *= ((gi->maxSlope + gi->maxSlopeFuzzy) - h) / (2.0f * gi->maxSlopeFuzzy);
264 
265 				factor *= gi->coverage;
266 				factor *= (rand() < gi->noise * RAND_MAX) ? 0.0f : 1.0f;
267 
268 				bm->at(x,y) = factor;
269 			}
270 		}
271 
272 		BlendmapFilter(bm);
273 		image = bm;
274 	}
275 
276 
CalcTexGenVector(float * v4)277 	void DetailBumpmap::CalcTexGenVector(float* v4)
278 	{
279 		QuadRenderData* rd = node->renderData;
280 		float pw = 1.0f / rd->normalMapTexWidth;
281 
282 		v4[0] = pw * (rd->normalMapW - 1) / (node->end.x - node->start.x);
283 		v4[2] = 0.5f * pw - v4[0] * node->start.x;
284 
285 		v4[1] = pw * (rd->normalMapW - 1) / (node->end.z - node->start.z);
286 		v4[3] = 0.5f * pw - v4[1] * node->start.z;
287 	}
288 
~DetailBumpmap()289 	DetailBumpmap::~DetailBumpmap() {}
290 
291 
292 	// Util functions
293 
GenSphereBumpmap()294 	GLuint GenSphereBumpmap()
295 	{
296 		GLuint id;
297 		glGenTextures(1, &id);
298 		glBindTexture(GL_TEXTURE_2D, id);
299 
300 		uchar img[3 * 64 * 64], *p = img;
301 
302 		for (int y = 0; y < 64; y++) {
303 			for (int x = 0; x < 64; x++)
304 			{
305 				int sx = x - 32;
306 				int sy = y - 32;
307 				Vector3 n;
308 
309 				if (sx*sx + sy*sy < 32*32) {
310 					const int sz = (int)math::sqrt(static_cast<float>(32 * 32 - sx*sx - sy*sy));
311 					n = Vector3(sx, sy, sz);
312 					n.ANormalize();
313 				}
314 
315 				// compress it into a color
316 				*(p++) = (uchar)(255 * (n.x * 0.5f + 0.5f));
317 				*(p++) = (uchar)(255 * (n.y * 0.5f + 0.5f));
318 				*(p++) = (uchar)(255 * (n.z * 0.5f + 0.5f));
319 			}
320 		}
321 
322 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
323 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
324 		glTexImage2D(GL_TEXTURE_2D, 0, 3, 64, 64, 0, GL_RGB, GL_UNSIGNED_BYTE, img);
325 
326 		SaveImage("sphere_bm.jpg", 3, GL_UNSIGNED_BYTE, 64, 64, img);
327 		return id;
328 	}
329 
LoadTexture(const std::string & fn,bool isBumpmap)330 	GLuint LoadTexture(const std::string& fn, bool isBumpmap)
331 	{
332 		CBitmap bmp;
333 
334 		if (!bmp.Load(fn))
335 			throw content_error("Failed to load texture: " + fn);
336 
337 		GLuint tex = bmp.CreateTexture(true);
338 		return tex;
339 	}
340 
SaveImage(const char * fn,int components,GLenum type,int w,int h,void * data)341 	void SaveImage(const char* fn, int components, GLenum type, int w, int h, void* data)
342 	{
343 		// We use the fact that DevIL has the same constants for component type as OpenGL
344 		/// ( GL_UNSIGNED_BYTE = IL_UNSIGNED_BYTE for example )
345 #if       defined(TERRAIN_USE_IL)
346 		// Save image
347 		ILuint out;
348 		ilGenImages(1, &out);
349 		ilBindImage(out);
350 		ILenum fmt = IL_RGB;
351 		if (components == 4) {
352 			fmt = IL_RGBA;
353 		} else if (components == 1) {
354 			fmt = IL_LUMINANCE;
355 		}
356 		ilTexImage(w, h, 1, components, fmt, type, data);
357 		FileSystem::Remove(fn);
358 		ilSaveImage((ILstring) fn);
359 		ilDeleteImages(1, &out);
360 #endif // defined(TERRAIN_USE_IL)
361 	}
362 
363 }
364 
365