1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include <cmath>
4 
5 #include "GrassDrawer.h"
6 #include "Game/Camera.h"
7 #include "Map/BaseGroundDrawer.h"
8 #include "Map/Ground.h"
9 #include "Map/MapInfo.h"
10 #include "Map/ReadMap.h"
11 #include "Rendering/GlobalRendering.h"
12 #include "Rendering/ShadowHandler.h"
13 #include "Rendering/Env/ISky.h"
14 #include "Rendering/Env/CubeMapHandler.h"
15 #include "Rendering/GL/myGL.h"
16 #include "Rendering/GL/FBO.h"
17 #include "Rendering/GL/VertexArray.h"
18 #include "Rendering/Shaders/ShaderHandler.h"
19 #include "Rendering/Shaders/Shader.h"
20 #include "Rendering/Textures/Bitmap.h"
21 #include "Sim/Misc/Wind.h"
22 #include "System/EventHandler.h"
23 #include "System/myMath.h"
24 #include "System/Config/ConfigHandler.h"
25 #include "System/Color.h"
26 #include "System/Exceptions.h"
27 #include "System/UnsyncedRNG.h"
28 #include "System/Util.h"
29 #include "System/ThreadPool.h"
30 #include "System/TimeProfiler.h"
31 #include "System/Log/ILog.h"
32 #include "System/FileSystem/FileHandler.h"
33 
34 CONFIG(int, GrassDetail).defaultValue(7).minimumValue(0).description("Sets how detailed the engine rendered grass will be on any given map.");
35 
36 static const float turfSize        = 20.0f;            // single turf size
37 static const float partTurfSize    = turfSize * 1.0f;  // single turf size
38 static const int   grassSquareSize = 4;                // mapsquares per grass square
39 static const int   grassBlockSize  = 4;                // grass squares per grass block
40 static const int   blockMapSize    = grassSquareSize * grassBlockSize;
41 
42 static const int   gSSsq = SQUARE_SIZE * grassSquareSize;
43 static const int   bMSsq = SQUARE_SIZE * blockMapSize;
44 
45 static UnsyncedRNG rng;
46 
47 
48 CGrassDrawer* grassDrawer = nullptr;
49 
50 
CGrassDrawer()51 CGrassDrawer::CGrassDrawer()
52 : CEventClient("[GrassDrawer]", 199992, false)
53 , grassOff(false)
54 , blocksX(gs->mapx / grassSquareSize / grassBlockSize)
55 , blocksY(gs->mapy / grassSquareSize / grassBlockSize)
56 , grassDL(0)
57 , grassBladeTex(0)
58 , farTex(0)
59 , farnearVA(nullptr)
60 , updateBillboards(false)
61 , grassMap(nullptr)
62 {
63 	rng.Seed(15);
64 	const int detail = configHandler->GetInt("GrassDetail");
65 
66 	// some ATI drivers crash with grass enabled, default to disabled
67 	if ((detail == 0) || ((detail == 7) && globalRendering->haveATI)) {
68 		grassOff = true;
69 		return;
70 	}
71 
72 	// needed to create the far tex
73 	if (!GLEW_EXT_framebuffer_blit) {
74 		grassOff = true;
75 		return;
76 	}
77 
78 	// load grass density from map
79 	{
80 		MapBitmapInfo grassbm;
81 		unsigned char* grassdata = readMap->GetInfoMap("grass", &grassbm);
82 		if (!grassdata) {
83 			grassOff = true;
84 			return;
85 		}
86 
87 		if (grassbm.width != gs->mapx / grassSquareSize || grassbm.height != gs->mapy / grassSquareSize) {
88 			char b[128];
89 			SNPRINTF(b, sizeof(b), "grass-map has wrong size (%dx%d, should be %dx%d)\n",
90 				grassbm.width, grassbm.height, gs->mapx / 4, gs->mapy / 4);
91 			throw std::runtime_error(b);
92 		}
93 		const int grassMapSize = gs->mapx * gs->mapy / (grassSquareSize * grassSquareSize);
94 		grassMap = new unsigned char[grassMapSize];
95 		memcpy(grassMap, grassdata, grassMapSize);
96 		readMap->FreeInfoMap("grass", grassdata);
97 	}
98 
99 	// create/load blade texture
100 	{
101 		CBitmap grassBladeTexBM;
102 		if (!grassBladeTexBM.Load(mapInfo->grass.bladeTexName)) {
103 			// map didn't define a grasstex, so generate one
104 			grassBladeTexBM.channels = 4;
105 			grassBladeTexBM.Alloc(256,64);
106 
107 			for (int a = 0; a < 16; ++a) {
108 				CreateGrassBladeTex(&grassBladeTexBM.mem[a * 16 * 4]);
109 			}
110 		}
111 		//grassBladeTexBM.Save("blade.png", false);
112 		grassBladeTex = grassBladeTexBM.CreateTexture(true);
113 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
114 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
115 	}
116 
117 	// create shaders * finalize
118 	grass.resize(blocksX * blocksY);
119 	farnearVA = new CVertexArray;
120 	grassDL = glGenLists(1);
121 
122 	ChangeDetail(detail);
123 	LoadGrassShaders();
124 	configHandler->NotifyOnChange(this);
125 
126 	// eventclient
127 	autoLinkEvents = true;
128 	RegisterLinkedEvents(this);
129 	eventHandler.AddClient(this);
130 }
131 
~CGrassDrawer()132 CGrassDrawer::~CGrassDrawer()
133 {
134 	configHandler->RemoveObserver(this);
135 
136 	delete farnearVA;
137 	delete[] grassMap;
138 	glDeleteLists(grassDL, 1);
139 	glDeleteTextures(1, &grassBladeTex);
140 	glDeleteTextures(1, &farTex);
141 	shaderHandler->ReleaseProgramObjects("[GrassDrawer]");
142 }
143 
144 
~GrassStruct()145 CGrassDrawer::GrassStruct::~GrassStruct() {
146 	delete va;
147 }
148 
149 
ChangeDetail(int detail)150 void CGrassDrawer::ChangeDetail(int detail) {
151 	// TODO: get rid of the magic constants
152 	const int detail_lim = std::min(3, detail);
153 	maxGrassDist = 800 + std::sqrt((float) detail) * 240;
154 	maxDetailedDist = 146 + detail * 24;
155 	detailedBlocks = int((maxDetailedDist + 128.f * 1.5f) / bMSsq) + 1;
156 	numTurfs = 3 + int(detail_lim * 0.5f);
157 	strawPerTurf = std::min(50 + int(std::sqrt(detail_lim) * 10), mapInfo->grass.maxStrawsPerTurf);
158 
159 	// recreate textures & XBOs
160 	CreateGrassDispList(grassDL);
161 	CreateFarTex();
162 
163 	// reset  all cached blocks
164 	for (GrassStruct& pGS: grass) {
165 		ResetPos(pGS.posX, pGS.posZ);
166 	}
167 }
168 
169 
ConfigNotify(const std::string & key,const std::string & value)170 void CGrassDrawer::ConfigNotify(const std::string& key, const std::string& value) {
171 	if (key == "GrassDetail") {
172 		ChangeDetail(std::atoi(value.c_str()));
173 	}
174 }
175 
176 
LoadGrassShaders()177 void CGrassDrawer::LoadGrassShaders() {
178 	if (!globalRendering->haveGLSL) {
179 		return;
180 	}
181 
182 	#define sh shaderHandler
183 	grassShaders.resize(GRASS_PROGRAM_LAST, NULL);
184 
185 	static const std::string shaderNames[GRASS_PROGRAM_LAST] = {
186 		"grassNearAdvShader",
187 		"grassDistAdvShader",
188 		"grassShadGenShader"
189 	};
190 	static const std::string shaderDefines[GRASS_PROGRAM_LAST] = {
191 		"#define DISTANCE_NEAR\n",
192 		"#define DISTANCE_FAR\n",
193 		"#define SHADOW_GEN\n"
194 	};
195 
196 	for (int i = 0; i < GRASS_PROGRAM_LAST; i++) {
197 		grassShaders[i] = sh->CreateProgramObject("[GrassDrawer]", shaderNames[i] + "GLSL", false);
198 		grassShaders[i]->AttachShaderObject(sh->CreateShaderObject("GLSL/GrassVertProg.glsl", shaderDefines[i], GL_VERTEX_SHADER));
199 		grassShaders[i]->AttachShaderObject(sh->CreateShaderObject("GLSL/GrassFragProg.glsl", shaderDefines[i], GL_FRAGMENT_SHADER));
200 		grassShaders[i]->Link();
201 
202 		grassShaders[i]->Enable();
203 		grassShaders[i]->SetUniform("mapSizePO2", 1.0f / (gs->pwr2mapx * SQUARE_SIZE), 1.0f / (gs->pwr2mapy * SQUARE_SIZE));
204 		grassShaders[i]->SetUniform("mapSize",    1.0f / (gs->mapx     * SQUARE_SIZE), 1.0f / (gs->mapy     * SQUARE_SIZE));
205 		grassShaders[i]->SetUniform("bladeTex",        0);
206 		grassShaders[i]->SetUniform("grassShadingTex", 1);
207 		grassShaders[i]->SetUniform("shadingTex",      2);
208 		grassShaders[i]->SetUniform("infoMap",         3);
209 		grassShaders[i]->SetUniform("shadowMap",       4);
210 		grassShaders[i]->SetUniform("specularTex",     5);
211 		grassShaders[i]->Disable();
212 		grassShaders[i]->Validate();
213 
214 		if (!grassShaders[i]->IsValid()) {
215 			grassOff = true;
216 			return;
217 		}
218 	}
219 
220 	#undef sh
221 }
222 
223 
EnableShader(const GrassShaderProgram type)224 void CGrassDrawer::EnableShader(const GrassShaderProgram type) {
225 	CBaseGroundDrawer* gd = readMap->GetGroundDrawer();
226 	const float3 windSpeed =
227 		wind.GetCurrentDirection() *
228 		wind.GetCurrentStrength() *
229 		mapInfo->grass.bladeWaveScale;
230 
231 	grassShader = grassShaders[type];
232 	grassShader->SetFlag("HAVE_INFOTEX", gd->DrawExtraTex());
233 	grassShader->SetFlag("HAVE_SHADOWS", shadowHandler->shadowsLoaded);
234 	grassShader->Enable();
235 
236 	grassShader->SetUniform("frame", gs->frameNum + globalRendering->timeOffset);
237 	grassShader->SetUniform3v("windSpeed", &windSpeed.x);
238 	grassShader->SetUniform3v("camPos",    &camera->GetPos().x);
239 	grassShader->SetUniform3v("camDir",    &camera->forward.x);
240 	grassShader->SetUniform3v("camUp",     &camera->up.x);
241 	grassShader->SetUniform3v("camRight",  &camera->right.x);
242 
243 	grassShader->SetUniform("groundShadowDensity", mapInfo->light.groundShadowDensity);
244 	grassShader->SetUniformMatrix4x4("shadowMatrix", false, &shadowHandler->shadowMatrix.m[0]);
245 	grassShader->SetUniform4v("shadowParams", &shadowHandler->GetShadowParams().x);
246 
247 	grassShader->SetUniform3v("ambientLightColor",  &mapInfo->light.unitAmbientColor.x);
248 	grassShader->SetUniform3v("diffuseLightColor",  &mapInfo->light.unitSunColor.x);
249 	grassShader->SetUniform3v("specularLightColor", &mapInfo->light.unitSpecularColor.x);
250 	grassShader->SetUniform3v("sunDir",             &mapInfo->light.sunDir.x);
251 }
252 
253 
GetCamDistOfGrassBlock(const int x,const int y,const bool square=false)254 static float GetCamDistOfGrassBlock(const int x, const int y, const bool square = false)
255 {
256 	float3 quadCenter = float3(x, 0.f, y) * gSSsq;
257 	quadCenter.y = CGround::GetHeightReal(quadCenter.x, quadCenter.z, false);
258 	const float3 dif = camera->GetPos() - quadCenter;
259 	return (square) ? dif.SqLength() : dif.Length();
260 }
261 
262 
GrassSort(const CGrassDrawer::GrassStruct * a,const CGrassDrawer::GrassStruct * b)263 static const bool GrassSort(const CGrassDrawer::GrassStruct* a, const CGrassDrawer::GrassStruct* b) {
264 	const float distA = GetCamDistOfGrassBlock((a->posX + 0.5f) * grassBlockSize, (a->posZ + 0.5f) * grassBlockSize, true);
265 	const float distB = GetCamDistOfGrassBlock((b->posX + 0.5f) * grassBlockSize, (b->posZ + 0.5f) * grassBlockSize, true);
266 	return (distA > distB);
267 }
GrassSortNear(const CGrassDrawer::InviewNearGrass & a,const CGrassDrawer::InviewNearGrass & b)268 static const bool GrassSortNear(const CGrassDrawer::InviewNearGrass& a, const CGrassDrawer::InviewNearGrass& b) { return (a.dist > b.dist); }
269 
270 //////////////////////////////////////////////////////////////////////////////////////////////////
271 //////////////////////////////////////////////////////////////////////////////////////////////////
272 /// CGrassBlockDrawer
273 
274 class CGrassBlockDrawer: public CReadMap::IQuadDrawer
275 {
276 public:
277 	std::vector<CGrassDrawer::InviewNearGrass> inviewGrass;
278 	std::vector<CGrassDrawer::InviewNearGrass> inviewNearGrass;
279 	std::vector<CGrassDrawer::GrassStruct*>    inviewFarGrass;
280 	int cx, cy;
281 	CGrassDrawer* gd;
282 
283 	void DrawQuad(int x, int y);
284 
285 private:
286 	void DrawDetailQuad(const int x, const int y);
287 	void DrawFarQuad(const int x, const int y);
288 };
289 
290 
291 static CGrassBlockDrawer drawer;
292 
293 
DrawQuad(int x,int y)294 void CGrassBlockDrawer::DrawQuad(int x, int y)
295 {
296 	const float distSq = GetCamDistOfGrassBlock((x + 0.5f) * grassBlockSize, (y + 0.5f) * grassBlockSize, true);
297 	if (distSq > Square(gd->maxGrassDist))
298 		return;
299 
300 	if (abs(x - cx) <= gd->detailedBlocks && abs(y - cy) <= gd->detailedBlocks) {
301 		return DrawDetailQuad(x, y);
302 	}
303 	DrawFarQuad(x, y);
304 }
305 
306 
DrawDetailQuad(const int x,const int y)307 void CGrassBlockDrawer::DrawDetailQuad(const int x, const int y)
308 {
309 	const float maxDetailedDist = gd->maxDetailedDist;
310 
311 	// blocks close to the camera
312 	for (int y2 = y * grassBlockSize; y2 < (y + 1) * grassBlockSize; ++y2) {
313 		for (int x2 = x * grassBlockSize; x2 < (x + 1) * grassBlockSize; ++x2) {
314 			if (!gd->grassMap[y2 * gs->mapx / grassSquareSize + x2]) {
315 				continue;
316 			}
317 
318 			rng.Seed(y2 * gs->mapx / grassSquareSize + x2);
319 			const float dist  = GetCamDistOfGrassBlock(x2, y2, false);
320 			const float rdist = 1.0f + rng.RandFloat() * 0.5f;
321 
322 			//TODO instead of adding grass turfs depending on their distance to the camera,
323 			//     there should be a fixed sized pool for mesh & billboard turfs
324 			//     and then we fill these pools with _preference_ for close distance turfs.
325 			//     So when a map has only less turfs, render them independent of the cam distance as mesh.
326 			//     -> see Ravaged_2
327 			if (dist < (maxDetailedDist + 128.f * rdist)) {
328 				// close grass (render as mesh)
329 				CGrassDrawer::InviewNearGrass iv;
330 				iv.dist = dist;
331 				iv.x = x2;
332 				iv.y = y2;
333 				inviewGrass.push_back(iv);
334 			}
335 
336 			if (dist > maxDetailedDist) {
337 				// near but not close, save for later drawing
338 				CGrassDrawer::InviewNearGrass iv;
339 				iv.dist = dist;
340 				iv.x = x2;
341 				iv.y = y2;
342 				inviewNearGrass.push_back(iv);
343 			}
344 		}
345 	}
346 }
347 
348 
DrawFarQuad(const int x,const int y)349 void CGrassBlockDrawer::DrawFarQuad(const int x, const int y)
350 {
351 	const int curSquare = y * gd->blocksX + x;
352 	CGrassDrawer::GrassStruct* grass = &gd->grass[curSquare];
353 	grass->lastSeen = globalRendering->drawFrame;
354 	grass->posX = x;
355 	grass->posZ = y;
356 	inviewFarGrass.push_back(grass);
357 }
358 
359 
360 //////////////////////////////////////////////////////////////////////////////////////////////////
361 //////////////////////////////////////////////////////////////////////////////////////////////////
362 
363 struct STurfParams {
364 	float x, y, rotation;
365 };
366 
367 
GetTurfParams(UnsyncedRNG & rng,const int x,const int y)368 static STurfParams GetTurfParams(UnsyncedRNG& rng, const int x, const int y)
369 {
370 	STurfParams result;
371 	result.x = (x + rng.RandFloat()) * gSSsq;
372 	result.y = (y + rng.RandFloat()) * gSSsq;
373 	result.rotation = rng.RandFloat() * 360.f;
374 	return result;
375 }
376 
377 
378 
DrawNear(const std::vector<InviewNearGrass> & inviewGrass)379 void CGrassDrawer::DrawNear(const std::vector<InviewNearGrass>& inviewGrass)
380 {
381 	for (const InviewNearGrass& g: inviewGrass) {
382 		rng.Seed(g.y * gs->mapx / grassSquareSize + g.x);
383 //		const float distSq = GetCamDistOfGrassBlock(g.x, g.y, true);
384 		const float rdist  = 1.0f + rng.RandFloat() * 0.5f;
385 		const float alpha  = linearstep(maxDetailedDist, maxDetailedDist + 128.f * rdist, g.dist);
386 
387 		for (int a = 0; a < numTurfs; a++) {
388 			const STurfParams& p = GetTurfParams(rng, g.x, g.y);
389 			float3 pos(p.x, CGround::GetHeightReal(p.x, p.y, false), p.y);
390 				pos.y -= CGround::GetSlope(p.x, p.y, false) * 30.0f;
391 				pos.y -= 2.0f * mapInfo->grass.bladeHeight * alpha;
392 
393 			glPushMatrix();
394 			glTranslatef3(pos);
395 			glRotatef(p.rotation, 0.0f, 1.0f, 0.0f);
396 			glCallList(grassDL);
397 			glPopMatrix();
398 		}
399 	}
400 }
401 
402 
DrawBillboard(const int x,const int y,const float dist,VA_TYPE_TN * va_tn)403 void CGrassDrawer::DrawBillboard(const int x, const int y, const float dist, VA_TYPE_TN* va_tn)
404 {
405 	UnsyncedRNG rng; // need our own, cause this function may run threaded
406 	rng.Seed(y * gs->mapx / grassSquareSize + x);
407 	const float rdist  = 1.0f + rng.RandFloat() * 0.5f;
408 	float alpha = 1.0f - linearstep(maxGrassDist,  maxGrassDist + 127.f, dist + 128.f);
409 	alpha = std::min(alpha, linearstep(maxDetailedDist, maxDetailedDist + 128.f * rdist, dist));
410 
411 	for (int a = 0; a < numTurfs; a++) {
412 		const STurfParams p = GetTurfParams(rng, x, y);
413 		float3 pos(p.x, CGround::GetHeightReal(p.x, p.y, false), p.y);
414 			pos.y -= CGround::GetSlope(p.x, p.y, false) * 30.0f;
415 
416 		va_tn[a * 4 + 0] = { pos,         0.0f, 1.0f, float3(-partTurfSize, -partTurfSize, alpha) };
417 		va_tn[a * 4 + 1] = { pos, 1.0f / 16.0f, 1.0f, float3( partTurfSize, -partTurfSize, alpha) };
418 		va_tn[a * 4 + 2] = { pos, 1.0f / 16.0f, 0.0f, float3( partTurfSize,  partTurfSize, alpha) };
419 		va_tn[a * 4 + 3] = { pos,         0.0f, 0.0f, float3(-partTurfSize,  partTurfSize, alpha) };
420 	}
421 }
422 
423 
DrawFarBillboards(const std::vector<GrassStruct * > & inviewFarGrass)424 void CGrassDrawer::DrawFarBillboards(const std::vector<GrassStruct*>& inviewFarGrass)
425 {
426 	// update far grass blocks
427 	if (updateBillboards) {
428 		updateBillboards = false;
429 
430 		for_mt(0, inviewFarGrass.size(), [&](const int i){
431 			GrassStruct& g = *inviewFarGrass[i];
432 			if (!g.va) {
433 				//TODO vertex arrays need to be send each frame to the gpu, that's slow. switch to VBOs.
434 				CVertexArray* va = new CVertexArray;
435 				g.va = va;
436 				g.lastDist = -1; // force a recreate
437 			}
438 
439 			const float distSq = GetCamDistOfGrassBlock((g.posX + 0.5f) * grassBlockSize, (g.posZ + 0.5f) * grassBlockSize, true);
440 			if (distSq == g.lastDist)
441 				return;
442 
443 			bool inAlphaRange1 = (    distSq < Square(maxDetailedDist + 128.f * 1.5f)) || (    distSq > Square(maxGrassDist - 128.f));
444 			bool inAlphaRange2 = (g.lastDist < Square(maxDetailedDist + 128.f * 1.5f)) || (g.lastDist > Square(maxGrassDist - 128.f));
445 			if (!inAlphaRange1 && (inAlphaRange1 == inAlphaRange2)) {
446 				return;
447 			}
448 
449 			g.lastDist = distSq;
450 			CVertexArray* va = g.va;
451 			va->Initialize();
452 
453 			for (int y2 = g.posZ * grassBlockSize; y2 < (g.posZ + 1) * grassBlockSize; ++y2) {
454 				for (int x2 = g.posX * grassBlockSize; x2 < (g.posX  + 1) * grassBlockSize; ++x2) {
455 					if (!grassMap[y2 * gs->mapx / grassSquareSize + x2]) {
456 						continue;
457 					}
458 
459 					const float dist = GetCamDistOfGrassBlock(x2, y2);
460 					auto* va_tn = va->GetTypedVertexArray<VA_TYPE_TN>(numTurfs * 4);
461 					DrawBillboard(x2, y2, dist, va_tn);
462 				}
463 			}
464 		});
465 	}
466 
467 	// render far grass blocks
468 	for (const GrassStruct* g: inviewFarGrass) {
469 		g->va->DrawArrayTN(GL_QUADS);
470 	}
471 }
472 
473 
DrawNearBillboards(const std::vector<InviewNearGrass> & inviewNearGrass)474 void CGrassDrawer::DrawNearBillboards(const std::vector<InviewNearGrass>& inviewNearGrass)
475 {
476 	if (farnearVA->drawIndex() == 0) {
477 		auto* va_tn = farnearVA->GetTypedVertexArray<VA_TYPE_TN>(inviewNearGrass.size() * numTurfs * 4);
478 		for_mt(0, inviewNearGrass.size(), [&](const int i){
479 			const InviewNearGrass& gi = inviewNearGrass[i];
480 			DrawBillboard(gi.x, gi.y, gi.dist, &va_tn[i * numTurfs * 4]);
481 		});
482 	}
483 
484 	farnearVA->DrawArrayTN(GL_QUADS);
485 }
486 
487 
Update()488 void CGrassDrawer::Update()
489 {
490 	// update visible turfs
491 	if (oldCamPos != camera->GetPos() || oldCamDir != camera->forward) {
492 		SCOPED_TIMER("Grass::Update");
493 		oldCamPos = camera->GetPos();
494 		oldCamDir = camera->forward;
495 		lastVisibilityUpdate = globalRendering->drawFrame;
496 
497 		drawer.cx = int(camera->GetPos().x / bMSsq);
498 		drawer.cy = int(camera->GetPos().z / bMSsq);
499 		drawer.inviewGrass.clear();
500 		drawer.inviewFarGrass.clear();
501 		drawer.inviewNearGrass.clear();
502 		drawer.gd = this;
503 		readMap->GridVisibility(camera, blockMapSize, maxGrassDist, &drawer);
504 
505 		if (
506 			globalRendering->haveGLSL
507 			&& (!shadowHandler->shadowsLoaded || !globalRendering->atiHacks) // Ati crashes w/o an error when shadows are enabled!?
508 		) {
509 			std::sort(drawer.inviewFarGrass.begin(), drawer.inviewFarGrass.end(), GrassSort);
510 			std::sort(drawer.inviewNearGrass.begin(), drawer.inviewNearGrass.end(), GrassSortNear);
511 			farnearVA->Initialize();
512 			updateBillboards = true;
513 		}
514 	}
515 
516 	// collect garbage
517 	for (GrassStruct& pGS: grass) {
518 		if ((pGS.lastSeen != lastVisibilityUpdate)
519 		 && (pGS.lastSeen  < globalRendering->drawFrame - 50)
520 		 && pGS.va
521 		) {
522 			ResetPos(pGS.posX, pGS.posZ);
523 		}
524 	}
525 }
526 
527 
Draw()528 void CGrassDrawer::Draw()
529 {
530 	if (grassOff || !readMap->GetGrassShadingTexture())
531 		return;
532 
533 	SCOPED_TIMER("Grass::Draw");
534 	glPushAttrib(GL_CURRENT_BIT);
535 	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
536 
537 	if (!drawer.inviewGrass.empty()) {
538 		SetupGlStateNear();
539 			DrawNear(drawer.inviewGrass);
540 		ResetGlStateNear();
541 	}
542 
543 	if (
544 		globalRendering->haveGLSL
545 		&& (!shadowHandler->shadowsLoaded || !globalRendering->atiHacks) // Ati crashes w/o an error when shadows are enabled!?
546 		&& !(drawer.inviewFarGrass.empty() && drawer.inviewNearGrass.empty())
547 	) {
548 		SetupGlStateFar();
549 			DrawFarBillboards(drawer.inviewFarGrass);
550 			DrawNearBillboards(drawer.inviewNearGrass);
551 		ResetGlStateFar();
552 	}
553 
554 	glPopAttrib();
555 }
556 
557 
DrawShadow()558 void CGrassDrawer::DrawShadow()
559 {
560 	// Grass self-shadowing doesn't look that good atm
561 /*	if (grassOff || !readMap->GetGrassShadingTexture())
562 		return;
563 
564 	// looks ad with low density grass
565 	//TODO either enable it on high density only, or wait for alpha transparent shadows and use those then
566 	EnableShader(GRASS_PROGRAM_SHADOW_GEN);
567 
568 	glActiveTexture(GL_TEXTURE0);
569 	//glBindTexture(GL_TEXTURE_2D, activeFarTex);
570 	glDisable(GL_TEXTURE_2D);
571 	glDisable(GL_ALPHA_TEST);
572 	glDisable(GL_CULL_FACE);
573 
574 	glPolygonOffset(5, 15);
575 	glEnable(GL_POLYGON_OFFSET_FILL);
576 
577 	// we pass it as uniform and want to have pos & rot
578 	// of the turfs to be saved alone in the modelview matrix
579 	glMatrixMode(GL_MODELVIEW);
580 	glPushMatrix();
581 	glLoadIdentity();
582 
583 	static CGrassBlockDrawer drawer;
584 	drawer.cx = int(camera->GetPos().x / bMSsq);
585 	drawer.cy = int(camera->GetPos().z / bMSsq);
586 	drawer.inviewGrass.clear();
587 	drawer.inviewFarGrass.clear();
588 	drawer.inviewNearGrass.clear();
589 	drawer.gd = this;
590 	readMap->GridVisibility(camera, blockMapSize, maxGrassDist, &drawer);
591 
592 	DrawNear(drawer.inviewGrass);
593 
594 	//FIXME needs own shader!
595 	//DrawNearBillboards(drawer.inviewNearGrass);
596 
597 	glMatrixMode(GL_MODELVIEW);
598 	glPopMatrix();
599 
600 	glEnable(GL_CULL_FACE);
601 	glDisable(GL_POLYGON_OFFSET_FILL);
602 	glDisable(GL_TEXTURE_2D);
603 	glDisable(GL_ALPHA_TEST);
604 
605 	grassShader->Disable();*/
606 }
607 
608 
SetupGlStateNear()609 void CGrassDrawer::SetupGlStateNear()
610 {
611 	CBaseGroundDrawer* gd = readMap->GetGroundDrawer();
612 
613 	// bind textures
614 	{
615 		glActiveTextureARB(GL_TEXTURE0_ARB);
616 			glBindTexture(GL_TEXTURE_2D, grassBladeTex);
617 		glActiveTextureARB(GL_TEXTURE1_ARB);
618 			glBindTexture(GL_TEXTURE_2D, readMap->GetGrassShadingTexture());
619 		glActiveTextureARB(GL_TEXTURE2_ARB);
620 			glBindTexture(GL_TEXTURE_2D, readMap->GetShadingTexture());
621 		if (gd->DrawExtraTex()) {
622 			glActiveTextureARB(GL_TEXTURE3_ARB);
623 				glBindTexture(GL_TEXTURE_2D, gd->GetActiveInfoTexture());
624 		}
625 		glActiveTextureARB(GL_TEXTURE5_ARB);
626 			glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, cubeMapHandler->GetSpecularTextureID());
627 	}
628 
629 	// bind shader
630 	if (globalRendering->haveGLSL) {
631 		EnableShader(GRASS_PROGRAM_NEAR);
632 
633 		if (shadowHandler->shadowsLoaded) {
634 			glActiveTextureARB(GL_TEXTURE4_ARB);
635 				glBindTexture(GL_TEXTURE_2D, shadowHandler->shadowTexture);
636 				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE);
637 				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
638 				glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_ALPHA);
639 		}
640 
641 		glMatrixMode(GL_PROJECTION);
642 			glPushMatrix();
643 			glMultMatrixf(camera->GetViewMatrix());
644 		glMatrixMode(GL_MODELVIEW);
645 			glPushMatrix();
646 			glLoadIdentity();
647 	} else {
648 		// FPP enable textures
649 		glActiveTextureARB(GL_TEXTURE0_ARB);
650 			glEnable(GL_TEXTURE_2D);
651 		glActiveTextureARB(GL_TEXTURE1_ARB);
652 			glEnable(GL_TEXTURE_2D);
653 			glMultiTexCoord4f(GL_TEXTURE1_ARB, 1.0f,1.0f,1.0f,1.0f); // workaround a nvidia bug with TexGen
654 			SetTexGen(1.0f / (gs->mapx * SQUARE_SIZE), 1.0f / (gs->mapy * SQUARE_SIZE), 0.0f, 0.0f);
655 		glActiveTextureARB(GL_TEXTURE2_ARB);
656 			glEnable(GL_TEXTURE_2D);
657 			glMultiTexCoord4f(GL_TEXTURE2_ARB, 1.0f,1.0f,1.0f,1.0f); // workaround a nvidia bug with TexGen
658 			SetTexGen(1.0f / (gs->pwr2mapx * SQUARE_SIZE), 1.0f / (gs->pwr2mapy * SQUARE_SIZE), 0.0f, 0.0f);
659 			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
660 			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
661 			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
662 			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
663 			glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 2);
664 		if (gd->DrawExtraTex()) {
665 			glActiveTextureARB(GL_TEXTURE3_ARB);
666 				glEnable(GL_TEXTURE_2D);
667 				glMultiTexCoord4f(GL_TEXTURE3_ARB, 1.0f,1.0f,1.0f,1.0f); // workaround a nvidia bug with TexGen
668 				SetTexGen(1.0f / (gs->pwr2mapx * SQUARE_SIZE), 1.0f / (gs->pwr2mapy * SQUARE_SIZE), 0.0f, 0.0f);
669 				glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD_SIGNED_ARB);
670 				glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
671 				glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB);
672 				glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_TEXTURE);
673 				glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
674 		}
675 	}
676 
677 	glActiveTextureARB(GL_TEXTURE0_ARB);
678 	glDisable(GL_BLEND);
679 	glDisable(GL_ALPHA_TEST);
680 	glDepthMask(GL_TRUE);
681 	ISky::SetupFog();
682 }
683 
684 
ResetGlStateNear()685 void CGrassDrawer::ResetGlStateNear()
686 {
687 	CBaseGroundDrawer* gd = readMap->GetGroundDrawer();
688 
689 	if (globalRendering->haveGLSL) {
690 		grassShader->Disable();
691 
692 		if (shadowHandler->shadowsLoaded) {
693 			glActiveTextureARB(GL_TEXTURE1_ARB);
694 				glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
695 				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
696 				glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
697 			glActiveTextureARB(GL_TEXTURE0_ARB);
698 		}
699 
700 		glMatrixMode(GL_PROJECTION);
701 		glPopMatrix();
702 		glMatrixMode(GL_MODELVIEW);
703 		glPopMatrix();
704 	} else {
705 		glActiveTextureARB(GL_TEXTURE1_ARB);
706 			glDisable(GL_TEXTURE_2D);
707 			glDisable(GL_TEXTURE_GEN_S);
708 			glDisable(GL_TEXTURE_GEN_T);
709 		glActiveTextureARB(GL_TEXTURE2_ARB);
710 			glDisable(GL_TEXTURE_2D);
711 			glDisable(GL_TEXTURE_GEN_S);
712 			glDisable(GL_TEXTURE_GEN_T);
713 			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
714 			glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1);
715 		if (gd->DrawExtraTex()) {
716 			glActiveTextureARB(GL_TEXTURE3_ARB);
717 				glDisable(GL_TEXTURE_2D);
718 				glDisable(GL_TEXTURE_GEN_S);
719 				glDisable(GL_TEXTURE_GEN_T);
720 				glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
721 		}
722 		glActiveTextureARB(GL_TEXTURE0_ARB);
723 	}
724 
725 	glDisable(GL_TEXTURE_2D);
726 	glEnable(GL_BLEND);
727 }
728 
729 
SetupGlStateFar()730 void CGrassDrawer::SetupGlStateFar()
731 {
732 	assert(globalRendering->haveGLSL);
733 
734 	CBaseGroundDrawer* gd = readMap->GetGroundDrawer();
735 
736 	//glEnable(GL_ALPHA_TEST);
737 	//glAlphaFunc(GL_GREATER, 0.01f);
738 	glEnable(GL_BLEND);
739 	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
740 	glDepthMask(GL_FALSE);
741 
742 	glMatrixMode(GL_PROJECTION);
743 		glPushMatrix();
744 		glMultMatrixf(camera->GetViewMatrix());
745 	glMatrixMode(GL_MODELVIEW);
746 		glPushMatrix();
747 		glLoadIdentity();
748 
749 	EnableShader(GRASS_PROGRAM_DIST);
750 
751 	glActiveTextureARB(GL_TEXTURE0_ARB);
752 		glBindTexture(GL_TEXTURE_2D, farTex);
753 	glActiveTextureARB(GL_TEXTURE1_ARB);
754 		glBindTexture(GL_TEXTURE_2D, readMap->GetGrassShadingTexture());
755 	glActiveTextureARB(GL_TEXTURE2_ARB);
756 		glBindTexture(GL_TEXTURE_2D, readMap->GetShadingTexture());
757 	if (gd->DrawExtraTex()) {
758 		glActiveTextureARB(GL_TEXTURE3_ARB);
759 			glBindTexture(GL_TEXTURE_2D, gd->GetActiveInfoTexture());
760 	}
761 	if (shadowHandler->shadowsLoaded) {
762 		glActiveTextureARB(GL_TEXTURE4_ARB);
763 			glBindTexture(GL_TEXTURE_2D, shadowHandler->shadowTexture);
764 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE);
765 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
766 			glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_ALPHA);
767 	}
768 
769 	glActiveTextureARB(GL_TEXTURE0_ARB);
770 }
771 
772 
ResetGlStateFar()773 void CGrassDrawer::ResetGlStateFar()
774 {
775 	grassShader->Disable();
776 
777 	glMatrixMode(GL_PROJECTION);
778 	glPopMatrix();
779 	glMatrixMode(GL_MODELVIEW);
780 	glPopMatrix();
781 
782 	if (shadowHandler->shadowsLoaded) {
783 		glActiveTextureARB(GL_TEXTURE1_ARB);
784 			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
785 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
786 			glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
787 		glActiveTextureARB(GL_TEXTURE0_ARB);
788 	}
789 
790 	glDepthMask(GL_TRUE);
791 	glDisable(GL_ALPHA_TEST);
792 
793 }
794 
795 
CreateGrassDispList(int listNum)796 void CGrassDrawer::CreateGrassDispList(int listNum)
797 {
798 	CVertexArray* va = GetVertexArray();
799 	va->Initialize();
800 	rng.Seed(15);
801 
802 	for (int a = 0; a < strawPerTurf; ++a) {
803 		// draw a single blade
804 		const float lngRnd = rng.RandFloat();
805 		const float length = mapInfo->grass.bladeHeight * (1.0f + lngRnd);
806 		const float maxAng = mapInfo->grass.bladeAngle * std::max(rng.RandFloat(), 1.f - smoothstep(0.f,1.f,lngRnd));
807 
808 		float3 sideVect(rng.RandFloat() - 0.5f, 0.0f, rng.RandFloat() - 0.5f);
809 		sideVect.ANormalize();
810 		float3 bendVect = sideVect.cross(UpVector); // direction to bend into
811 		sideVect *= mapInfo->grass.bladeWidth * (-0.15f * lngRnd + 1.0f);
812 		const float3 basePos = rng.RandVector2D() * (turfSize - (bendVect * std::sin(maxAng) * length).Length2D());
813 
814 		// select one of the 16 color shadings
815 		const float xtexCoord = (rng.RandInt() % 16) / 16.0f;
816 		const int numSections = 2 + int(maxAng * 1.2f + length * 0.2f);
817 
818 		float3 normalBend = -bendVect;
819 
820 		// start btm
821 		va->AddVertexTN(basePos + sideVect - float3(0.0f, 3.0f, 0.0f), xtexCoord              , 0.f, normalBend);
822 		va->AddVertexTN(basePos - sideVect - float3(0.0f, 3.0f, 0.0f), xtexCoord + (1.0f / 16), 0.f, normalBend);
823 
824 		for (float h = 0.0f; h < 1.0f; h += (1.0f / numSections)) {
825 			const float ang = maxAng * h;
826 			const float3 n = (normalBend * std::cos(ang) + UpVector * std::sin(ang)).ANormalize();
827 			const float3 edgePos  = (UpVector * std::cos(ang) + bendVect * std::sin(ang)) * length * h;
828 			const float3 edgePosL = edgePos - sideVect * (1.0f - h);
829 			const float3 edgePosR = edgePos + sideVect * (1.0f - h);
830 
831 			va->AddVertexTN(basePos + edgePosR, xtexCoord + (1.0f / 32) * h              , h, (n + sideVect * 0.04f).ANormalize());
832 			va->AddVertexTN(basePos + edgePosL, xtexCoord - (1.0f / 32) * h + (1.0f / 16), h, (n - sideVect * 0.04f).ANormalize());
833 		}
834 
835 		// end top tip (single triangle)
836 		const float3 edgePos = (UpVector * std::cos(maxAng) + bendVect * std::sin(maxAng)) * length;
837 		const float3 n = (normalBend * std::cos(maxAng) + UpVector * std::sin(maxAng)).ANormalize();
838 		va->AddVertexTN(basePos + edgePos, xtexCoord + (1.0f / 32), 1.0f, n);
839 
840 		// next blade
841 		va->EndStrip();
842 	}
843 
844 	glNewList(listNum, GL_COMPILE);
845 	va->DrawArrayTN(GL_TRIANGLE_STRIP);
846 	glEndList();
847 }
848 
CreateGrassBladeTex(unsigned char * buf)849 void CGrassDrawer::CreateGrassBladeTex(unsigned char* buf)
850 {
851 	float3 redish = float3(0.95f, 0.70f, 0.4f);
852 	float3 col = mix(mapInfo->grass.color, redish, 0.1f * rng.RandFloat());
853 	col.x = Clamp(col.x, 0.f, 1.f);
854 	col.y = Clamp(col.y, 0.f, 1.f);
855 	col.z = Clamp(col.z, 0.f, 1.f);
856 
857 	SColor* img = reinterpret_cast<SColor*>(buf);
858 	for (int y=0; y<64; ++y) {
859 		for (int x=0; x<16; ++x) {
860 			const float brightness = smoothstep(-0.8f, 0.5f, y/63.0f) + ((x%2) == 0 ? 0.035f : 0.0f);
861 			const float3 c = col * brightness;
862 			img[y*256+x] = SColor(c.r, c.g, c.b, 1.0f);
863 		}
864 	}
865 }
866 
CreateFarTex()867 void CGrassDrawer::CreateFarTex()
868 {
869 	//TODO create normalmap, too?
870 	const int sizeMod = 2;
871 	const int billboardSize = 256;
872 	const int numAngles = 16;
873 	const int texSizeX = billboardSize * numAngles;
874 	const int texSizeY = billboardSize;
875 
876 	if (farTex == 0) {
877 		glGenTextures(1, &farTex);
878 		glBindTexture(GL_TEXTURE_2D, farTex);
879 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
880 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
881 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
882 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
883 		glSpringTexStorage2D(GL_TEXTURE_2D, -1, GL_RGBA8, texSizeX, texSizeY);
884 	}
885 
886 	FBO fboTex;
887 	fboTex.Bind();
888 	fboTex.AttachTexture(farTex);
889 	fboTex.CheckStatus("GRASSDRAWER1");
890 
891 	FBO fbo;
892 	fbo.Bind();
893 	fbo.CreateRenderBuffer(GL_DEPTH_ATTACHMENT_EXT, GL_DEPTH_COMPONENT16, texSizeX * sizeMod, texSizeY * sizeMod);
894 	fbo.CreateRenderBuffer(GL_COLOR_ATTACHMENT0_EXT, GL_RGBA8, texSizeX * sizeMod, texSizeY * sizeMod);
895 	fbo.CheckStatus("GRASSDRAWER2");
896 
897 	if (!fboTex.IsValid() || !fbo.IsValid()) {
898 		grassOff = true;
899 		return;
900 	}
901 
902 	glPushMatrix();
903 	glLoadIdentity();
904 	glMatrixMode(GL_PROJECTION);
905 	glPushMatrix();
906 
907 	glDisable(GL_FOG);
908 	glDisable(GL_BLEND);
909 	glDisable(GL_ALPHA_TEST);
910 	glBindTexture(GL_TEXTURE_2D, grassBladeTex);
911 	glEnable(GL_TEXTURE_2D);
912 	glEnable(GL_CLIP_PLANE0);
913 	glEnable(GL_DEPTH_TEST);
914 	glDepthMask(GL_TRUE);
915 	glColor4f(1,1,1,1);
916 
917 	glViewport(0,0,texSizeX*sizeMod, texSizeY*sizeMod);
918 	glClearColor(mapInfo->grass.color.r,mapInfo->grass.color.g,mapInfo->grass.color.b,0.f);
919 	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
920 	glClearColor(0.f,0.f,0.f,0.f);
921 
922 	static const GLdouble eq[4] = {0.f, 1.f, 0.f, 0.f};
923 
924 	// render turf from different vertical angles
925 	for (int a=0;a<numAngles;++a) {
926 		glViewport(a*billboardSize*sizeMod, 0, billboardSize*sizeMod, billboardSize*sizeMod);
927 		glMatrixMode(GL_MODELVIEW);
928 			glLoadIdentity();
929 			glRotatef(a*90.f/(numAngles-1),1,0,0);
930 			//glTranslatef(0,-0.5f,0);
931 		glMatrixMode(GL_PROJECTION);
932 			glLoadIdentity();
933 			glOrtho(-partTurfSize, partTurfSize, partTurfSize, -partTurfSize, -turfSize, turfSize);
934 
935 		// has to be applied after the matrix transformations,
936 		// cause it uses those an `compiles` them into the clip plane
937 		glClipPlane(GL_CLIP_PLANE0, &eq[0]);
938 
939 		glCallList(grassDL);
940 	}
941 
942 	glDisable(GL_CLIP_PLANE0);
943 
944 	// scale down the rendered fartextures (MSAA) and write to the final texture
945 	glBindFramebufferEXT(GL_READ_FRAMEBUFFER, fbo.fboId);
946 	glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, fboTex.fboId);
947 	glBlitFramebufferEXT(0, 0, texSizeX*sizeMod, texSizeY*sizeMod,
948 		0, 0, texSizeX, texSizeY,
949 		GL_COLOR_BUFFER_BIT, GL_LINEAR);
950 
951 	// compute mipmaps
952 	glBindTexture(GL_TEXTURE_2D, farTex);
953 	glGenerateMipmap(GL_TEXTURE_2D);
954 
955 	// blur non-rendered areas, so in mipmaps color data isn't blurred with background color
956 	{
957 		const int mipLevels = std::ceil(std::log(std::max(texSizeX, texSizeY) + 1));
958 
959 		glMatrixMode(GL_MODELVIEW);
960 			glLoadIdentity();
961 		glMatrixMode(GL_PROJECTION);
962 			glLoadIdentity();
963 
964 		glEnable(GL_BLEND);
965 		glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA, GL_ZERO, GL_DST_ALPHA);
966 
967 		// copy each mipmap to its predecessor background
968 		// -> fill background with blurred color data
969 		fboTex.Bind();
970 		for (int mipLevel = mipLevels - 2; mipLevel >= 0; --mipLevel) {
971 			fboTex.AttachTexture(farTex, GL_TEXTURE_2D, GL_COLOR_ATTACHMENT0_EXT, mipLevel);
972 			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, mipLevel + 1.f);
973 			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, mipLevel + 1.f);
974 			glViewport(0, 0, texSizeX>>mipLevel, texSizeY>>mipLevel);
975 
976 			CVertexArray* va = GetVertexArray();
977 			va->Initialize();
978 				va->AddVertexT(float3(-1.0f,  1.0f, 0.0f), 0.0f, 1.0f);
979 				va->AddVertexT(float3( 1.0f,  1.0f, 0.0f), 1.0f, 1.0f);
980 				va->AddVertexT(float3( 1.0f, -1.0f, 0.0f), 1.0f, 0.0f);
981 				va->AddVertexT(float3(-1.0f, -1.0f, 0.0f), 0.0f, 0.0f);
982 			va->DrawArrayT(GL_QUADS);
983 		}
984 
985 		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
986 
987 		// recreate mipmaps from now blurred base level
988 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, -1000.f);
989 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD,  1000.f);
990 		glGenerateMipmap(GL_TEXTURE_2D);
991 	}
992 
993 	glViewport(globalRendering->viewPosX, 0, globalRendering->viewSizeX, globalRendering->viewSizeY);
994 	glMatrixMode(GL_PROJECTION);
995 	glPopMatrix();
996 	glMatrixMode(GL_MODELVIEW);
997 	glPopMatrix();
998 
999 	FBO::Unbind();
1000 	//glSaveTexture(farTex, "grassfar.png");
1001 }
1002 
1003 
ResetPos(const int grassBlockX,const int grassBlockZ)1004 void CGrassDrawer::ResetPos(const int grassBlockX, const int grassBlockZ)
1005 {
1006 	if (grassOff)
1007 		return;
1008 
1009 	assert(grassBlockX >= 0 && grassBlockX < blocksX);
1010 	assert(grassBlockZ >= 0 && grassBlockZ < blocksY);
1011 
1012 	GrassStruct& gb = grass[grassBlockZ * blocksX + grassBlockX];
1013 	delete gb.va;
1014 	gb.va = nullptr;
1015 
1016 	updateBillboards = true;
1017 }
1018 
1019 
ResetPos(const float3 & pos)1020 void CGrassDrawer::ResetPos(const float3& pos)
1021 {
1022 	ResetPos(pos.x / bMSsq, pos.z / bMSsq);
1023 }
1024 
1025 
AddGrass(const float3 & pos)1026 void CGrassDrawer::AddGrass(const float3& pos)
1027 {
1028 	if (grassOff)
1029 		return;
1030 
1031 	const int x = int(pos.x) / (SQUARE_SIZE * grassSquareSize);
1032 	const int z = int(pos.z) / (SQUARE_SIZE * grassSquareSize);
1033 	assert(x >= 0 && x < (gs->mapx / grassSquareSize));
1034 	assert(z >= 0 && z < (gs->mapy / grassSquareSize));
1035 
1036 	grassMap[z * gs->mapx / grassSquareSize + x] = 1;
1037 	ResetPos(pos);
1038 }
1039 
1040 
RemoveGrass(const float3 & pos)1041 void CGrassDrawer::RemoveGrass(const float3& pos)
1042 {
1043 	if (grassOff)
1044 		return;
1045 
1046 	const int x = int(pos.x) / (SQUARE_SIZE * grassSquareSize);
1047 	const int z = int(pos.z) / (SQUARE_SIZE * grassSquareSize);
1048 	assert(x >= 0 && x < (gs->mapx / grassSquareSize));
1049 	assert(z >= 0 && z < (gs->mapy / grassSquareSize));
1050 
1051 	grassMap[z * gs->mapx / grassSquareSize + x] = 0;
1052 	ResetPos(pos);
1053 }
1054 
1055 
UnsyncedHeightMapUpdate(const SRectangle & rect)1056 void CGrassDrawer::UnsyncedHeightMapUpdate(const SRectangle& rect)
1057 {
1058 	for (int z = rect.z1; z <= rect.z2; ++z) {
1059 		for (int x = rect.x1; x <= rect.x2; ++x) {
1060 			ResetPos(float3(x, 0.f, z));
1061 		}
1062 	}
1063 }
1064