1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include <cstring>
4 #include "TerrainBase.h"
5 #include "Terrain.h"
6 #include "TerrainNode.h"
7 #include "Textures.h"
8 #include "Lightcalc.h"
9 #include "Map/ReadMap.h"
10 #include "Map/SM3/Vector3.h"
11 #include "Rendering/GL/myGL.h"
12 #include "System/Matrix44f.h"
13 #include "System/Util.h"
14 #include "System/Log/ILog.h"
15 
16 #include <IL/il.h>
17 #include <assert.h>
18 
19 #include "System/Misc/SpringTime.h"
20 
21 namespace terrain {
22 
23 using std::min;
24 
BlurGrayscaleImage(int w,int h,uchar * data)25 void BlurGrayscaleImage(int w, int h, uchar* data)
26 {
27 	uchar* nd = new uchar [w*h];
28 
29 	for (int y = 0; y < h; y++) {
30 		for (int x = 0; x < w; x++) {
31 			int l = x           ? (x - 1) : x;
32 			int r = x < (w - 1) ? (x + 1) : x;
33 			int t = y           ? (y - 1) : y;
34 			int b = y < (h - 1) ? (y + 1) : y;
35 
36 			int result = 0;
37 
38 #define AT(X,Y) result += data[w*(Y)+(X)]
39 			AT(l, t);
40 			AT(x, t);
41 			AT(r, t);
42 
43 			AT(l, y);
44 			AT(r, y);
45 
46 			AT(l, b);
47 			AT(x, b);
48 			AT(r, b);
49 #undef AT
50 			nd[y*w + x] = (uchar)(result / 8);
51 		}
52 	}
53 	memcpy(data, nd, w*h);
54 	delete[] nd;
55 }
56 
Lightmap(Heightmap * orghm,int level,int shadowLevelDif,LightingInfo * li)57 Lightmap::Lightmap(Heightmap* orghm, int level, int shadowLevelDif, LightingInfo* li)
58 {
59 	const spring_time startTicks = spring_gettime();
60 	tilesize.x = orghm->w-1;
61 	tilesize.y = orghm->h-1;
62 	name = "lightmap";
63 
64 	const Heightmap* hm = NULL;
65 	int w;
66 
67 	for(;;) {
68 		hm = orghm->GetLevel(-level);
69 		w = hm->w - 1;
70 
71 		GLint maxw;
72 		glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxw);
73 
74 		if (w > maxw) level++;
75 		else break;
76 	}
77 
78 	shadowLevelDif = 0;
79 	const Heightmap* shadowhm = orghm->GetLevel(-(level + shadowLevelDif));
80 	int shadowScale= 1 << shadowLevelDif;
81 	int shadowW = shadowhm->w - 1;
82 	assert(w/shadowW == shadowScale);
83 	//float org2c = w/float(orghm->w-1);
84 	//float c2org = (float)(orghm->w-1)/w;
85 
86 	float* centerhm = new float[w*w];
87 	Vector3* shading = new Vector3[w*w];
88 	for (int y=0;y<w;y++)
89 		for (int x=0;x<w;x++) {
90 			centerhm[y*w+x] =/* hm->scale * */ 0.25f * ( (int)hm->atSynced(x, y)+ (int)hm->atSynced(x + 1, y) + (int)hm->atSynced(x, y + 1) + (int)hm->atSynced(x + 1, y + 1) ); //+ hm->offset;
91 			shading[y*w + x] = li->ambient;
92 		}
93 
94 	uchar* lightMap = new uchar[shadowW * shadowW];
95 	for (std::vector<StaticLight>::const_iterator l = li->staticLights.begin(); l != li->staticLights.end(); ++l)
96 	{
97 		float lightx;
98 		float lighty;
99 
100 		if (l->directional) {
101 			lightx = l->position.x;
102 			lighty = l->position.y;
103 		} else {
104 			lightx = (int)(l->position.x / shadowhm->squareSize);
105 			lighty = (int)(l->position.z / shadowhm->squareSize);
106 		}
107 		CalculateShadows(lightMap, shadowW, lightx, lighty,
108 			l->position.y, centerhm, w, shadowScale, l->directional);
109 
110 		for (int y = 0; y < w; y++)
111 		{
112 			for (int x = 0; x < w; x++)
113 			{
114 				if (!lightMap[(y*shadowW + x) / shadowScale])
115 					continue;
116 
117 				Vector3 wp;
118 				if (l->directional)
119 					wp = l->position;
120 				else
121 					wp = l->position - Vector3((x + 0.5f) * hm->squareSize,centerhm[y*w + x], (y + 0.5f) * hm->squareSize);
122 
123 				const uchar* normal = hm->GetNormal(x, y);
124 				Vector3 normv((2 * (int)normal[0] - 256)/255.0f, (2 * (int)normal[1] - 256)/255.0f, (2 * (int)normal[2] - 256)/255.0f);
125 
126 				wp.ANormalize();
127 				float dot = wp.dot(normv);
128 				if(dot < 0.0f) dot = 0.0f;
129 				if(dot > 1.0f) dot = 1.0f;
130 				dot *= lightMap[(y*shadowW + x) / shadowScale] * (1.0f / 255.0f);
131 				shading[y*w + x] += l->color * dot;
132 			}
133 		}
134 
135 	}
136 	delete[] lightMap;
137 
138 	glGenTextures(1, &shadingTex);
139 	glBindTexture (GL_TEXTURE_2D, shadingTex);
140 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
141 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
142 
143 	uchar* shadingTexData = new uchar[w*w*4];
144 	for (int y = 0; y < w; y++) {
145 		for (int x = 0; x < w; x++) {
146 			shadingTexData[(y*w + x) * 4 + 0] = (uchar)(min(1.0f, shading[y*w + x].x) * 255);
147 			shadingTexData[(y*w + x) * 4 + 1] = (uchar)(min(1.0f, shading[y*w + x].y) * 255);
148 			shadingTexData[(y*w + x) * 4 + 2] = (uchar)(min(1.0f, shading[y*w + x].z) * 255);
149 			shadingTexData[(y*w + x) * 4 + 3] = CReadMap::EncodeHeight(centerhm[w*y + x]);
150 		}
151 	}
152 
153 	SaveImage ("lightmap.png", 4, IL_UNSIGNED_BYTE, w, w, shadingTexData);
154 
155 	glBuildMipmaps(GL_TEXTURE_2D, 4, w, w, GL_RGBA, GL_UNSIGNED_BYTE, shadingTexData);
156 	delete[] shadingTexData;
157 
158 	id = shadingTex;
159 
160 	delete[] shading;
161 	delete[] centerhm;
162 
163 	const spring_time numTicks = spring_gettime() - startTicks;
164 	LOG("Lightmap generation: %2.3f seconds", spring_tomsecs(numTicks) * 0.001f);
165 }
166 
~Lightmap()167 Lightmap::~Lightmap()
168 {
169 	if (shadingTex)
170 		glDeleteTextures(1, &shadingTex);
171 }
172 
CalculateShadows(uchar * dst,int dstw,float lightX,float lightY,float lightH,float * centerhm,int hmw,int hmscale,bool directional)173 void Lightmap::CalculateShadows (uchar* dst, int dstw, float lightX, float lightY, float lightH, float* centerhm, int hmw, int hmscale, bool directional)
174 {
175 	memset(dst, 255, dstw * dstw); // 255 is lit, 0 is unlit
176 
177 	for (int y = 0; y < dstw; y++)
178 	{
179 		for (int x = 0; x < dstw; x++)
180 		{
181 			if (!dst[y*dstw + x]) // shadowed pixels can't shadow other pixels
182 				continue;
183 
184 			float dx, dy, dh;
185 			float h = centerhm[(y*hmw + x) * hmscale];
186 
187 			if (directional) {
188 				dx = lightX;
189 				dy = lightY;
190 				dh = lightH;
191 			} else {
192 				dx = lightX - x;
193 				dy = lightY - y;
194 				dh = lightH - h;
195 
196 				if (x == lightX && y == lightY)
197 					continue;
198 			}
199 
200 			float len = math::sqrt(dx*dx + dy*dy);
201 			const float step = 5.0f;
202 			float invLength2d = step / len;
203 			dx *= invLength2d;
204 			dy *= invLength2d;
205 			dh *= invLength2d;
206 
207 			float px = (x + dx) * hmscale, py = (y + dy) * hmscale;
208 			h += dh;
209 			while ((px >= 0.0f) && (px < hmw) && (py >= 0.0f) && (py < hmw) && (len >= 0.0f))
210 			{
211 				int index = (int)py * hmw + (int)px;
212 
213 				if (centerhm[index] > h + 2.0f)
214 				{
215 					dst[y*dstw + x] = 0;
216 					break;
217 				}
218 
219 				px += dx;
220 				py += dy;
221 				h += dh;
222 				len -= step;
223 			}
224 		}
225 	}
226 
227 	BlurGrayscaleImage(dstw, dstw, dst);
228 
229 	char sm_fn[64];
230 	static int lightIndex = 0;
231 	SNPRINTF(sm_fn, sizeof(sm_fn), "shadowmap%d.png", lightIndex++);
232 	SaveImage(sm_fn, 1, IL_UNSIGNED_BYTE, dstw, dstw, dst);
233 }
234 }
235