Пример #1
0
void CModelAbstract::CalcSelectionBox()
{
	if (m_CustomSelectionShape)
	{
		// custom shape
		switch(m_CustomSelectionShape->m_Type)
		{
		case CustomSelectionShape::BOX:
			{
				// create object-space bounds according to the information in the descriptor, and transform them to world-space.
				// the box is centered on the X and Z axes, but extends from 0 to its height on the Y axis.
				const float width = m_CustomSelectionShape->m_Size0;
				const float depth = m_CustomSelectionShape->m_Size1;
				const float height = m_CustomSelectionShape->m_Height;

				CBoundingBoxAligned bounds;
				bounds += CVector3D(-width/2.f, 0,     -depth/2.f);
				bounds += CVector3D( width/2.f, height, depth/2.f);

				bounds.Transform(GetTransform(), m_SelectionBox);
			}
			break;
		case CustomSelectionShape::CYLINDER:
			{
				// TODO: unimplemented
				m_SelectionBox.SetEmpty();
				LOGWARNING(L"[ModelAbstract] TODO: Cylinder selection boxes are not yet implemented. Use BOX or BOUNDS selection shapes instead.");
			}
			break;
		default:
			{
				m_SelectionBox.SetEmpty();
				//LOGWARNING(L"[ModelAbstract] Unrecognized selection shape type: %ld", m_CustomSelectionShape->m_Type);
				debug_warn("[ModelAbstract] Unrecognized selection shape type");
			}
			break;
		}
	}
	else
	{
		// standard method

		// Get the object-space bounds that should be used to construct this model (and its children)'s selection box
		CBoundingBoxAligned objBounds = GetObjectSelectionBoundsRec();
		if (objBounds.IsEmpty())
		{
			m_SelectionBox.SetEmpty(); // model does not wish to participate in selection
			return;
		}

		// Prevent the bounding box from extending through the terrain; clip the lower plane at Y=0 in object space.
		if (objBounds[1].Y > 0.f) // should always be the case, unless the models are defined really weirdly
			objBounds[0].Y = std::max(0.f, objBounds[0].Y);

		// transform object-space axis-aligned bounds to world-space arbitrary-aligned box
		objBounds.Transform(GetTransform(), m_SelectionBox);
	}
	
}
Пример #2
0
const CBoundingBoxAligned CModel::GetObjectSelectionBoundsRec()
{
	CBoundingBoxAligned objBounds = GetObjectBounds();		// updates the (children-not-included) object-space bounds if necessary

	// now extend these bounds to include the props' selection bounds (if any)
	for (size_t i = 0; i < m_Props.size(); ++i)
	{
		const Prop& prop = m_Props[i];
		if (prop.m_Hidden)
			continue; // prop is hidden from rendering, so it also shouldn't be used for selection

		CBoundingBoxAligned propSelectionBounds = prop.m_Model->GetObjectSelectionBoundsRec();
		if (propSelectionBounds.IsEmpty())
			continue;	// submodel does not wish to participate in selection box, exclude it

		// We have the prop's bounds in its own object-space; now we need to transform them so they can be properly added 
		// to the bounds in our object-space. For that, we need the transform of the prop attachment point.
		// 
		// We have the prop point information; however, it's not trivial to compute its exact location in our object-space
		// since it may or may not be attached to a bone (see SPropPoint), which in turn may or may not be in the middle of
		// an animation. The bone matrices might be of interest, but they're really only meant to be used for the animation 
		// system and are quite opaque to use from the outside (see @ref ValidatePosition).
		// 
		// However, a nice side effect of ValidatePosition is that it also computes the absolute world-space transform of 
		// our props and sets it on their respective models. In particular, @ref ValidatePosition will compute the prop's
		// world-space transform as either
		// 
		// T' = T x	B x O
		// or 
		// T' = T x O
		// 
		// where T' is the prop's world-space transform, T is our world-space transform, O is the prop's local
		// offset/rotation matrix, and B is an optional transformation matrix of the bone the prop is attached to 
		// (taking into account animation and everything).
		// 
		// From this, it is clear that either O or B x O is the object-space transformation matrix of the prop. So,
		// all we need to do is apply our own inverse world-transform T^(-1) to T' to get our desired result. Luckily,
		// this is precomputed upon setting the transform matrix (see @ref SetTransform), so it is free to fetch.
		
		CMatrix3D propObjectTransform = prop.m_Model->GetTransform(); // T'
		propObjectTransform.Concatenate(GetInvTransform()); // T^(-1) x T'

		// Transform the prop's bounds into our object coordinate space
		CBoundingBoxAligned transformedPropSelectionBounds;
		propSelectionBounds.Transform(propObjectTransform, transformedPropSelectionBounds);

		objBounds += transformedPropSelectionBounds;
	}

	return objBounds;
}
CBoundingBoxOriented::CBoundingBoxOriented(const CBoundingBoxAligned& bound)
{
	if (bound.IsEmpty())
	{
		SetEmpty();
	}
	else
	{
		bound.GetCentre(m_Center);

		// the axes of an AABB are the world-space axes
		m_Basis[0].X = 1.f; m_Basis[0].Y = 0.f; m_Basis[0].Z = 0.f;
		m_Basis[1].X = 0.f; m_Basis[1].Y = 1.f; m_Basis[1].Z = 0.f;
		m_Basis[2].X = 0.f; m_Basis[2].Y = 0.f; m_Basis[2].Z = 1.f;

		// element-wise division by two to get half sizes (remember, [1] and [0] are the max and min coord points)
		m_HalfSizes = (bound[1] - bound[0]) * 0.5f;
	}
}
Пример #4
0
void SimRender::ConstructBoxOutline(const CBoundingBoxAligned& bound, SOverlayLine& overlayLine)
{
	overlayLine.m_Coords.clear();

	if (bound.IsEmpty())
		return;

	const CVector3D& pMin = bound[0];
	const CVector3D& pMax = bound[1];

	// floor square
	overlayLine.PushCoords(pMin.X, pMin.Y, pMin.Z);
	overlayLine.PushCoords(pMax.X, pMin.Y, pMin.Z);
	overlayLine.PushCoords(pMax.X, pMin.Y, pMax.Z);
	overlayLine.PushCoords(pMin.X, pMin.Y, pMax.Z);
	overlayLine.PushCoords(pMin.X, pMin.Y, pMin.Z);
	// roof square
	overlayLine.PushCoords(pMin.X, pMax.Y, pMin.Z);
	overlayLine.PushCoords(pMax.X, pMax.Y, pMin.Z);
	overlayLine.PushCoords(pMax.X, pMax.Y, pMax.Z);
	overlayLine.PushCoords(pMin.X, pMax.Y, pMax.Z);
	overlayLine.PushCoords(pMin.X, pMax.Y, pMin.Z);
}
Пример #5
0
///////////////////////////////////////////////////////////////////////////////////////////////////
// CalcShadowMatrices: calculate required matrices for shadow map generation - the light's
// projection and transformation matrices
void ShadowMapInternals::CalcShadowMatrices()
{
	CRenderer& renderer = g_Renderer;
	float minZ = ShadowBound[0].Z;

	ShadowBound.IntersectFrustumConservative(LightspaceCamera.GetFrustum());

	// ShadowBound might have been empty to begin with, producing an empty result
	if (ShadowBound.IsEmpty())
	{
		// no-op
		LightProjection.SetIdentity();
		TextureMatrix = LightTransform;
		return;
	}

	// round off the shadow boundaries to sane increments to help reduce swim effect
	float boundInc = 16.0f;
	ShadowBound[0].X = floor(ShadowBound[0].X / boundInc) * boundInc;
	ShadowBound[0].Y = floor(ShadowBound[0].Y / boundInc) * boundInc;
	ShadowBound[1].X = ceil(ShadowBound[1].X / boundInc) * boundInc;
	ShadowBound[1].Y = ceil(ShadowBound[1].Y / boundInc) * boundInc;

	// minimum Z bound must not be clipped too much, because objects that lie outside
	// the shadow bounds cannot cast shadows either
	// the 2.0 is rather arbitrary: it should be big enough so that we won't accidentally miss
	// a shadow generator, and small enough not to affect Z precision
	ShadowBound[0].Z = minZ - 2.0;

	// Setup orthogonal projection (lightspace -> clip space) for shadowmap rendering
	CVector3D scale = ShadowBound[1] - ShadowBound[0];
	CVector3D shift = (ShadowBound[1] + ShadowBound[0]) * -0.5;

	if (scale.X < 1.0)
		scale.X = 1.0;
	if (scale.Y < 1.0)
		scale.Y = 1.0;
	if (scale.Z < 1.0)
		scale.Z = 1.0;

	scale.X = 2.0 / scale.X;
	scale.Y = 2.0 / scale.Y;
	scale.Z = 2.0 / scale.Z;

	// make sure a given world position falls on a consistent shadowmap texel fractional offset
	float offsetX = fmod(ShadowBound[0].X - LightTransform._14, 2.0f/(scale.X*EffectiveWidth));
	float offsetY = fmod(ShadowBound[0].Y - LightTransform._24, 2.0f/(scale.Y*EffectiveHeight));

	LightProjection.SetZero();
	LightProjection._11 = scale.X;
	LightProjection._14 = (shift.X + offsetX) * scale.X;
	LightProjection._22 = scale.Y;
	LightProjection._24 = (shift.Y + offsetY) * scale.Y;
	LightProjection._33 = scale.Z;
	LightProjection._34 = shift.Z * scale.Z + renderer.m_ShadowZBias;
	LightProjection._44 = 1.0;

	// Calculate texture matrix by creating the clip space to texture coordinate matrix
	// and then concatenating all matrices that have been calculated so far

	float texscalex = scale.X * 0.5f * (float)EffectiveWidth / (float)Width;
	float texscaley = scale.Y * 0.5f * (float)EffectiveHeight / (float)Height;
	float texscalez = scale.Z * 0.5f;

	CMatrix3D lightToTex;
	lightToTex.SetZero();
	lightToTex._11 = texscalex;
	lightToTex._14 = (offsetX - ShadowBound[0].X) * texscalex;
	lightToTex._22 = texscaley;
	lightToTex._24 = (offsetY - ShadowBound[0].Y) * texscaley;
	lightToTex._33 = texscalez;
	lightToTex._34 = -ShadowBound[0].Z * texscalez;
	lightToTex._44 = 1.0;

	TextureMatrix = lightToTex * LightTransform;
}
Пример #6
0
std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, player_id_t player, bool allowEditorSelectables)
{
	CVector3D origin, dir;
	camera.BuildCameraRay(screenX, screenY, origin, dir);

	CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY);
	ENSURE(cmpRangeManager);

	std::vector<std::pair<float, entity_id_t> > hits; // (dist^2, entity) pairs

	const CSimulation2::InterfaceListUnordered& ents = simulation.GetEntitiesWithInterfaceUnordered(IID_Selectable);
	for (CSimulation2::InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it)
	{
		entity_id_t ent = it->first;

		// Check if this entity is only selectable in Atlas
		if (!allowEditorSelectables && static_cast<ICmpSelectable*>(it->second)->IsEditorOnly())
			continue;

		// Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld)
		if (cmpRangeManager->GetLosVisibility(ent, player) == ICmpRangeManager::VIS_HIDDEN)
			continue;

		CmpPtr<ICmpVisual> cmpVisual(simulation.GetSimContext(), ent);
		if (!cmpVisual)
			continue;

		CVector3D center;
		float tmin, tmax;

		CBoundingBoxOriented selectionBox = cmpVisual->GetSelectionBox();
		if (selectionBox.IsEmpty())
		{
			if (!allowEditorSelectables)
				continue;

			// Fall back to using old AABB selection method for decals
			//	see: http://trac.wildfiregames.com/ticket/1032
			CBoundingBoxAligned aABBox = cmpVisual->GetBounds();
			if (aABBox.IsEmpty())
				continue;

			if (!aABBox.RayIntersect(origin, dir, tmin, tmax))
				continue;

			aABBox.GetCentre(center);
		}
		else
		{
			if (!selectionBox.RayIntersect(origin, dir, tmin, tmax))
				continue;

			center = selectionBox.m_Center;
		}

		// Find the perpendicular distance from the object's centre to the picker ray

		float dist2;
		CVector3D closest = origin + dir * (center - origin).Dot(dir);
		dist2 = (closest - center).LengthSquared();

		hits.push_back(std::make_pair(dist2, ent));
	}

	// Sort hits by distance
	std::sort(hits.begin(), hits.end()); // lexicographic comparison

	// Extract the entity IDs
	std::vector<entity_id_t> hitEnts;
	hitEnts.reserve(hits.size());
	for (size_t i = 0; i < hits.size(); ++i)
		hitEnts.push_back(hits[i].second);
	return hitEnts;
}
Пример #7
0
void TerrainRenderer::RenderTerrainOverlayTexture(CMatrix3D& textureMatrix)
{
#if CONFIG2_GLES
#warning TODO: implement TerrainRenderer::RenderTerrainOverlayTexture for GLES
	UNUSED2(textureMatrix);
#else
	ENSURE(m->phase == Phase_Render);

	std::vector<CPatchRData*>& visiblePatches = m->visiblePatches;

	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);

	glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glDepthMask(0);

	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

	glMatrixMode(GL_TEXTURE);
	glLoadMatrixf(&textureMatrix._11);
	glMatrixMode(GL_MODELVIEW);

	CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy", CShaderDefines());
	dummyShader->Bind();
	CPatchRData::RenderStreams(visiblePatches, dummyShader, STREAM_POS|STREAM_POSTOUV0);
	dummyShader->Unbind();

	// To make the overlay visible over water, render an additional map-sized
	// water-height patch
	CBoundingBoxAligned waterBounds;
	for (size_t i = 0; i < m->visiblePatches.size(); ++i)
	{
		CPatchRData* data = m->visiblePatches[i];
		waterBounds += data->GetWaterBounds();
	}
	if (!waterBounds.IsEmpty())
	{
		float h = g_Renderer.GetWaterManager()->m_WaterHeight + 0.05f; // add a delta to avoid z-fighting
		float waterPos[] = {
			waterBounds[0].X, h, waterBounds[0].Z,
			waterBounds[1].X, h, waterBounds[0].Z,
			waterBounds[0].X, h, waterBounds[1].Z,
			waterBounds[1].X, h, waterBounds[1].Z
		};
		glVertexPointer(3, GL_FLOAT, 3*sizeof(float), waterPos);
		glTexCoordPointer(3, GL_FLOAT, 3*sizeof(float), waterPos);
		glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	}

	glMatrixMode(GL_TEXTURE);
	glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);

	glDepthMask(1);
	glDisable(GL_BLEND);
	glDisableClientState(GL_COLOR_ARRAY);
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
#endif
}
Пример #8
0
std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, player_id_t player, bool allowEditorSelectables, int range)
{
	PROFILE2("PickEntitiesAtPoint");
	CVector3D origin, dir;
	camera.BuildCameraRay(screenX, screenY, origin, dir);

	CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY);
	ENSURE(cmpRangeManager);

	/* We try to approximate where the mouse is hovering by drawing a ray from
	 * the center of the camera and through the mouse then taking the position
	 * at which the ray intersects the terrain.                               */
	// TODO: Do this smarter without being slow.
	CVector3D pos3d = camera.GetWorldCoordinates(screenX, screenY, true);
	// Change the position to 2D by removing the terrain height.
	CFixedVector2D pos(fixed::FromFloat(pos3d.X), fixed::FromFloat(pos3d.Z));

	// Get a rough group of entities using our approximated origin.
	SpatialQueryArray ents;
	cmpRangeManager->GetSubdivision()->GetNear(ents, pos, entity_pos_t::FromInt(range));

	// Filter for relevent entities and calculate precise distances.
	std::vector<std::pair<float, entity_id_t> > hits; // (dist^2, entity) pairs
	for (int i = 0; i < ents.size(); ++i)
	{
		CmpPtr<ICmpSelectable> cmpSelectable(simulation, ents[i]);
		if (!cmpSelectable)
			continue;

		CEntityHandle handle = cmpSelectable->GetEntityHandle();

		// Check if this entity is only selectable in Atlas
		if (!allowEditorSelectables && cmpSelectable->IsEditorOnly())
			continue;

		// Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld)
		if (cmpRangeManager->GetLosVisibility(handle, player) == ICmpRangeManager::VIS_HIDDEN)
			continue;

		CmpPtr<ICmpVisual> cmpVisual(handle);
		if (!cmpVisual)
			continue;

		CVector3D center;
		float tmin, tmax;

		CBoundingBoxOriented selectionBox = cmpVisual->GetSelectionBox();
		if (selectionBox.IsEmpty())
		{
			if (!allowEditorSelectables)
				continue;

			// Fall back to using old AABB selection method for decals
			//	see: http://trac.wildfiregames.com/ticket/1032
			CBoundingBoxAligned aABBox = cmpVisual->GetBounds();
			if (aABBox.IsEmpty())
				continue;

			if (!aABBox.RayIntersect(origin, dir, tmin, tmax))
				continue;

			aABBox.GetCentre(center);
		}
		else
		{
			if (!selectionBox.RayIntersect(origin, dir, tmin, tmax))
				continue;

			center = selectionBox.m_Center;
		}

		// Find the perpendicular distance from the object's centre to the picker ray
		float dist2;
		CVector3D closest = origin + dir * (center - origin).Dot(dir);
		dist2 = (closest - center).LengthSquared();

		hits.push_back(std::make_pair(dist2, ents[i]));
	}

	// Sort hits by distance
	std::sort(hits.begin(), hits.end()); // lexicographic comparison

	// Extract the entity IDs
	std::vector<entity_id_t> hitEnts;
	hitEnts.reserve(hits.size());
	for (size_t i = 0; i < hits.size(); ++i)
		hitEnts.push_back(hits[i].second);
	return hitEnts;
}