Esempio n. 1
0
/**
 * Separating axis test; returns true if the square defined by u/v/halfSize at the origin
 * is not entirely on the clockwise side of a line in direction 'axis' passing through 'a'
 */
static bool SquareSAT(const CFixedVector2D& a, const CFixedVector2D& axis, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize)
{
	fixed hw = halfSize.X;
	fixed hh = halfSize.Y;

	CFixedVector2D p = axis.Perpendicular();
	if (p.Dot((u.Multiply(hw) + v.Multiply(hh)) - a) <= fixed::Zero())
		return true;
	if (p.Dot((u.Multiply(hw) - v.Multiply(hh)) - a) <= fixed::Zero())
		return true;
	if (p.Dot((-u.Multiply(hw) - v.Multiply(hh)) - a) <= fixed::Zero())
		return true;
	if (p.Dot((-u.Multiply(hw) + v.Multiply(hh)) - a) <= fixed::Zero())
		return true;

	return false;
}
Esempio n. 2
0
	virtual CFixedVector3D PickSpawnPoint(entity_id_t spawned)
	{
		// Try to find a free space around the building's footprint.
		// (Note that we use the footprint, not the obstruction shape - this might be a bit dodgy
		// because the footprint might be inside the obstruction, but it hopefully gives us a nicer
		// shape.)

		const CFixedVector3D error(fixed::FromInt(-1), fixed::FromInt(-1), fixed::FromInt(-1));

		CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId());
		if (!cmpPosition || !cmpPosition->IsInWorld())
			return error;

		CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY);
		if (!cmpObstructionManager)
			return error;

		entity_pos_t spawnedRadius;
		ICmpObstructionManager::tag_t spawnedTag;

		CmpPtr<ICmpObstruction> cmpSpawnedObstruction(GetSimContext(), spawned);
		if (cmpSpawnedObstruction)
		{
			spawnedRadius = cmpSpawnedObstruction->GetUnitRadius();
			spawnedTag = cmpSpawnedObstruction->GetObstruction();
		}
		// else use zero radius

		// Get passability class from UnitMotion
		CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), spawned);
		if (!cmpUnitMotion)
			return error;

		ICmpPathfinder::pass_class_t spawnedPass = cmpUnitMotion->GetPassabilityClass();
		CmpPtr<ICmpPathfinder> cmpPathfinder(GetSimContext(), SYSTEM_ENTITY);
		if (!cmpPathfinder)
			return error;
		
		CFixedVector2D initialPos = cmpPosition->GetPosition2D();
		entity_angle_t initialAngle = cmpPosition->GetRotation().Y;

		// Max spawning distance in tiles
		const i32 maxSpawningDistance = 4;

		if (m_Shape == CIRCLE)
		{
			// Expand outwards from foundation
			for (i32 dist = 0; dist <= maxSpawningDistance; ++dist)
			{
				// The spawn point should be far enough from this footprint to fit the unit, plus a little gap
				entity_pos_t clearance = spawnedRadius + entity_pos_t::FromInt(2 + (int)TERRAIN_TILE_SIZE*dist);
				entity_pos_t radius = m_Size0 + clearance;

				// Try equally-spaced points around the circle in alternating directions, starting from the front
				const i32 numPoints = 31 + 2*dist;
				for (i32 i = 0; i < (numPoints+1)/2; i = (i > 0 ? -i : 1-i)) // [0, +1, -1, +2, -2, ... (np-1)/2, -(np-1)/2]
				{
					entity_angle_t angle = initialAngle + (entity_angle_t::Pi()*2).Multiply(entity_angle_t::FromInt(i)/(int)numPoints);

					fixed s, c;
					sincos_approx(angle, s, c);

					CFixedVector3D pos (initialPos.X + s.Multiply(radius), fixed::Zero(), initialPos.Y + c.Multiply(radius));

					SkipTagObstructionFilter filter(spawnedTag); // ignore collisions with the spawned entity
					if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Z, spawnedRadius, spawnedPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS)
						return pos; // this position is okay, so return it
				}
			}
		}
		else
		{
			fixed s, c;
			sincos_approx(initialAngle, s, c);

			// Expand outwards from foundation
			for (i32 dist = 0; dist <= maxSpawningDistance; ++dist)
			{
				// The spawn point should be far enough from this footprint to fit the unit, plus a little gap
				entity_pos_t clearance = spawnedRadius + entity_pos_t::FromInt(2 + (int)TERRAIN_TILE_SIZE*dist);

				for (i32 edge = 0; edge < 4; ++edge)
				{
					// Try equally-spaced points along the edge in alternating directions, starting from the middle
					const i32 numPoints = 9 + 2*dist;

					// Compute the direction and length of the current edge
					CFixedVector2D dir;
					fixed sx, sy;
					switch (edge)
					{
					case 0:
						dir = CFixedVector2D(c, -s);
						sx = m_Size0;
						sy = m_Size1;
						break;
					case 1:
						dir = CFixedVector2D(-s, -c);
						sx = m_Size1;
						sy = m_Size0;
						break;
					case 2:
						dir = CFixedVector2D(s, c);
						sx = m_Size1;
						sy = m_Size0;
						break;
					case 3:
						dir = CFixedVector2D(-c, s);
						sx = m_Size0;
						sy = m_Size1;
						break;
					}
					CFixedVector2D center = initialPos - dir.Perpendicular().Multiply(sy/2 + clearance);
					dir = dir.Multiply((sx + clearance*2) / (int)(numPoints-1));

					for (i32 i = 0; i < (numPoints+1)/2; i = (i > 0 ? -i : 1-i)) // [0, +1, -1, +2, -2, ... (np-1)/2, -(np-1)/2]
					{
						CFixedVector2D pos (center + dir*i);

						SkipTagObstructionFilter filter(spawnedTag); // ignore collisions with the spawned entity
						if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, spawnedRadius, spawnedPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS)
							return CFixedVector3D(pos.X, fixed::Zero(), pos.Y); // this position is okay, so return it
					}
				}
			}
		}

		return error;
	}