1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include "Game/Camera.h"
4 #include "Game/Game.h"
5 #include "Map/BaseGroundDrawer.h"
6 #include "Map/Ground.h"
7 #include "Map/ReadMap.h"
8 #include "Map/MapInfo.h"
9 #include "Rendering/GlobalRendering.h"
10 #include "Rendering/UnitDrawer.h"
11 #include "Rendering/GL/myGL.h"
12 #include "Rendering/Env/ISky.h"
13 #include "Rendering/Env/CubeMapHandler.h"
14 #include "System/Config/ConfigHandler.h"
15 
16 CONFIG(int, CubeTexSizeSpecular).defaultValue(128).minimumValue(1);
17 CONFIG(int, CubeTexSizeReflection).defaultValue(128).minimumValue(1);
18 
19 static char cameraMemBuf[sizeof(CCamera)];
20 
21 CubeMapHandler* cubeMapHandler = NULL;
22 
CubeMapHandler()23 CubeMapHandler::CubeMapHandler() {
24 	envReflectionTexID = 0;
25 	skyReflectionTexID = 0;
26 	specularTexID = 0;
27 
28 	reflTexSize = 0;
29 	specTexSize = 0;
30 
31 	currReflectionFace = 0;
32 	specularTexIter = 0;
33 	mapSkyReflections = false;
34 
35 	specTexBuf = NULL;
36 }
37 
Init()38 bool CubeMapHandler::Init() {
39 	specTexSize = configHandler->GetInt("CubeTexSizeSpecular");
40 	reflTexSize = configHandler->GetInt("CubeTexSizeReflection");
41 	specTexBuf = new unsigned char[specTexSize * 4];
42 
43 	mapSkyReflections = !(mapInfo->smf.skyReflectModTexName.empty());
44 
45 	{
46 		glGenTextures(1, &specularTexID);
47 		glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, specularTexID);
48 		glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
49 		glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
50 		glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
51 		glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
52 
53 		CreateSpecularFace(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, specTexSize, float3( 1,  1,  1), float3( 0, 0, -2), float3(0, -2,  0));
54 		CreateSpecularFace(GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, specTexSize, float3(-1,  1, -1), float3( 0, 0,  2), float3(0, -2,  0));
55 		CreateSpecularFace(GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, specTexSize, float3(-1,  1, -1), float3( 2, 0,  0), float3(0,  0,  2));
56 		CreateSpecularFace(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, specTexSize, float3(-1, -1,  1), float3( 2, 0,  0), float3(0,  0, -2));
57 		CreateSpecularFace(GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, specTexSize, float3(-1,  1,  1), float3( 2, 0,  0), float3(0, -2,  0));
58 		CreateSpecularFace(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, specTexSize, float3( 1,  1, -1), float3(-2, 0,  0), float3(0, -2,  0));
59 	}
60 
61 	{
62 		glGenTextures(1, &envReflectionTexID);
63 		glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, envReflectionTexID);
64 		glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
65 		glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
66 		glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
67 		glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
68 
69 		glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, 0, GL_RGBA8, reflTexSize, reflTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
70 		glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, 0, GL_RGBA8, reflTexSize, reflTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
71 		glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, 0, GL_RGBA8, reflTexSize, reflTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
72 		glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, 0, GL_RGBA8, reflTexSize, reflTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
73 		glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, 0, GL_RGBA8, reflTexSize, reflTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
74 		glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, 0, GL_RGBA8, reflTexSize, reflTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
75 	}
76 
77 	if (mapSkyReflections) {
78 		glGenTextures(1, &skyReflectionTexID);
79 		glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, skyReflectionTexID);
80 		glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
81 		glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
82 		glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
83 		glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
84 
85 		glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, 0, GL_RGBA8, reflTexSize, reflTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
86 		glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, 0, GL_RGBA8, reflTexSize, reflTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
87 		glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, 0, GL_RGBA8, reflTexSize, reflTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
88 		glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, 0, GL_RGBA8, reflTexSize, reflTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
89 		glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, 0, GL_RGBA8, reflTexSize, reflTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
90 		glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, 0, GL_RGBA8, reflTexSize, reflTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
91 	}
92 
93 
94 	if (reflectionCubeFBO.IsValid()) {
95 		reflectionCubeFBO.Bind();
96 		reflectionCubeFBO.CreateRenderBuffer(GL_DEPTH_ATTACHMENT_EXT, GL_DEPTH_COMPONENT, reflTexSize, reflTexSize);
97 		reflectionCubeFBO.Unbind();
98 	}
99 
100 	if (!reflectionCubeFBO.IsValid()) {
101 		Free();
102 		return false;
103 	}
104 
105 	return true;
106 }
107 
Free()108 void CubeMapHandler::Free() {
109 	if (specularTexID != 0) {
110 		glDeleteTextures(1, &specularTexID);
111 		specularTexID = 0;
112 	}
113 	if (envReflectionTexID != 0) {
114 		glDeleteTextures(1, &envReflectionTexID);
115 		envReflectionTexID = 0;
116 	}
117 	if (skyReflectionTexID != 0) {
118 		glDeleteTextures(1, &skyReflectionTexID);
119 		skyReflectionTexID = 0;
120 	}
121 
122 	delete [] specTexBuf;
123 }
124 
125 
126 
UpdateReflectionTexture()127 void CubeMapHandler::UpdateReflectionTexture()
128 {
129 	if (!unitDrawer->UseAdvShading())
130 		return;
131 
132 	switch (currReflectionFace++) {
133 		case 0: {
134 			reflectionCubeFBO.Bind();
135 			CreateReflectionFace(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, -FwdVector, false);
136 			CreateReflectionFace(GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,   UpVector, false);
137 
138 			if (mapSkyReflections) {
139 				CreateReflectionFace(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,  RgtVector, true);
140 				CreateReflectionFace(GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, -RgtVector, true);
141 			}
142 		} break;
143 		case 1: {} break;
144 		case 2: {} break;
145 		case 3: {
146 			reflectionCubeFBO.Bind();
147 			CreateReflectionFace(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,  RgtVector, false);
148 			CreateReflectionFace(GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, -RgtVector, false);
149 
150 			if (mapSkyReflections) {
151 				CreateReflectionFace(GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,  UpVector, true);
152 				CreateReflectionFace(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, -UpVector, true);
153 			}
154 		} break;
155 		case 4: {} break;
156 		case 5: {} break;
157 		case 6: {
158 			reflectionCubeFBO.Bind();
159 			CreateReflectionFace(GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, FwdVector, false);
160 			CreateReflectionFace(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, -UpVector, false);
161 
162 			if (mapSkyReflections) {
163 				CreateReflectionFace(GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,  FwdVector, true);
164 				CreateReflectionFace(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, -FwdVector, true);
165 			}
166 		} break;
167 		case 7: {} break;
168 		case 8: {
169 			currReflectionFace = 0;
170 		} break;
171 		default: {
172 			currReflectionFace = 0;
173 		} break;
174 	}
175 }
176 
CreateReflectionFace(unsigned int glType,const float3 & camDir,bool skyOnly)177 void CubeMapHandler::CreateReflectionFace(unsigned int glType, const float3& camDir, bool skyOnly)
178 {
179 	reflectionCubeFBO.AttachTexture((skyOnly? skyReflectionTexID: envReflectionTexID), glType);
180 
181 	glPushAttrib(GL_FOG_BIT | GL_DEPTH_BUFFER_BIT);
182 	glViewport(0, 0, reflTexSize, reflTexSize);
183 
184 	if (skyOnly) {
185 		glDepthMask(GL_FALSE);
186 		glDisable(GL_DEPTH_TEST);
187 	} else {
188 		glClear(GL_DEPTH_BUFFER_BIT);
189 		glDepthMask(GL_TRUE);
190 		glEnable(GL_DEPTH_TEST);
191 	}
192 
193 	// anti-crash workaround for multi-threading
194 	new (cameraMemBuf) CCamera(*camera);
195 
196 	game->SetDrawMode(CGame::gameReflectionDraw);
197 
198 	camera->forward = camDir;
199 	camera->SetFov(90.0f);
200 	camera->SetPos((camera->GetPos()) * XZVector + UpVector * (CGround::GetHeightAboveWater(camera->GetPos().x, camera->GetPos().z, false) + 50.0f));
201 	// calculate temporary new coor-system and matrices
202 	camera->Update(true);
203 
204 	sky->Draw();
205 
206 	if (!skyOnly) {
207 		readMap->GetGroundDrawer()->Draw(DrawPass::TerrainReflection);
208 	}
209 
210 	// NOTE we do this later to save render context switches (this is one of the slowest OpenGL operations!)
211 	// reflectionCubeFBO.Unbind();
212 	// glViewport(globalRendering->viewPosX, 0, globalRendering->viewSizeX, globalRendering->viewSizeY);
213 	glPopAttrib();
214 
215 	game->SetDrawMode(CGame::gameNormalDraw);
216 
217 	camera->~CCamera();
218 	new (camera) CCamera(*reinterpret_cast<CCamera*>(cameraMemBuf));
219 	reinterpret_cast<CCamera*>(cameraMemBuf)->~CCamera();
220 
221 	camera->Update();
222 }
223 
224 
UpdateSpecularTexture()225 void CubeMapHandler::UpdateSpecularTexture()
226 {
227 	if (!unitDrawer->UseAdvShading())
228 		return;
229 
230 	glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, specularTexID);
231 
232 	int specularTexRow = specularTexIter / 3; //FIXME WTF
233 
234 	switch (specularTexIter % 3) {
235 		case 0: {
236 			UpdateSpecularFace(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, specTexSize, float3( 1,  1,  1), float3( 0, 0, -2), float3(0, -2,  0), specularTexRow, specTexBuf);
237 			UpdateSpecularFace(GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, specTexSize, float3(-1,  1, -1), float3( 0, 0,  2), float3(0, -2,  0), specularTexRow, specTexBuf);
238 			break;
239 		}
240 		case 1: {
241 			UpdateSpecularFace(GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, specTexSize, float3(-1,  1, -1), float3( 2, 0,  0), float3(0,  0,  2), specularTexRow, specTexBuf);
242 			UpdateSpecularFace(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, specTexSize, float3(-1, -1,  1), float3( 2, 0,  0), float3(0,  0, -2), specularTexRow, specTexBuf);
243 			break;
244 		}
245 		case 2: {
246 			UpdateSpecularFace(GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, specTexSize, float3(-1,  1,  1), float3( 2, 0,  0), float3(0, -2,  0), specularTexRow, specTexBuf);
247 			UpdateSpecularFace(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, specTexSize, float3( 1,  1, -1), float3(-2, 0,  0), float3(0, -2,  0), specularTexRow, specTexBuf);
248 			break;
249 		}
250 	}
251 
252 	// update one face of one row per frame
253 	++specularTexIter;
254 	specularTexIter = specularTexIter % (specTexSize * 3);
255 }
256 
CreateSpecularFacePart(unsigned int texType,unsigned int size,const float3 & cdir,const float3 & xdif,const float3 & ydif,unsigned int y,unsigned char * buf)257 void CubeMapHandler::CreateSpecularFacePart(
258 	unsigned int texType,
259 	unsigned int size,
260 	const float3& cdir,
261 	const float3& xdif,
262 	const float3& ydif,
263 	unsigned int y,
264 	unsigned char* buf)
265 {
266 	// TODO move to a shader
267 	for (int x = 0; x < size; ++x) {
268 		const float3 dir = (cdir + (xdif * (x + 0.5f)) / size + (ydif * (y + 0.5f)) / size).Normalize();
269 		const float dot  = std::max(0.0f, dir.dot(sky->GetLight()->GetLightDir()));
270 		const float spec = std::min(1.0f, math::pow(dot, mapInfo->light.specularExponent) + math::pow(dot, 3.0f) * 0.25f);
271 
272 		buf[x * 4 + 0] = (mapInfo->light.unitSpecularColor.x * spec * 255);
273 		buf[x * 4 + 1] = (mapInfo->light.unitSpecularColor.y * spec * 255);
274 		buf[x * 4 + 2] = (mapInfo->light.unitSpecularColor.z * spec * 255);
275 		buf[x * 4 + 3] = 255;
276 	}
277 }
278 
CreateSpecularFace(unsigned int texType,unsigned int size,const float3 & cdir,const float3 & xdif,const float3 & ydif)279 void CubeMapHandler::CreateSpecularFace(
280 	unsigned int texType,
281 	unsigned int size,
282 	const float3& cdir,
283 	const float3& xdif,
284 	const float3& ydif)
285 {
286 	std::vector<unsigned char> buf(size * size * 4, 0);
287 
288 	for (int y = 0; y < size; ++y) {
289 		CreateSpecularFacePart(texType, size, cdir, xdif, ydif, y, &buf[y * size * 4]);
290 	}
291 
292 	//! note: no mipmaps, cubemap linear filtering is broken
293 	glTexImage2D(texType, 0, GL_RGBA8, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, &buf[0]);
294 }
295 
UpdateSpecularFace(unsigned int texType,unsigned int size,const float3 & cdir,const float3 & xdif,const float3 & ydif,unsigned int y,unsigned char * buf)296 void CubeMapHandler::UpdateSpecularFace(
297 	unsigned int texType,
298 	unsigned int size,
299 	const float3& cdir,
300 	const float3& xdif,
301 	const float3& ydif,
302 	unsigned int y,
303 	unsigned char* buf)
304 {
305 	CreateSpecularFacePart(texType, size, cdir, xdif, ydif, y, buf);
306 
307 	glTexSubImage2D(texType, 0, 0, y, size, 1, GL_RGBA, GL_UNSIGNED_BYTE, buf);
308 }
309 
310