void CPatchRData::RenderPriorities(CTextRenderer& textRenderer) { CTerrain* terrain = m_Patch->m_Parent; CCamera* camera = g_Game->GetView()->GetCamera(); for (ssize_t j = 0; j < PATCH_SIZE; ++j) { for (ssize_t i = 0; i < PATCH_SIZE; ++i) { ssize_t gx = m_Patch->m_X * PATCH_SIZE + i; ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j; CVector3D pos; terrain->CalcPosition(gx, gz, pos); // Move a bit towards the center of the tile pos.X += TERRAIN_TILE_SIZE/4.f; pos.Z += TERRAIN_TILE_SIZE/4.f; float x, y; camera->GetScreenCoordinates(pos, x, y); textRenderer.PrintfAt(x, y, L"%d", m_Patch->m_MiniPatches[j][i].Priority); } } }
void CPatchRData::RenderOutline() { CTerrain* terrain = m_Patch->m_Parent; ssize_t gx = m_Patch->m_X * PATCH_SIZE; ssize_t gz = m_Patch->m_Z * PATCH_SIZE; CVector3D pos; std::vector<CVector3D> line; ssize_t i, j; for (i = 0, j = 0; i <= PATCH_SIZE; ++i) { terrain->CalcPosition(gx + i, gz + j, pos); line.push_back(pos); } for (i = PATCH_SIZE, j = 1; j <= PATCH_SIZE; ++j) { terrain->CalcPosition(gx + i, gz + j, pos); line.push_back(pos); } for (i = PATCH_SIZE-1, j = PATCH_SIZE; i >= 0; --i) { terrain->CalcPosition(gx + i, gz + j, pos); line.push_back(pos); } for (i = 0, j = PATCH_SIZE-1; j >= 0; --j) { terrain->CalcPosition(gx + i, gz + j, pos); line.push_back(pos); } #if CONFIG2_GLES #warning TODO: implement CPatchRData::RenderOutlines for GLES #else glVertexPointer(3, GL_FLOAT, sizeof(CVector3D), &line[0]); glDrawArrays(GL_LINE_STRIP, 0, line.size()); #endif }
void CPatchRData::BuildSide(std::vector<SSideVertex>& vertices, CPatchSideFlags side) { ssize_t vsize = PATCH_SIZE + 1; CTerrain* terrain = m_Patch->m_Parent; CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY); for (ssize_t k = 0; k < vsize; k++) { ssize_t gx = m_Patch->m_X * PATCH_SIZE; ssize_t gz = m_Patch->m_Z * PATCH_SIZE; switch (side) { case CPATCH_SIDE_NEGX: gz += k; break; case CPATCH_SIDE_POSX: gx += PATCH_SIZE; gz += PATCH_SIZE-k; break; case CPATCH_SIDE_NEGZ: gx += PATCH_SIZE-k; break; case CPATCH_SIDE_POSZ: gz += PATCH_SIZE; gx += k; break; } CVector3D pos; terrain->CalcPosition(gx, gz, pos); // Clamp the height to the water level float waterHeight = 0.f; if (cmpWaterManager) waterHeight = cmpWaterManager->GetExactWaterLevel(pos.X, pos.Z); pos.Y = std::max(pos.Y, waterHeight); SSideVertex v0, v1; v0.m_Position = pos; v1.m_Position = pos; v1.m_Position.Y = 0; // If this is the start of this tristrip, but we've already got a partial // tristrip, add a couple of degenerate triangles to join the strips properly if (k == 0 && !vertices.empty()) { vertices.push_back(vertices.back()); vertices.push_back(v1); } // Now add the new triangles vertices.push_back(v1); vertices.push_back(v0); } }
void CPatchRData::AddBlend(std::vector<SBlendVertex>& blendVertices, std::vector<u16>& blendIndices, u16 i, u16 j, u8 shape, CTerrainTextureEntry* texture) { CTerrain* terrain = m_Patch->m_Parent; ssize_t gx = m_Patch->m_X * PATCH_SIZE + i; ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j; // uses the current neighbour texture BlendShape8 shape8; for (size_t m = 0; m < 8; ++m) shape8[m] = (shape & (1 << m)) ? 0 : 1; // calculate the required alphamap and the required rotation of the alphamap from blendshape unsigned int alphamapflags; int alphamap = CAlphaMapCalculator::Calculate(shape8, alphamapflags); // now actually render the blend tile (if we need one) if (alphamap == -1) return; float u0 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].u0; float u1 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].u1; float v0 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].v0; float v1 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].v1; if (alphamapflags & BLENDMAP_FLIPU) std::swap(u0, u1); if (alphamapflags & BLENDMAP_FLIPV) std::swap(v0, v1); int base = 0; if (alphamapflags & BLENDMAP_ROTATE90) base = 1; else if (alphamapflags & BLENDMAP_ROTATE180) base = 2; else if (alphamapflags & BLENDMAP_ROTATE270) base = 3; SBlendVertex vtx[4]; vtx[(base + 0) % 4].m_AlphaUVs[0] = u0; vtx[(base + 0) % 4].m_AlphaUVs[1] = v0; vtx[(base + 1) % 4].m_AlphaUVs[0] = u1; vtx[(base + 1) % 4].m_AlphaUVs[1] = v0; vtx[(base + 2) % 4].m_AlphaUVs[0] = u1; vtx[(base + 2) % 4].m_AlphaUVs[1] = v1; vtx[(base + 3) % 4].m_AlphaUVs[0] = u0; vtx[(base + 3) % 4].m_AlphaUVs[1] = v1; SBlendVertex dst; const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); CVector3D normal; bool cpuLighting = (g_Renderer.GetRenderPath() == CRenderer::RP_FIXED); size_t index = blendVertices.size(); terrain->CalcPosition(gx, gz, dst.m_Position); terrain->CalcNormal(gx, gz, normal); dst.m_Normal = normal; dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal); dst.m_AlphaUVs[0] = vtx[0].m_AlphaUVs[0]; dst.m_AlphaUVs[1] = vtx[0].m_AlphaUVs[1]; blendVertices.push_back(dst); terrain->CalcPosition(gx + 1, gz, dst.m_Position); terrain->CalcNormal(gx + 1, gz, normal); dst.m_Normal = normal; dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal); dst.m_AlphaUVs[0] = vtx[1].m_AlphaUVs[0]; dst.m_AlphaUVs[1] = vtx[1].m_AlphaUVs[1]; blendVertices.push_back(dst); terrain->CalcPosition(gx + 1, gz + 1, dst.m_Position); terrain->CalcNormal(gx + 1, gz + 1, normal); dst.m_Normal = normal; dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal); dst.m_AlphaUVs[0] = vtx[2].m_AlphaUVs[0]; dst.m_AlphaUVs[1] = vtx[2].m_AlphaUVs[1]; blendVertices.push_back(dst); terrain->CalcPosition(gx, gz + 1, dst.m_Position); terrain->CalcNormal(gx, gz + 1, normal); dst.m_Normal = normal; dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal); dst.m_AlphaUVs[0] = vtx[3].m_AlphaUVs[0]; dst.m_AlphaUVs[1] = vtx[3].m_AlphaUVs[1]; blendVertices.push_back(dst); bool dir = terrain->GetTriangulationDir(gx, gz); if (dir) { blendIndices.push_back(index+0); blendIndices.push_back(index+1); blendIndices.push_back(index+3); blendIndices.push_back(index+1); blendIndices.push_back(index+2); blendIndices.push_back(index+3); } else { blendIndices.push_back(index+0); blendIndices.push_back(index+1); blendIndices.push_back(index+2); blendIndices.push_back(index+2); blendIndices.push_back(index+3); blendIndices.push_back(index+0); } }
// Build vertex buffer for water vertices over our patch void CPatchRData::BuildWater() { PROFILE3("build water"); // number of vertices in each direction in each patch ENSURE((PATCH_SIZE % water_cell_size) == 0); if (m_VBWater) { g_VBMan.Release(m_VBWater); m_VBWater = 0; } if (m_VBWaterIndices) { g_VBMan.Release(m_VBWaterIndices); m_VBWaterIndices = 0; } m_WaterBounds.SetEmpty(); // We need to use this to access the water manager or we may not have the // actual values but some compiled-in defaults CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY); if (!cmpWaterManager) return; // Build data for water std::vector<SWaterVertex> water_vertex_data; std::vector<GLushort> water_indices; u16 water_index_map[PATCH_SIZE+1][PATCH_SIZE+1]; memset(water_index_map, 0xFF, sizeof(water_index_map)); // TODO: This is not (yet) exported via the ICmp interface so... we stick to these values which can be compiled in defaults WaterManager* WaterMgr = g_Renderer.GetWaterManager(); if (WaterMgr->m_NeedsFullReloading && !g_AtlasGameLoop->running) { WaterMgr->m_NeedsFullReloading = false; WaterMgr->CreateSuperfancyInfo(m_Simulation); } CPatch* patch = m_Patch; CTerrain* terrain = patch->m_Parent; ssize_t mapSize = (size_t)terrain->GetVerticesPerSide(); ssize_t x1 = m_Patch->m_X*PATCH_SIZE; ssize_t z1 = m_Patch->m_Z*PATCH_SIZE; // build vertices, uv, and shader varying for (ssize_t z = 0; z < PATCH_SIZE; z += water_cell_size) { for (ssize_t x = 0; x <= PATCH_SIZE; x += water_cell_size) { // Check that the edge at x is partially underwater float startTerrainHeight[2] = { terrain->GetVertexGroundLevel(x+x1, z+z1), terrain->GetVertexGroundLevel(x+x1, z+z1 + water_cell_size) }; float startWaterHeight[2] = { cmpWaterManager->GetExactWaterLevel(x+x1, z+z1), cmpWaterManager->GetExactWaterLevel(x+x1, z+z1 + water_cell_size) }; if (startTerrainHeight[0] >= startWaterHeight[0] && startTerrainHeight[1] >= startWaterHeight[1]) continue; // Move x back one cell (unless at start of patch), then scan rightwards bool belowWater = true; ssize_t stripStart; for (stripStart = x = std::max(x-water_cell_size, (ssize_t)0); x <= PATCH_SIZE; x += water_cell_size) { // If this edge is not underwater, and neither is the previous edge // (i.e. belowWater == false), then stop this strip since we've reached // a cell that's entirely above water float terrainHeight[2] = { terrain->GetVertexGroundLevel(x+x1, z+z1), terrain->GetVertexGroundLevel(x+x1, z+z1 + water_cell_size) }; float waterHeight[2] = { cmpWaterManager->GetExactWaterLevel(x+x1, z+z1), cmpWaterManager->GetExactWaterLevel(x+x1, z+z1 + water_cell_size) }; if (terrainHeight[0] >= waterHeight[0] && terrainHeight[1] >= waterHeight[1]) { if (!belowWater) break; belowWater = false; } else belowWater = true; // Edge (x,z)-(x,z+1) is at least partially underwater, so extend the water plane strip across it // Compute vertex data for the 2 points on the edge for (int j = 0; j < 2; j++) { // Check if we already computed this vertex from an earlier strip if (water_index_map[z+j*water_cell_size][x] != 0xFFFF) continue; SWaterVertex vertex; terrain->CalcPosition(x+x1, z+z1 + j*water_cell_size, vertex.m_Position); float depth = waterHeight[j] - vertex.m_Position.Y; vertex.m_Position.Y = waterHeight[j]; m_WaterBounds += vertex.m_Position; // NB: Usually this factor is view dependent, but for performance reasons // we do not take it into account with basic non-shader based water. // Average constant Fresnel effect for non-fancy water float alpha = clamp(depth / WaterMgr->m_WaterFullDepth + WaterMgr->m_WaterAlphaOffset, WaterMgr->m_WaterAlphaOffset, WaterMgr->m_WaterMaxAlpha); // Split the depth data across 24 bits, so the fancy-water shader can reconstruct // the depth value while the simple-water can just use the precomputed alpha float depthInt = floor(depth); float depthFrac = depth - depthInt; vertex.m_DepthData = SColor4ub( u8(clamp(depthInt, 0.0f, 255.0f)), u8(clamp(-depthInt, 0.0f, 255.0f)), u8(clamp(depthFrac*255.0f, 0.0f, 255.0f)), u8(clamp(alpha*255.0f, 0.0f, 255.0f))); int tx = x+x1; int ty = z+z1 + j*water_cell_size; if (g_AtlasGameLoop->running) { // currently no foam is used so push whatever vertex.m_WaterData = CVector4D(0.0f,0.0f,0.0f,0.0f); } else { vertex.m_WaterData = CVector4D(WaterMgr->m_WaveX[tx + ty*mapSize], WaterMgr->m_WaveZ[tx + ty*mapSize], WaterMgr->m_DistanceToShore[tx + ty*mapSize], WaterMgr->m_FoamFactor[tx + ty*mapSize]); } water_index_map[z+j*water_cell_size][x] = water_vertex_data.size(); water_vertex_data.push_back(vertex); } // If this was not the first x in the strip, then add a quad // using the computed vertex data if (x <= stripStart) continue; water_indices.push_back(water_index_map[z + water_cell_size][x - water_cell_size]); water_indices.push_back(water_index_map[z][x - water_cell_size]); water_indices.push_back(water_index_map[z][x]); water_indices.push_back(water_index_map[z + water_cell_size][x]); } } } // no vertex buffers if no data generated if (water_indices.size() == 0) return; // allocate vertex buffer m_VBWater = g_VBMan.Allocate(sizeof(SWaterVertex), water_vertex_data.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER); m_VBWater->m_Owner->UpdateChunkVertices(m_VBWater, &water_vertex_data[0]); // Construct indices buffer m_VBWaterIndices = g_VBMan.Allocate(sizeof(GLushort), water_indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER); m_VBWaterIndices->m_Owner->UpdateChunkVertices(m_VBWaterIndices, &water_indices[0]); }