CStr8 CTerrain::GetMovementClass(ssize_t i, ssize_t j) const { CMiniPatch* tile = GetTile(i, j); if (tile && tile->GetTextureEntry()) return tile->GetTextureEntry()->GetProperties().GetMovementClass(); return "default"; }
void CMiniMap::RebuildTerrainTexture() { u32 x = 0; u32 y = 0; u32 w = m_MapSize - 1; u32 h = m_MapSize - 1; float waterHeight = g_Renderer.GetWaterManager()->m_WaterHeight; m_TerrainDirty = false; for(u32 j = 0; j < h; j++) { u32 *dataPtr = m_TerrainData + ((y + j) * (m_MapSize - 1)) + x; for(u32 i = 0; i < w; i++) { float avgHeight = ( m_Terrain->GetVertexGroundLevel((int)i, (int)j) + m_Terrain->GetVertexGroundLevel((int)i+1, (int)j) + m_Terrain->GetVertexGroundLevel((int)i, (int)j+1) + m_Terrain->GetVertexGroundLevel((int)i+1, (int)j+1) ) / 4.0f; if(avgHeight < waterHeight) { *dataPtr++ = 0xff304080; // TODO: perhaps use the renderer's water color? } else { int hmap = ((int)m_Terrain->GetHeightMap()[(y + j) * m_MapSize + x + i]) >> 8; int val = (hmap / 3) + 170; u32 color = 0xFFFFFFFF; CMiniPatch *mp = m_Terrain->GetTile(x + i, y + j); if(mp) { CTerrainTextureEntry *tex = mp->GetTextureEntry(); if(tex) { // If the texture can't be loaded yet, set the dirty flags // so we'll try regenerating the terrain texture again soon if(!tex->GetTexture()->TryLoad()) m_TerrainDirty = true; color = tex->GetBaseColor(); } } *dataPtr++ = ScaleColor(color, float(val) / 255.0f); } } } // Upload the texture g_Renderer.BindTexture(0, m_TerrainTexture); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize - 1, m_MapSize - 1, GL_BGRA_EXT, GL_UNSIGNED_BYTE, m_TerrainData); }
void CMiniMap::RebuildTerrainTexture() { u32 x = 0; u32 y = 0; u32 w = m_MapSize - 1; u32 h = m_MapSize - 1; float waterHeight = g_Renderer.GetWaterManager()->m_WaterHeight; m_TerrainDirty = false; for (u32 j = 0; j < h; ++j) { u32* dataPtr = m_TerrainData + ((y + j) * (m_MapSize - 1)) + x; for (u32 i = 0; i < w; ++i) { float avgHeight = ( m_Terrain->GetVertexGroundLevel((int)i, (int)j) + m_Terrain->GetVertexGroundLevel((int)i+1, (int)j) + m_Terrain->GetVertexGroundLevel((int)i, (int)j+1) + m_Terrain->GetVertexGroundLevel((int)i+1, (int)j+1) ) / 4.0f; if (avgHeight < waterHeight && avgHeight > waterHeight - m_ShallowPassageHeight) { // shallow water *dataPtr++ = 0xffc09870; } else if (avgHeight < waterHeight) { // Set water as constant color for consistency on different maps *dataPtr++ = 0xffa07850; } else { int hmap = ((int)m_Terrain->GetHeightMap()[(y + j) * m_MapSize + x + i]) >> 8; int val = (hmap / 3) + 170; u32 color = 0xFFFFFFFF; CMiniPatch* mp = m_Terrain->GetTile(x + i, y + j); if (mp) { CTerrainTextureEntry* tex = mp->GetTextureEntry(); if (tex) { // If the texture can't be loaded yet, set the dirty flags // so we'll try regenerating the terrain texture again soon if(!tex->GetTexture()->TryLoad()) m_TerrainDirty = true; color = tex->GetBaseColor(); } } *dataPtr++ = ScaleColor(color, float(val) / 255.0f); } } } // Upload the texture g_Renderer.BindTexture(0, m_TerrainTexture); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize - 1, m_MapSize - 1, GL_RGBA, GL_UNSIGNED_BYTE, m_TerrainData); }
void CPatchRData::BuildBlends() { PROFILE3("build blends"); m_BlendSplats.clear(); std::vector<SBlendVertex> blendVertices; std::vector<u16> blendIndices; CTerrain* terrain = m_Patch->m_Parent; std::vector<STileBlendStack> blendStacks; blendStacks.reserve(PATCH_SIZE*PATCH_SIZE); // For each tile in patch .. 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; std::vector<STileBlend> blends; blends.reserve(9); // Compute a blend for every tile in the 3x3 square around this tile for (size_t n = 0; n < 9; ++n) { ssize_t ox = gx + BlendOffsets[n][1]; ssize_t oz = gz + BlendOffsets[n][0]; CMiniPatch* nmp = terrain->GetTile(ox, oz); if (!nmp) continue; STileBlend blend; blend.m_Texture = nmp->GetTextureEntry(); blend.m_Priority = nmp->GetPriority(); blend.m_TileMask = 1 << n; blends.push_back(blend); } // Sort the blends, highest priority first std::sort(blends.begin(), blends.end(), STileBlend::DecreasingPriority()); STileBlendStack blendStack; blendStack.i = i; blendStack.j = j; // Put the blends into the tile's stack, merging any adjacent blends with the same texture for (size_t k = 0; k < blends.size(); ++k) { if (!blendStack.blends.empty() && blendStack.blends.back().m_Texture == blends[k].m_Texture) blendStack.blends.back().m_TileMask |= blends[k].m_TileMask; else blendStack.blends.push_back(blends[k]); } // Remove blends that are after (i.e. lower priority than) the current tile // (including the current tile), since we don't want to render them on top of // the tile's base texture blendStack.blends.erase( std::find_if(blendStack.blends.begin(), blendStack.blends.end(), STileBlend::CurrentTile()), blendStack.blends.end()); blendStacks.push_back(blendStack); } } // Given the blend stack per tile, we want to batch together as many blends as possible. // Group them into a series of layers (each of which has a single texture): // (This is effectively a topological sort / linearisation of the partial order induced // by the per-tile stacks, preferring to make tiles with equal textures adjacent.) std::vector<SBlendLayer> blendLayers; while (true) { if (!blendLayers.empty()) { // Try to grab as many tiles as possible that match our current layer, // from off the blend stacks of all the tiles CTerrainTextureEntry* tex = blendLayers.back().m_Texture; for (size_t k = 0; k < blendStacks.size(); ++k) { if (!blendStacks[k].blends.empty() && blendStacks[k].blends.back().m_Texture == tex) { SBlendLayer::Tile t = { blendStacks[k].i, blendStacks[k].j, blendStacks[k].blends.back().m_TileMask }; blendLayers.back().m_Tiles.push_back(t); blendStacks[k].blends.pop_back(); } // (We've already merged adjacent entries of the same texture in each stack, // so we don't need to bother looping to check the next entry in this stack again) } } // We've grabbed as many tiles as possible; now we need to start a new layer. // The new layer's texture could come from the back of any non-empty stack; // choose the longest stack as a heuristic to reduce the number of layers CTerrainTextureEntry* bestTex = NULL; size_t bestStackSize = 0; for (size_t k = 0; k < blendStacks.size(); ++k) { if (blendStacks[k].blends.size() > bestStackSize) { bestStackSize = blendStacks[k].blends.size(); bestTex = blendStacks[k].blends.back().m_Texture; } } // If all our stacks were empty, we're done if (bestStackSize == 0) break; // Otherwise add the new layer, then loop back and start filling it in SBlendLayer layer; layer.m_Texture = bestTex; blendLayers.push_back(layer); } // Now build outgoing splats m_BlendSplats.resize(blendLayers.size()); for (size_t k = 0; k < blendLayers.size(); ++k) { SSplat& splat = m_BlendSplats[k]; splat.m_IndexStart = blendIndices.size(); splat.m_Texture = blendLayers[k].m_Texture; for (size_t t = 0; t < blendLayers[k].m_Tiles.size(); ++t) { SBlendLayer::Tile& tile = blendLayers[k].m_Tiles[t]; AddBlend(blendVertices, blendIndices, tile.i, tile.j, tile.shape, splat.m_Texture); } splat.m_IndexCount = blendIndices.size() - splat.m_IndexStart; } // Release existing vertex buffer chunks if (m_VBBlends) { g_VBMan.Release(m_VBBlends); m_VBBlends = 0; } if (m_VBBlendIndices) { g_VBMan.Release(m_VBBlendIndices); m_VBBlendIndices = 0; } if (blendVertices.size()) { // Construct vertex buffer m_VBBlends = g_VBMan.Allocate(sizeof(SBlendVertex), blendVertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER); m_VBBlends->m_Owner->UpdateChunkVertices(m_VBBlends, &blendVertices[0]); // Update the indices to include the base offset of the vertex data for (size_t k = 0; k < blendIndices.size(); ++k) blendIndices[k] += m_VBBlends->m_Index; m_VBBlendIndices = g_VBMan.Allocate(sizeof(u16), blendIndices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER); m_VBBlendIndices->m_Owner->UpdateChunkVertices(m_VBBlendIndices, &blendIndices[0]); } }