Exemple #1
0
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);
}