Example #1
0
	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); 
	} 
Example #2
0
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);
	}
}
Example #3
0
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);
	}
}
Example #4
0
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);
	}
}
Example #5
0
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;
	}
}
Example #6
0
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;
	}
}