void CGrassDrawer::Update() { // grass is never drawn in any special (non-opaque) pass const CCamera* cam = CCamera::GetCamera(CCamera::CAMTYPE_PLAYER); // update visible turfs updateVisibility |= (oldCamPos != cam->GetPos()); updateVisibility |= (oldCamDir != cam->GetDir()); if (updateVisibility) { SCOPED_TIMER("Update::Update::Grass"); oldCamPos = cam->GetPos(); oldCamDir = cam->GetDir(); lastVisibilityUpdate = globalRendering->drawFrame; blockDrawer.ResetState(); blockDrawer.cx = int(cam->GetPos().x / BMSSQ); blockDrawer.cy = int(cam->GetPos().z / BMSSQ); blockDrawer.gd = this; readMap->GridVisibility(nullptr, &blockDrawer, maxGrassDist, blockMapSize); // ATI crashes w/o an error when shadows are enabled!? static const bool shaders = globalRendering->haveGLSL; const bool shadows = (shadowHandler->ShadowsLoaded() && globalRendering->atiHacks); if (shaders && !shadows) { std::sort(blockDrawer.inviewFarGrass.begin(), blockDrawer.inviewFarGrass.end(), GrassSort); std::sort(blockDrawer.inviewNearGrass.begin(), blockDrawer.inviewNearGrass.end(), GrassSortNear); farnearVA.Initialize(); updateBillboards = true; } updateVisibility = false; } // collect garbage // originally, this deleted the billboard VA of any patch that was not drawn for 50 frames // now it only resets lastFar s.t. patches are forcibly recreated when they become visible // again (reusing memory) // pass negative coordinates since we do not want to set updateVisibility during this step for (const GrassStruct& gs: grass) { if ((gs.lastSeen != lastVisibilityUpdate) && (gs.lastSeen < globalRendering->drawFrame - 50) && gs.lastFar != 0) { ResetPos(-gs.posX, -gs.posZ); } } }
void CGrassDrawer::Update() { // update visible turfs if (oldCamPos != camera->GetPos() || oldCamDir != camera->GetDir()) { SCOPED_TIMER("Grass::Update"); oldCamPos = camera->GetPos(); oldCamDir = camera->GetDir(); lastVisibilityUpdate = globalRendering->drawFrame; blockDrawer.ResetState(); blockDrawer.cx = int(camera->GetPos().x / bMSsq); blockDrawer.cy = int(camera->GetPos().z / bMSsq); blockDrawer.gd = this; readMap->GridVisibility(camera, blockMapSize, maxGrassDist, &blockDrawer); if ( globalRendering->haveGLSL && (!shadowHandler->shadowsLoaded || !globalRendering->atiHacks) // Ati crashes w/o an error when shadows are enabled!? ) { std::sort(blockDrawer.inviewFarGrass.begin(), blockDrawer.inviewFarGrass.end(), GrassSort); std::sort(blockDrawer.inviewNearGrass.begin(), blockDrawer.inviewNearGrass.end(), GrassSortNear); farnearVA->Initialize(); updateBillboards = true; } } // collect garbage for (GrassStruct& pGS: grass) { if ((pGS.lastSeen != lastVisibilityUpdate) && (pGS.lastSeen < globalRendering->drawFrame - 50) && pGS.va ) { ResetPos(pGS.posX, pGS.posZ); } } }
CGrassDrawer::CGrassDrawer() : CEventClient("[GrassDrawer]", 199992, false) , grassOff(false) , blocksX(mapDims.mapx / grassSquareSize / grassBlockSize) , blocksY(mapDims.mapy / grassSquareSize / grassBlockSize) , grassDL(0) , grassBladeTex(0) , farTex(0) , farnearVA(nullptr) , updateBillboards(false) , grassMap(nullptr) { blockDrawer.ResetState(); rng.Seed(15); const int detail = configHandler->GetInt("GrassDetail"); // some ATI drivers crash with grass enabled, default to disabled if ((detail == 0) || ((detail == 7) && globalRendering->haveATI)) { grassOff = true; return; } // needed to create the far tex if (!GLEW_EXT_framebuffer_blit) { grassOff = true; return; } // load grass density from map { MapBitmapInfo grassbm; unsigned char* grassdata = readMap->GetInfoMap("grass", &grassbm); if (!grassdata) { grassOff = true; return; } if (grassbm.width != mapDims.mapx / grassSquareSize || grassbm.height != mapDims.mapy / grassSquareSize) { char b[128]; SNPRINTF(b, sizeof(b), "grass-map has wrong size (%dx%d, should be %dx%d)\n", grassbm.width, grassbm.height, mapDims.mapx / 4, mapDims.mapy / 4); throw std::runtime_error(b); } const int grassMapSize = mapDims.mapx * mapDims.mapy / (grassSquareSize * grassSquareSize); grassMap = new unsigned char[grassMapSize]; memcpy(grassMap, grassdata, grassMapSize); readMap->FreeInfoMap("grass", grassdata); } // create/load blade texture { CBitmap grassBladeTexBM; if (!grassBladeTexBM.Load(mapInfo->grass.bladeTexName)) { // map didn't define a grasstex, so generate one grassBladeTexBM.channels = 4; grassBladeTexBM.Alloc(256,64); for (int a = 0; a < 16; ++a) { CreateGrassBladeTex(&grassBladeTexBM.mem[a * 16 * 4]); } } //grassBladeTexBM.Save("blade.png", false); grassBladeTex = grassBladeTexBM.CreateTexture(true); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } // create shaders * finalize grass.resize(blocksX * blocksY); farnearVA = new CVertexArray; grassDL = glGenLists(1); ChangeDetail(detail); LoadGrassShaders(); configHandler->NotifyOnChange(this); // eventclient autoLinkEvents = true; RegisterLinkedEvents(this); eventHandler.AddClient(this); }