void CGrassDrawer::DrawFarBillboards(const std::vector<GrassStruct*>& inviewFarGrass) { // update far grass blocks if (updateBillboards) { updateBillboards = false; for_mt(0, inviewFarGrass.size(), [&](const int i){ GrassStruct& g = *inviewFarGrass[i]; if (!g.va) { //TODO vertex arrays need to be send each frame to the gpu, that's slow. switch to VBOs. CVertexArray* va = new CVertexArray; g.va = va; g.lastDist = -1; // force a recreate } const float distSq = GetCamDistOfGrassBlock((g.posX + 0.5f) * grassBlockSize, (g.posZ + 0.5f) * grassBlockSize, true); if (distSq == g.lastDist) return; bool inAlphaRange1 = ( distSq < Square(maxDetailedDist + 128.f * 1.5f)) || ( distSq > Square(maxGrassDist - 128.f)); bool inAlphaRange2 = (g.lastDist < Square(maxDetailedDist + 128.f * 1.5f)) || (g.lastDist > Square(maxGrassDist - 128.f)); if (!inAlphaRange1 && (inAlphaRange1 == inAlphaRange2)) { return; } g.lastDist = distSq; CVertexArray* va = g.va; va->Initialize(); for (int y2 = g.posZ * grassBlockSize; y2 < (g.posZ + 1) * grassBlockSize; ++y2) { for (int x2 = g.posX * grassBlockSize; x2 < (g.posX + 1) * grassBlockSize; ++x2) { if (!grassMap[y2 * mapDims.mapx / grassSquareSize + x2]) { continue; } const float dist = GetCamDistOfGrassBlock(x2, y2); auto* va_tn = va->GetTypedVertexArray<VA_TYPE_TN>(numTurfs * 4); DrawBillboard(x2, y2, dist, va_tn); } } }); } // render far grass blocks for (const GrassStruct* g: inviewFarGrass) { g->va->DrawArrayTN(GL_QUADS); } }
void DrawQuad(int x, int y) { const float distSq = GetCamDistOfGrassBlock((x + 0.5f) * grassBlockSize, (y + 0.5f) * grassBlockSize, true); if (distSq > Square(gd->maxGrassDist)) return; if (abs(x - cx) <= gd->detailedBlocks && abs(y - cy) <= gd->detailedBlocks) { return DrawDetailQuad(x, y); } DrawFarQuad(x, y); }
void CGrassBlockDrawer::DrawDetailQuad(const int x, const int y) { const float maxDetailedDist = gd->maxDetailedDist; // blocks close to the camera for (int y2 = y * grassBlockSize; y2 < (y + 1) * grassBlockSize; ++y2) { for (int x2 = x * grassBlockSize; x2 < (x + 1) * grassBlockSize; ++x2) { if (!gd->grassMap[y2 * gs->mapx / grassSquareSize + x2]) { continue; } rng.Seed(y2 * gs->mapx / grassSquareSize + x2); const float dist = GetCamDistOfGrassBlock(x2, y2, false); const float rdist = 1.0f + rng.RandFloat() * 0.5f; //TODO instead of adding grass turfs depending on their distance to the camera, // there should be a fixed sized pool for mesh & billboard turfs // and then we fill these pools with _preference_ for close distance turfs. // So when a map has only less turfs, render them independent of the cam distance as mesh. // -> see Ravaged_2 if (dist < (maxDetailedDist + 128.f * rdist)) { // close grass (render as mesh) CGrassDrawer::InviewNearGrass iv; iv.dist = dist; iv.x = x2; iv.y = y2; inviewGrass.push_back(iv); } if (dist > maxDetailedDist) { // near but not close, save for later drawing CGrassDrawer::InviewNearGrass iv; iv.dist = dist; iv.x = x2; iv.y = y2; inviewNearGrass.push_back(iv); } } } }
static const bool GrassSort(const CGrassDrawer::GrassStruct* a, const CGrassDrawer::GrassStruct* b) { const float distA = GetCamDistOfGrassBlock((a->posX + 0.5f) * grassBlockSize, (a->posZ + 0.5f) * grassBlockSize, true); const float distB = GetCamDistOfGrassBlock((b->posX + 0.5f) * grassBlockSize, (b->posZ + 0.5f) * grassBlockSize, true); return (distA > distB); }