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