virtual CFixedVector3D GetPreviousPosition() { if (!m_InWorld) { LOGERROR(L"CCmpPosition::GetPreviousPosition called on entity when IsInWorld is false"); return CFixedVector3D(); } entity_pos_t baseY; if (m_RelativeToGround) { CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY); if (cmpTerrain) baseY = cmpTerrain->GetGroundLevel(m_PrevX, m_PrevZ); if (m_Floating) { CmpPtr<ICmpWaterManager> cmpWaterMan(GetSimContext(), SYSTEM_ENTITY); if (cmpWaterMan) baseY = std::max(baseY, cmpWaterMan->GetWaterLevel(m_PrevX, m_PrevZ)); } } return CFixedVector3D(m_PrevX, baseY + m_YOffset, m_PrevZ); }
void SimRender::ConstructCircleOnGround(const CSimContext& context, float x, float z, float radius, SOverlayLine& overlay, bool floating) { overlay.m_Coords.clear(); CmpPtr<ICmpTerrain> cmpTerrain(context, SYSTEM_ENTITY); if (cmpTerrain.null()) return; float water = 0.f; if (floating) { CmpPtr<ICmpWaterManager> cmpWaterMan(context, SYSTEM_ENTITY); if (!cmpWaterMan.null()) water = cmpWaterMan->GetExactWaterLevel(x, z); } // Adapt the circle resolution to look reasonable for small and largeish radiuses size_t numPoints = clamp((size_t)(radius*4.0f), (size_t)12, (size_t)48); overlay.m_Coords.reserve((numPoints + 1) * 3); for (size_t i = 0; i <= numPoints; ++i) // use '<=' so it's a closed loop { float a = i * 2 * (float)M_PI / numPoints; float px = x + radius * sin(a); float pz = z + radius * cos(a); float py = std::max(water, cmpTerrain->GetExactGroundLevel(px, pz)) + RENDER_HEIGHT_DELTA; overlay.m_Coords.push_back(px); overlay.m_Coords.push_back(py); overlay.m_Coords.push_back(pz); } }
void SimRender::ConstructLineOnGround(const CSimContext& context, std::vector<float> xz, SOverlayLine& overlay, bool floating) { overlay.m_Coords.clear(); CmpPtr<ICmpTerrain> cmpTerrain(context, SYSTEM_ENTITY); if (cmpTerrain.null()) return; if (xz.size() < 2) return; float water = 0.f; if (floating) { CmpPtr<ICmpWaterManager> cmpWaterMan(context, SYSTEM_ENTITY); if (!cmpWaterMan.null()) water = cmpWaterMan->GetExactWaterLevel(xz[0], xz[1]); } overlay.m_Coords.reserve(xz.size()/2 * 3); for (size_t i = 0; i < xz.size(); i += 2) { float px = xz[i]; float pz = xz[i+1]; float py = std::max(water, cmpTerrain->GetExactGroundLevel(px, pz)) + RENDER_HEIGHT_DELTA; overlay.m_Coords.push_back(px); overlay.m_Coords.push_back(py); overlay.m_Coords.push_back(pz); } }
void SimRender::ConstructSquareOnGround(const CSimContext& context, float x, float z, float w, float h, float a, SOverlayLine& overlay, bool floating) { overlay.m_Coords.clear(); CmpPtr<ICmpTerrain> cmpTerrain(context, SYSTEM_ENTITY); if (cmpTerrain.null()) return; float water = 0.f; if (floating) { CmpPtr<ICmpWaterManager> cmpWaterMan(context, SYSTEM_ENTITY); if (!cmpWaterMan.null()) water = cmpWaterMan->GetExactWaterLevel(x, z); } float c = cos(a); float s = sin(a); std::vector<std::pair<float, float> > coords; // Add the first vertex, since SplitLine will be adding only the second end-point of the each line to // the coordinates list. We don't have to worry about the other lines, since the end-point of one line // will be the starting point of the next coords.push_back(std::make_pair(x - w/2*c + h/2*s, z + w/2*s + h/2*c)); SplitLine(coords, x - w/2*c + h/2*s, z + w/2*s + h/2*c, x - w/2*c - h/2*s, z + w/2*s - h/2*c); SplitLine(coords, x - w/2*c - h/2*s, z + w/2*s - h/2*c, x + w/2*c - h/2*s, z - w/2*s - h/2*c); SplitLine(coords, x + w/2*c - h/2*s, z - w/2*s - h/2*c, x + w/2*c + h/2*s, z - w/2*s + h/2*c); SplitLine(coords, x + w/2*c + h/2*s, z - w/2*s + h/2*c, x - w/2*c + h/2*s, z + w/2*s + h/2*c); overlay.m_Coords.reserve(coords.size() * 3); for (size_t i = 0; i < coords.size(); ++i) { float px = coords[i].first; float pz = coords[i].second; float py = std::max(water, cmpTerrain->GetExactGroundLevel(px, pz)) + RENDER_HEIGHT_DELTA; overlay.m_Coords.push_back(px); overlay.m_Coords.push_back(py); overlay.m_Coords.push_back(pz); } }
void CCmpPathfinder::UpdateGrid() { // If the terrain was resized then delete the old grid data if (m_Grid && m_MapSize != GetSimContext().GetTerrain().GetTilesPerSide()) { SAFE_DELETE(m_Grid); SAFE_DELETE(m_ObstructionGrid); m_TerrainDirty = true; } // Initialise the terrain data when first needed if (!m_Grid) { // TOOD: these bits should come from ICmpTerrain ssize_t size = GetSimContext().GetTerrain().GetTilesPerSide(); ENSURE(size >= 1 && size <= 0xffff); // must fit in 16 bits m_MapSize = size; m_Grid = new Grid<TerrainTile>(m_MapSize, m_MapSize); m_ObstructionGrid = new Grid<u8>(m_MapSize, m_MapSize); } CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY); bool obstructionsDirty = cmpObstructionManager->Rasterise(*m_ObstructionGrid); if (obstructionsDirty && !m_TerrainDirty) { PROFILE("UpdateGrid obstructions"); // Obstructions changed - we need to recompute passability // Since terrain hasn't changed we only need to update the obstruction bits // and can skip the rest of the data // TODO: if ObstructionManager::SetPassabilityCircular was called at runtime // (which should probably never happen, but that's not guaranteed), // then TILE_OUTOFBOUNDS will change and we can't use this fast path, but // currently it'll just set obstructionsDirty and we won't notice for (u16 j = 0; j < m_MapSize; ++j) { for (u16 i = 0; i < m_MapSize; ++i) { TerrainTile& t = m_Grid->get(i, j); u8 obstruct = m_ObstructionGrid->get(i, j); if (obstruct & ICmpObstructionManager::TILE_OBSTRUCTED_PATHFINDING) t |= 1; else t &= ~1; if (obstruct & ICmpObstructionManager::TILE_OBSTRUCTED_FOUNDATION) t |= 2; else t &= ~2; } } ++m_Grid->m_DirtyID; } else if (obstructionsDirty || m_TerrainDirty) { PROFILE("UpdateGrid full"); // Obstructions or terrain changed - we need to recompute passability // TODO: only bother recomputing the region that has actually changed CmpPtr<ICmpWaterManager> cmpWaterMan(GetSimContext(), SYSTEM_ENTITY); CTerrain& terrain = GetSimContext().GetTerrain(); for (u16 j = 0; j < m_MapSize; ++j) { for (u16 i = 0; i < m_MapSize; ++i) { fixed x, z; TileCenter(i, j, x, z); TerrainTile t = 0; u8 obstruct = m_ObstructionGrid->get(i, j); fixed height = terrain.GetVertexGroundLevelFixed(i, j); // TODO: should use tile centre fixed water; if (!cmpWaterMan.null()) water = cmpWaterMan->GetWaterLevel(x, z); fixed depth = water - height; fixed slope = terrain.GetSlopeFixed(i, j); if (obstruct & ICmpObstructionManager::TILE_OBSTRUCTED_PATHFINDING) t |= 1; if (obstruct & ICmpObstructionManager::TILE_OBSTRUCTED_FOUNDATION) t |= 2; if (obstruct & ICmpObstructionManager::TILE_OUTOFBOUNDS) { // If out of bounds, nobody is allowed to pass for (size_t n = 0; n < m_PassClasses.size(); ++n) t |= m_PassClasses[n].m_Mask; } else { for (size_t n = 0; n < m_PassClasses.size(); ++n) { if (!m_PassClasses[n].IsPassable(depth, slope)) t |= m_PassClasses[n].m_Mask; } } std::string moveClass = terrain.GetMovementClass(i, j); if (m_TerrainCostClassTags.find(moveClass) != m_TerrainCostClassTags.end()) t |= COST_CLASS_MASK(m_TerrainCostClassTags[moveClass]); m_Grid->set(i, j, t); } } m_TerrainDirty = false; ++m_Grid->m_DirtyID; } }
void CCmpPathfinder::UpdateGrid() { CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY); if (cmpTerrain.null()) return; // error // If the terrain was resized then delete the old grid data if (m_Grid && m_MapSize != cmpTerrain->GetTilesPerSide()) { SAFE_DELETE(m_Grid); SAFE_DELETE(m_ObstructionGrid); m_TerrainDirty = true; } // Initialise the terrain data when first needed if (!m_Grid) { m_MapSize = cmpTerrain->GetTilesPerSide(); m_Grid = new Grid<TerrainTile>(m_MapSize, m_MapSize); m_ObstructionGrid = new Grid<u8>(m_MapSize, m_MapSize); } CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY); bool obstructionsDirty = cmpObstructionManager->Rasterise(*m_ObstructionGrid); if (obstructionsDirty && !m_TerrainDirty) { PROFILE("UpdateGrid obstructions"); // Obstructions changed - we need to recompute passability // Since terrain hasn't changed we only need to update the obstruction bits // and can skip the rest of the data // TODO: if ObstructionManager::SetPassabilityCircular was called at runtime // (which should probably never happen, but that's not guaranteed), // then TILE_OUTOFBOUNDS will change and we can't use this fast path, but // currently it'll just set obstructionsDirty and we won't notice for (u16 j = 0; j < m_MapSize; ++j) { for (u16 i = 0; i < m_MapSize; ++i) { TerrainTile& t = m_Grid->get(i, j); u8 obstruct = m_ObstructionGrid->get(i, j); if (obstruct & ICmpObstructionManager::TILE_OBSTRUCTED_PATHFINDING) t |= 1; else t &= (TerrainTile)~1; if (obstruct & ICmpObstructionManager::TILE_OBSTRUCTED_FOUNDATION) t |= 2; else t &= (TerrainTile)~2; } } ++m_Grid->m_DirtyID; } else if (obstructionsDirty || m_TerrainDirty) { PROFILE("UpdateGrid full"); // Obstructions or terrain changed - we need to recompute passability // TODO: only bother recomputing the region that has actually changed CmpPtr<ICmpWaterManager> cmpWaterMan(GetSimContext(), SYSTEM_ENTITY); // TOOD: these bits should come from ICmpTerrain CTerrain& terrain = GetSimContext().GetTerrain(); // avoid integer overflow in intermediate calculation const u16 shoreMax = 32767; // First pass - find underwater tiles Grid<bool> waterGrid(m_MapSize, m_MapSize); for (u16 j = 0; j < m_MapSize; ++j) { for (u16 i = 0; i < m_MapSize; ++i) { fixed x, z; TileCenter(i, j, x, z); bool underWater = !cmpWaterMan.null() && (cmpWaterMan->GetWaterLevel(x, z) > terrain.GetExactGroundLevelFixed(x, z)); waterGrid.set(i, j, underWater); } } // Second pass - find shore tiles Grid<u16> shoreGrid(m_MapSize, m_MapSize); for (u16 j = 0; j < m_MapSize; ++j) { for (u16 i = 0; i < m_MapSize; ++i) { // Find a land tile if (!waterGrid.get(i, j)) { if ((i > 0 && waterGrid.get(i-1, j)) || (i > 0 && j < m_MapSize-1 && waterGrid.get(i-1, j+1)) || (i > 0 && j > 0 && waterGrid.get(i-1, j-1)) || (i < m_MapSize-1 && waterGrid.get(i+1, j)) || (i < m_MapSize-1 && j < m_MapSize-1 && waterGrid.get(i+1, j+1)) || (i < m_MapSize-1 && j > 0 && waterGrid.get(i+1, j-1)) || (j > 0 && waterGrid.get(i, j-1)) || (j < m_MapSize-1 && waterGrid.get(i, j+1)) ) { // If it's bordered by water, it's a shore tile shoreGrid.set(i, j, 0); } else { shoreGrid.set(i, j, shoreMax); } } } } // Expand influences on land to find shore distance for (u16 y = 0; y < m_MapSize; ++y) { u16 min = shoreMax; for (u16 x = 0; x < m_MapSize; ++x) { if (!waterGrid.get(x, y)) { u16 g = shoreGrid.get(x, y); if (g > min) shoreGrid.set(x, y, min); else if (g < min) min = g; ++min; } } for (u16 x = m_MapSize; x > 0; --x) { if (!waterGrid.get(x-1, y)) { u16 g = shoreGrid.get(x-1, y); if (g > min) shoreGrid.set(x-1, y, min); else if (g < min) min = g; ++min; } } } for (u16 x = 0; x < m_MapSize; ++x) { u16 min = shoreMax; for (u16 y = 0; y < m_MapSize; ++y) { if (!waterGrid.get(x, y)) { u16 g = shoreGrid.get(x, y); if (g > min) shoreGrid.set(x, y, min); else if (g < min) min = g; ++min; } } for (u16 y = m_MapSize; y > 0; --y) { if (!waterGrid.get(x, y-1)) { u16 g = shoreGrid.get(x, y-1); if (g > min) shoreGrid.set(x, y-1, min); else if (g < min) min = g; ++min; } } } // Apply passability classes to terrain for (u16 j = 0; j < m_MapSize; ++j) { for (u16 i = 0; i < m_MapSize; ++i) { fixed x, z; TileCenter(i, j, x, z); TerrainTile t = 0; u8 obstruct = m_ObstructionGrid->get(i, j); fixed height = terrain.GetExactGroundLevelFixed(x, z); fixed water; if (!cmpWaterMan.null()) water = cmpWaterMan->GetWaterLevel(x, z); fixed depth = water - height; fixed slope = terrain.GetSlopeFixed(i, j); fixed shoredist = fixed::FromInt(shoreGrid.get(i, j)); if (obstruct & ICmpObstructionManager::TILE_OBSTRUCTED_PATHFINDING) t |= 1; if (obstruct & ICmpObstructionManager::TILE_OBSTRUCTED_FOUNDATION) t |= 2; if (obstruct & ICmpObstructionManager::TILE_OUTOFBOUNDS) { // If out of bounds, nobody is allowed to pass for (size_t n = 0; n < m_PassClasses.size(); ++n) t |= m_PassClasses[n].m_Mask; } else { for (size_t n = 0; n < m_PassClasses.size(); ++n) { if (!m_PassClasses[n].IsPassable(depth, slope, shoredist)) t |= m_PassClasses[n].m_Mask; } } std::string moveClass = terrain.GetMovementClass(i, j); if (m_TerrainCostClassTags.find(moveClass) != m_TerrainCostClassTags.end()) t |= COST_CLASS_MASK(m_TerrainCostClassTags[moveClass]); m_Grid->set(i, j, t); } } m_TerrainDirty = false; ++m_Grid->m_DirtyID; } }