Beispiel #1
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;
}
Beispiel #2
0
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ValidatePosition: ensure that current transform and bone matrices are both uptodate
void CModel::ValidatePosition()
{
	if (m_PositionValid)
	{
		ENSURE(!m_Parent || m_Parent->m_PositionValid);
		return;
	}
	
	if (m_Parent && !m_Parent->m_PositionValid)
	{
		// Make sure we don't base our calculations on
		// a parent animation state that is out of date.
		m_Parent->ValidatePosition();
		
		// Parent will recursively call our validation.
		ENSURE(m_PositionValid);
		return;
	}

	if (m_Anim && m_BoneMatrices)
	{
//		PROFILE( "generating bone matrices" );
	
		ENSURE(m_pModelDef->GetNumBones() == m_Anim->m_AnimDef->GetNumKeys());
	
		m_Anim->m_AnimDef->BuildBoneMatrices(m_AnimTime, m_BoneMatrices, !(m_Flags & MODELFLAG_NOLOOPANIMATION));
	}
	else if (m_BoneMatrices)
	{
		// Bones but no animation - probably a buggy actor forgot to set up the animation,
		// so just render it in its bind pose

		for (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)
		{
			m_BoneMatrices[i].SetIdentity();
			m_BoneMatrices[i].Rotate(m_pModelDef->GetBones()[i].m_Rotation);
			m_BoneMatrices[i].Translate(m_pModelDef->GetBones()[i].m_Translation);
		}
	}

	// For CPU skinning, we precompute as much as possible so that the only
	// per-vertex work is a single matrix*vec multiplication.
	// For GPU skinning, we try to minimise CPU work by doing most computation
	// in the vertex shader instead.
	// Using g_Renderer.m_Options to detect CPU vs GPU is a bit hacky,
	// and this doesn't allow the setting to change at runtime, but there isn't
	// an obvious cleaner way to determine what data needs to be computed,
	// and GPU skinning is a rarely-used experimental feature anyway.
	bool worldSpaceBoneMatrices = !g_Renderer.m_Options.m_GPUSkinning;
	bool computeBlendMatrices = !g_Renderer.m_Options.m_GPUSkinning;

	if (m_BoneMatrices && worldSpaceBoneMatrices)
	{
		// add world-space transformation to m_BoneMatrices
		const CMatrix3D transform = GetTransform();
		for (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)
			m_BoneMatrices[i].Concatenate(transform);
	}

	// our own position is now valid; now we can safely update our props' positions without fearing 
	// that doing so will cause a revalidation of this model (see recursion above).
	m_PositionValid = true;
	
	// re-position and validate all props
	for (size_t j = 0; j < m_Props.size(); ++j)
	{
		const Prop& prop=m_Props[j];

		CMatrix3D proptransform = prop.m_Point->m_Transform;;
		if (prop.m_Point->m_BoneIndex != 0xff)
		{
			CMatrix3D boneMatrix = m_BoneMatrices[prop.m_Point->m_BoneIndex];
			if (!worldSpaceBoneMatrices)
				boneMatrix.Concatenate(GetTransform());
			proptransform.Concatenate(boneMatrix);
		}
		else
		{
			// not relative to any bone; just apply world-space transformation (i.e. relative to object-space origin)
			proptransform.Concatenate(m_Transform);
		}
		
		prop.m_Model->SetTransform(proptransform);
		prop.m_Model->ValidatePosition();
	}

	if (m_BoneMatrices)
	{
		for (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)
		{
			m_BoneMatrices[i] = m_BoneMatrices[i] * m_pModelDef->GetInverseBindBoneMatrices()[i];
		}

		// Note: there is a special case of joint influence, in which the vertex
		//	is influenced by the bind-shape transform instead of a particular bone,
		//	which we indicate with the blending bone ID set to the total number
		//	of bones. But since we're skinning in world space, we use the model's
		//	world space transform and store that matrix in this special index.
		//	(see http://trac.wildfiregames.com/ticket/1012)
		m_BoneMatrices[m_pModelDef->GetNumBones()] = m_Transform;

		if (computeBlendMatrices)
			m_pModelDef->BlendBoneMatrices(m_BoneMatrices);
	}
}