This was SVN commit r10593.
This commit is contained in:
parent
9e97c73544
commit
85186c98b2
@ -97,7 +97,7 @@
|
||||
/>
|
||||
|
||||
<!-- Dev/cheat commands -->
|
||||
<object name="devCommands" size="100%-156 50%-80 100%-8 50%+80" type="image" sprite="devCommandsBackground"
|
||||
<object name="devCommands" size="100%-156 50%-88 100%-8 50%+88" type="image" sprite="devCommandsBackground"
|
||||
hidden="true" hotkey="session.devcommands.toggle">
|
||||
<action on="Press">
|
||||
toggleDeveloperOverlay();
|
||||
@ -134,23 +134,28 @@
|
||||
<action on="Press">Engine.GuiInterfaceCall("SetRangeDebugOverlay", this.checked);</action>
|
||||
</object>
|
||||
|
||||
<object size="0 96 100%-18 112" type="text" style="devCommandsText">Restrict camera</object>
|
||||
<object size="100%-16 96 100% 112" type="checkbox" style="StoneCrossBox" checked="true">
|
||||
<object size="0 96 100%-18 112" type="text" style="devCommandsText">Bounding box overlay</object>
|
||||
<object size="100%-16 96 100% 112" type="checkbox" style="StoneCrossBox">
|
||||
<action on="Press">Engine.SetBoundingBoxDebugOverlay(this.checked);</action>
|
||||
</object>
|
||||
|
||||
<object size="0 112 100%-18 128" type="text" style="devCommandsText">Restrict camera</object>
|
||||
<object size="100%-16 112 100% 128" type="checkbox" style="StoneCrossBox" checked="true">
|
||||
<action on="Press">gameView.constrainCamera = this.checked;</action>
|
||||
</object>
|
||||
|
||||
<object size="0 112 100%-18 128" type="text" style="devCommandsText">Reveal map</object>
|
||||
<object name="devCommandsRevealMap" size="100%-16 112 100% 128" type="checkbox" style="StoneCrossBox">
|
||||
<object size="0 128 100%-18 144" type="text" style="devCommandsText">Reveal map</object>
|
||||
<object size="100%-16 128 100% 144" type="checkbox" name="devCommandsRevealMap" style="StoneCrossBox">
|
||||
<action on="Press">Engine.PostNetworkCommand({"type": "reveal-map", "enable": this.checked});</action>
|
||||
</object>
|
||||
|
||||
<object size="0 128 100%-18 144" type="text" style="devCommandsText">Enable time warp</object>
|
||||
<object size="100%-16 128 100% 144" type="checkbox" name="devTimeWarp" style="StoneCrossBox">
|
||||
<object size="0 144 100%-18 160" type="text" style="devCommandsText">Enable time warp</object>
|
||||
<object size="100%-16 144 100% 160" type="checkbox" name="devTimeWarp" style="StoneCrossBox">
|
||||
<action on="Press">Engine.EnableTimeWarpRecording(this.checked ? 10 : 0);</action>
|
||||
</object>
|
||||
|
||||
<object size="0 144 100%-18 160" type="text" style="devCommandsText">Promote selected units</object>
|
||||
<object size="100%-16 144 100% 160" type="button" style="StoneCrossBox">
|
||||
<object size="0 160 100%-18 176" type="text" style="devCommandsText">Promote selected units</object>
|
||||
<object size="100%-16 160 100% 176" type="button" style="StoneCrossBox">
|
||||
<action on="Press">Engine.PostNetworkCommand({"type": "promote", "entities": g_Selection.toList()});</action>
|
||||
</object>
|
||||
</object>
|
||||
|
@ -16,4 +16,9 @@
|
||||
<Type>wood.tree</Type>
|
||||
</ResourceSupply>
|
||||
<Selectable/>
|
||||
<VisualActor>
|
||||
<SelectionShape>
|
||||
<Footprint />
|
||||
</SelectionShape>
|
||||
</VisualActor>
|
||||
</Entity>
|
||||
|
@ -80,7 +80,7 @@ void CCamera::SetProjectionTile(int tiles, int tile_x, int tile_y)
|
||||
//Updates the frustum planes. Should be called
|
||||
//everytime the view or projection matrices are
|
||||
//altered.
|
||||
void CCamera::UpdateFrustum(const CBound& scissor)
|
||||
void CCamera::UpdateFrustum(const CBoundingBoxAligned& scissor)
|
||||
{
|
||||
CMatrix3D MatFinal;
|
||||
CMatrix3D MatView;
|
||||
|
@ -24,7 +24,7 @@
|
||||
#define INCLUDED_CAMERA
|
||||
|
||||
#include "Frustum.h"
|
||||
#include "maths/Bound.h"
|
||||
#include "maths/BoundingBoxAligned.h"
|
||||
#include "maths/Matrix3D.h"
|
||||
|
||||
// view port
|
||||
@ -56,7 +56,7 @@ class CCamera
|
||||
// Updates the frustum planes. Should be called
|
||||
// everytime the view or projection matrices are
|
||||
// altered.
|
||||
void UpdateFrustum(const CBound& scissor = CBound(CVector3D(-1.0f, -1.0f, -1.0f), CVector3D(1.0f, 1.0f, 1.0f)));
|
||||
void UpdateFrustum(const CBoundingBoxAligned& scissor = CBoundingBoxAligned(CVector3D(-1.0f, -1.0f, -1.0f), CVector3D(1.0f, 1.0f, 1.0f)));
|
||||
void ClipFrustum(const CPlane& clipPlane);
|
||||
const CFrustum& GetFrustum() const { return m_ViewFrustum; }
|
||||
|
||||
|
@ -55,7 +55,7 @@ void CModelDecal::CalcBounds()
|
||||
{
|
||||
ssize_t i0, j0, i1, j1;
|
||||
CalcVertexExtents(i0, j0, i1, j1);
|
||||
m_Bounds = m_Terrain->GetVertexesBound(i0, j0, i1, j1);
|
||||
m_WorldBounds = m_Terrain->GetVertexesBound(i0, j0, i1, j1);
|
||||
}
|
||||
|
||||
void CModelDecal::SetTerrainDirty(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1)
|
||||
|
@ -28,7 +28,7 @@ portal rendering, where a portal may have 3 or more edges.
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "Frustum.h"
|
||||
#include "maths/Bound.h"
|
||||
#include "maths/BoundingBoxAligned.h"
|
||||
#include "maths/MathUtil.h"
|
||||
|
||||
CFrustum::CFrustum ()
|
||||
@ -113,7 +113,7 @@ bool CFrustum::IsSphereVisible (const CVector3D ¢er, float radius) const
|
||||
}
|
||||
|
||||
|
||||
bool CFrustum::IsBoxVisible (const CVector3D &position,const CBound &bounds) const
|
||||
bool CFrustum::IsBoxVisible (const CVector3D &position,const CBoundingBoxAligned &bounds) const
|
||||
{
|
||||
//basically for every plane we calculate the furthest point
|
||||
//in the box to that plane. If that point is beyond the plane
|
||||
|
@ -33,7 +33,7 @@ portal rendering, where a portal may have 3 or more edges.
|
||||
//10 planes should be enough
|
||||
#define MAX_NUM_FRUSTUM_PLANES (10)
|
||||
|
||||
class CBound;
|
||||
class CBoundingBoxAligned;
|
||||
|
||||
class CFrustum
|
||||
{
|
||||
@ -55,7 +55,7 @@ public:
|
||||
bool IsPointVisible (const CVector3D &point) const;
|
||||
bool DoesSegmentIntersect(const CVector3D& start, const CVector3D &end);
|
||||
bool IsSphereVisible (const CVector3D ¢er, float radius) const;
|
||||
bool IsBoxVisible (const CVector3D &position,const CBound &bounds) const;
|
||||
bool IsBoxVisible (const CVector3D &position,const CBoundingBoxAligned &bounds) const;
|
||||
|
||||
CPlane& operator[](size_t idx) { return m_aPlanes[idx]; }
|
||||
const CPlane& operator[](size_t idx) const { return m_aPlanes[idx]; }
|
||||
|
@ -36,7 +36,7 @@
|
||||
#include "graphics/UnitManager.h"
|
||||
#include "lib/input.h"
|
||||
#include "lib/timer.h"
|
||||
#include "maths/Bound.h"
|
||||
#include "maths/BoundingBoxAligned.h"
|
||||
#include "maths/MathUtil.h"
|
||||
#include "maths/Matrix3D.h"
|
||||
#include "maths/Quaternion.h"
|
||||
@ -499,7 +499,7 @@ void CGameView::EnumerateObjects(const CFrustum& frustum, SceneCollector* c)
|
||||
CPatch* patch=pTerrain->GetPatch(i,j); // can't fail
|
||||
|
||||
// If the patch is underwater, calculate a bounding box that also contains the water plane
|
||||
CBound bounds = patch->GetBounds();
|
||||
CBoundingBoxAligned bounds = patch->GetWorldBounds();
|
||||
float waterHeight = g_Renderer.GetWaterManager()->m_WaterHeight + 0.001f;
|
||||
if(bounds[1].Y < waterHeight) {
|
||||
bounds[1].Y = waterHeight;
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
#include "HFTracer.h"
|
||||
#include "Terrain.h"
|
||||
#include "maths/Bound.h"
|
||||
#include "maths/BoundingBoxAligned.h"
|
||||
#include "maths/Vector3D.h"
|
||||
|
||||
// To cope well with points that are slightly off the edge of the map,
|
||||
@ -136,7 +136,7 @@ bool CHFTracer::RayIntersect(const CVector3D& origin, const CVector3D& dir, int&
|
||||
}
|
||||
|
||||
// intersect first against bounding box
|
||||
CBound bound;
|
||||
CBoundingBoxAligned bound;
|
||||
bound[0] = CVector3D(-MARGIN_SIZE * m_CellSize, 0, -MARGIN_SIZE * m_CellSize);
|
||||
bound[1] = CVector3D((m_MapSize + MARGIN_SIZE) * m_CellSize, 65535 * m_HeightScale, (m_MapSize + MARGIN_SIZE) * m_CellSize);
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include "Model.h"
|
||||
#include "ModelDef.h"
|
||||
#include "maths/Quaternion.h"
|
||||
#include "maths/Bound.h"
|
||||
#include "maths/BoundingBoxAligned.h"
|
||||
#include "SkeletonAnim.h"
|
||||
#include "SkeletonAnimDef.h"
|
||||
#include "SkeletonAnimManager.h"
|
||||
@ -34,7 +34,6 @@
|
||||
#include "lib/res/h_mgr.h"
|
||||
#include "lib/sysdep/rtl.h"
|
||||
#include "ps/Profile.h"
|
||||
|
||||
#include "ps/CLogger.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -117,12 +116,12 @@ void CModel::CalcBounds()
|
||||
if (! (m_Anim && m_Anim->m_AnimDef))
|
||||
{
|
||||
if (m_ObjectBounds.IsEmpty())
|
||||
CalcObjectBounds();
|
||||
CalcStaticObjectBounds();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Anim->m_ObjectBounds.IsEmpty())
|
||||
CalcAnimatedObjectBound(m_Anim->m_AnimDef, m_Anim->m_ObjectBounds);
|
||||
CalcAnimatedObjectBounds(m_Anim->m_AnimDef, m_Anim->m_ObjectBounds);
|
||||
ENSURE(! m_Anim->m_ObjectBounds.IsEmpty()); // (if this happens, it'll be recalculating the bounds every time)
|
||||
m_ObjectBounds = m_Anim->m_ObjectBounds;
|
||||
}
|
||||
@ -130,12 +129,13 @@ void CModel::CalcBounds()
|
||||
// Ensure the transform is set correctly before we use it
|
||||
ValidatePosition();
|
||||
|
||||
m_ObjectBounds.Transform(GetTransform(), m_Bounds);
|
||||
// Now transform the object-space bounds to world-space bounds
|
||||
m_ObjectBounds.Transform(GetTransform(), m_WorldBounds);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// CalcObjectBounds: calculate object space bounds of this model, based solely on vertex positions
|
||||
void CModel::CalcObjectBounds()
|
||||
void CModel::CalcStaticObjectBounds()
|
||||
{
|
||||
m_ObjectBounds.SetEmpty();
|
||||
|
||||
@ -149,7 +149,7 @@ void CModel::CalcObjectBounds()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// CalcAnimatedObjectBound: calculate bounds encompassing all vertex positions for given animation
|
||||
void CModel::CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result)
|
||||
void CModel::CalcAnimatedObjectBounds(CSkeletonAnimDef* anim, CBoundingBoxAligned& result)
|
||||
{
|
||||
result.SetEmpty();
|
||||
|
||||
@ -200,14 +200,66 @@ void CModel::CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result)
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const CBound CModel::GetBoundsRec()
|
||||
const CBoundingBoxAligned CModel::GetWorldBoundsRec()
|
||||
{
|
||||
CBound bounds = GetBounds();
|
||||
CBoundingBoxAligned bounds = GetWorldBounds();
|
||||
for (size_t i = 0; i < m_Props.size(); ++i)
|
||||
bounds += m_Props[i].m_Model->GetBoundsRec();
|
||||
bounds += m_Props[i].m_Model->GetWorldBoundsRec();
|
||||
return bounds;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// BuildAnimation: load raw animation frame animation from given file, and build a
|
||||
// animation specific to this model
|
||||
@ -294,6 +346,7 @@ void CModel::ValidatePosition()
|
||||
|
||||
m_Anim->m_AnimDef->BuildBoneMatrices(m_AnimTime, m_BoneMatrices, !(m_Flags & MODELFLAG_NOLOOPANIMATION));
|
||||
|
||||
// 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);
|
||||
@ -313,6 +366,8 @@ void CModel::ValidatePosition()
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -322,9 +377,15 @@ void CModel::ValidatePosition()
|
||||
|
||||
CMatrix3D proptransform = prop.m_Point->m_Transform;;
|
||||
if (prop.m_Point->m_BoneIndex != 0xff)
|
||||
{
|
||||
// m_BoneMatrices[i] already have world transform pre-applied (see above)
|
||||
proptransform.Concatenate(m_BoneMatrices[prop.m_Point->m_BoneIndex]);
|
||||
}
|
||||
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();
|
||||
@ -410,6 +471,8 @@ void CModel::CopyAnimationFrom(CModel* source)
|
||||
void CModel::AddProp(const SPropPoint* point, CModelAbstract* model, CObjectEntry* objectentry)
|
||||
{
|
||||
// position model according to prop point position
|
||||
|
||||
// this next call will invalidate the bounds of "model", which will in turn also invalidate the selection box
|
||||
model->SetTransform(point->m_Transform);
|
||||
model->m_Parent = this;
|
||||
|
||||
@ -426,6 +489,10 @@ void CModel::AddAmmoProp(const SPropPoint* point, CModelAbstract* model, CObject
|
||||
m_AmmoPropPoint = point;
|
||||
m_AmmoLoadedProp = m_Props.size() - 1;
|
||||
m_Props[m_AmmoLoadedProp].m_Hidden = true;
|
||||
|
||||
// we only need to invalidate the selection box here if it is based on props and their visibilities
|
||||
if (!m_CustomSelectionShape)
|
||||
m_SelectionBoxValid = false;
|
||||
}
|
||||
|
||||
void CModel::ShowAmmoProp()
|
||||
@ -437,6 +504,10 @@ void CModel::ShowAmmoProp()
|
||||
for (size_t i = 0; i < m_Props.size(); ++i)
|
||||
if (m_Props[i].m_Point == m_AmmoPropPoint)
|
||||
m_Props[i].m_Hidden = (i != m_AmmoLoadedProp);
|
||||
|
||||
// we only need to invalidate the selection box here if it is based on props and their visibilities
|
||||
if (!m_CustomSelectionShape)
|
||||
m_SelectionBoxValid = false;
|
||||
}
|
||||
|
||||
void CModel::HideAmmoProp()
|
||||
@ -448,6 +519,10 @@ void CModel::HideAmmoProp()
|
||||
for (size_t i = 0; i < m_Props.size(); ++i)
|
||||
if (m_Props[i].m_Point == m_AmmoPropPoint)
|
||||
m_Props[i].m_Hidden = (i == m_AmmoLoadedProp);
|
||||
|
||||
// we only need to invalidate here if the selection box is based on props and their visibilities
|
||||
if (!m_CustomSelectionShape)
|
||||
m_SelectionBoxValid = false;
|
||||
}
|
||||
|
||||
CModelAbstract* CModel::FindFirstAmmoProp()
|
||||
|
@ -54,11 +54,22 @@ public:
|
||||
{
|
||||
Prop() : m_Point(0), m_Model(0), m_ObjectEntry(0), m_Hidden(false) {}
|
||||
|
||||
/**
|
||||
* Location of the prop point within its parent model, relative to either a bone in the parent model or to the
|
||||
* parent model's origin. See the documentation for @ref SPropPoint for more details.
|
||||
* @see SPropPoint
|
||||
*/
|
||||
const SPropPoint* m_Point;
|
||||
|
||||
/**
|
||||
* Pointer to the model associated with this prop. Note that the transform matrix held by this model is the full object-to-world
|
||||
* space transform, taking into account all parent model positioning (see @ref CModel::ValidatePosition for positioning logic).
|
||||
* @see CModel::ValidatePosition
|
||||
*/
|
||||
CModelAbstract* m_Model;
|
||||
CObjectEntry* m_ObjectEntry;
|
||||
|
||||
bool m_Hidden; // temporarily removed from rendering
|
||||
bool m_Hidden; ///< Should this prop be temporarily removed from rendering?
|
||||
};
|
||||
|
||||
public:
|
||||
@ -75,8 +86,6 @@ public:
|
||||
|
||||
// setup model from given geometry
|
||||
bool InitModel(const CModelDefPtr& modeldef);
|
||||
// calculate the world space bounds of this model
|
||||
virtual void CalcBounds();
|
||||
// update this model's state; 'time' is the absolute time since the start of the animation, in MS
|
||||
void UpdateTo(float time);
|
||||
|
||||
@ -133,12 +142,35 @@ public:
|
||||
m_Props[i].m_Model->SetEntityVariable(name, value);
|
||||
}
|
||||
|
||||
// calculate object space bounds of this model, based solely on vertex positions
|
||||
void CalcObjectBounds();
|
||||
// calculate bounds encompassing all vertex positions for given animation
|
||||
void CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result);
|
||||
// --- WORLD/OBJECT SPACE BOUNDS -----------------------------------------------------------------
|
||||
|
||||
virtual const CBound GetBoundsRec();
|
||||
/// Overridden to calculate both the world-space and object-space bounds of this model, and stores the result in
|
||||
/// m_Bounds and m_ObjectBounds, respectively.
|
||||
virtual void CalcBounds();
|
||||
|
||||
/// Returns the object-space bounds for this model, excluding its children.
|
||||
const CBoundingBoxAligned& GetObjectBounds()
|
||||
{
|
||||
RecalculateBoundsIfNecessary(); // recalculates both object-space and world-space bounds if necessary
|
||||
return m_ObjectBounds;
|
||||
}
|
||||
|
||||
virtual const CBoundingBoxAligned GetWorldBoundsRec(); // reimplemented here
|
||||
|
||||
/// Auxiliary method; calculates object space bounds of this model, based solely on vertex positions, and stores
|
||||
/// the result in m_ObjectBounds. Called by CalcBounds (instead of CalcAnimatedObjectBounds) if it has been determined
|
||||
/// that the object-space bounds are static.
|
||||
void CalcStaticObjectBounds();
|
||||
|
||||
/// Auxiliary method; calculate object-space bounds encompassing all vertex positions for given animation, and stores
|
||||
/// the result in m_ObjectBounds. Called by CalcBounds (instead of CalcStaticBounds) if it has been determined that the
|
||||
/// object-space bounds need to take animations into account.
|
||||
void CalcAnimatedObjectBounds(CSkeletonAnimDef* anim,CBoundingBoxAligned& result);
|
||||
|
||||
// --- SELECTION BOX/BOUNDS ----------------------------------------------------------------------
|
||||
|
||||
/// Reimplemented here since proper models should participate in selection boxes.
|
||||
virtual const CBoundingBoxAligned GetObjectSelectionBoundsRec();
|
||||
|
||||
/**
|
||||
* Set transform of this object.
|
||||
@ -236,12 +268,20 @@ private:
|
||||
// object space bounds of model - accounts for bounds of all possible animations
|
||||
// that can play on a model. Not always up-to-date - currently CalcBounds()
|
||||
// updates it when necessary.
|
||||
CBound m_ObjectBounds;
|
||||
CBoundingBoxAligned m_ObjectBounds;
|
||||
// animation currently playing on this model, if any
|
||||
CSkeletonAnim* m_Anim;
|
||||
// time (in MS) into the current animation
|
||||
float m_AnimTime;
|
||||
// current state of all bones on this model; null if associated modeldef isn't skeletal
|
||||
|
||||
/**
|
||||
* Current state of all bones on this model; null if associated modeldef isn't skeletal.
|
||||
* Props may attach to these bones by means of the SPropPoint::m_BoneIndex field; in this case their
|
||||
* transformation matrix held is relative to the bone transformation (see @ref SPropPoint and
|
||||
* @ref CModel::ValidatePosition).
|
||||
*
|
||||
* @see SPropPoint
|
||||
*/
|
||||
CMatrix3D* m_BoneMatrices;
|
||||
// inverse matrices for the bind pose's bones; null if not skeletal
|
||||
CMatrix3D* m_InverseBindBoneMatrices;
|
||||
|
92
source/graphics/ModelAbstract.cpp
Normal file
92
source/graphics/ModelAbstract.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "ModelAbstract.h"
|
||||
|
||||
#include "ps/CLogger.h"
|
||||
|
||||
const CBoundingBoxOriented& CModelAbstract::GetSelectionBox()
|
||||
{
|
||||
if (!m_SelectionBoxValid)
|
||||
{
|
||||
CalcSelectionBox();
|
||||
m_SelectionBoxValid = true;
|
||||
}
|
||||
return m_SelectionBox;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
#ifndef INCLUDED_MODELABSTRACT
|
||||
#define INCLUDED_MODELABSTRACT
|
||||
|
||||
#include "maths/BoundingBoxOriented.h"
|
||||
#include "graphics/RenderableObject.h"
|
||||
#include "ps/Overlay.h"
|
||||
#include "simulation2/helpers/Player.h"
|
||||
@ -37,10 +38,36 @@ class CModelAbstract : public CRenderableObject
|
||||
NONCOPYABLE(CModelAbstract);
|
||||
|
||||
public:
|
||||
CModelAbstract() :
|
||||
m_Parent(NULL), m_PositionValid(false),
|
||||
m_ShadingColor(1, 1, 1, 1), m_PlayerID(INVALID_PLAYER)
|
||||
|
||||
/**
|
||||
* Describes a custom selection shape to be used for a model's selection box instead of the default
|
||||
* recursive bounding boxes.
|
||||
*/
|
||||
struct CustomSelectionShape
|
||||
{
|
||||
enum EType {
|
||||
/// The selection shape is determined by an oriented box of custom, user-specified size.
|
||||
BOX,
|
||||
/// The selection shape is determined by a cylinder of custom, user-specified size.
|
||||
CYLINDER
|
||||
};
|
||||
|
||||
EType m_Type; ///< Type of shape.
|
||||
float m_Size0; ///< Box width if @ref BOX, or radius if @ref CYLINDER
|
||||
float m_Size1; ///< Box depth if @ref BOX, or radius if @ref CYLINDER
|
||||
float m_Height; ///< Box height if @ref BOX, cylinder height if @ref CYLINDER
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
CModelAbstract()
|
||||
: m_Parent(NULL), m_PositionValid(false), m_ShadingColor(1, 1, 1, 1), m_PlayerID(INVALID_PLAYER),
|
||||
m_SelectionBoxValid(false), m_CustomSelectionShape(NULL)
|
||||
{ }
|
||||
|
||||
~CModelAbstract()
|
||||
{
|
||||
delete m_CustomSelectionShape; // allocated and set externally by CCmpVisualActor, but our responsibility to clean up
|
||||
}
|
||||
|
||||
virtual CModelAbstract* Clone() const = 0;
|
||||
@ -58,15 +85,47 @@ public:
|
||||
// and this seems the easiest way to integrate with other code that wants
|
||||
// type-specific processing)
|
||||
|
||||
/**
|
||||
* Calls SetDirty on this model and all child objects.
|
||||
*/
|
||||
/// Calls SetDirty on this model and all child objects.
|
||||
virtual void SetDirtyRec(int dirtyflags) = 0;
|
||||
|
||||
/// Returns world space bounds of this object and all child objects.
|
||||
virtual const CBoundingBoxAligned GetWorldBoundsRec() { return GetWorldBounds(); }
|
||||
|
||||
/**
|
||||
* Returns world space bounds of this object and all child objects.
|
||||
* Returns the world-space selection box of this model. Used primarily for hittesting against against a selection ray. The
|
||||
* returned selection box may be empty to indicate that it does not wish to participate in the selection process.
|
||||
*/
|
||||
virtual const CBound GetBoundsRec() { return GetBounds(); }
|
||||
virtual const CBoundingBoxOriented& GetSelectionBox();
|
||||
|
||||
virtual void InvalidateBounds()
|
||||
{
|
||||
m_BoundsValid = false;
|
||||
// a call to this method usually means that the model's transform has changed, i.e. it has moved or rotated, so we'll also
|
||||
// want to update the selection box accordingly regardless of the shape it is built from.
|
||||
m_SelectionBoxValid = false;
|
||||
}
|
||||
|
||||
/// Sets a custom selection shape as described by a @p descriptor. Argument may be NULL
|
||||
/// if you wish to keep the default behaviour of using the recursively-calculated bounding boxes.
|
||||
void SetCustomSelectionShape(CustomSelectionShape* descriptor)
|
||||
{
|
||||
if (m_CustomSelectionShape != descriptor)
|
||||
{
|
||||
m_CustomSelectionShape = descriptor;
|
||||
m_SelectionBoxValid = false; // update the selection box when it is next requested
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the (object-space) bounds that should be used to construct a selection box for this model and its children.
|
||||
* May return an empty bound to indicate that this model and its children should not be selectable themselves, or should
|
||||
* not be included in its parent model's selection box. This method is used for constructing the default selection boxes,
|
||||
* as opposed to any boxes of custom shape specified by @ref m_CustomSelectionShape.
|
||||
*
|
||||
* If you wish your model type to be included in selection boxes, override this method and have it return the object-space
|
||||
* bounds of itself, augmented recursively (via this method) with the object-space selection bounds of its children.
|
||||
*/
|
||||
virtual const CBoundingBoxAligned GetObjectSelectionBoundsRec() { return CBoundingBoxAligned::EMPTY; }
|
||||
|
||||
/**
|
||||
* Called when terrain has changed in the given inclusive bounds.
|
||||
@ -81,14 +140,12 @@ public:
|
||||
virtual void SetEntityVariable(const std::string& UNUSED(name), float UNUSED(value)) { }
|
||||
|
||||
/**
|
||||
* Ensure that both the transformation and the bone
|
||||
* matrices are correct for this model and all its props.
|
||||
* Ensure that both the transformation and the bone matrices are correct for this model and all its props.
|
||||
*/
|
||||
virtual void ValidatePosition() = 0;
|
||||
|
||||
/**
|
||||
* Mark this model's position and bone matrices,
|
||||
* and all props' positions as invalid.
|
||||
* Mark this model's position and bone matrices, and all props' positions as invalid.
|
||||
*/
|
||||
virtual void InvalidatePosition() = 0;
|
||||
|
||||
@ -100,7 +157,11 @@ public:
|
||||
virtual void SetShadingColor(const CColor& colour) { m_ShadingColor = colour; }
|
||||
virtual CColor GetShadingColor() const { return m_ShadingColor; }
|
||||
|
||||
/// If non-null points to the model that we are attached to.
|
||||
protected:
|
||||
void CalcSelectionBox();
|
||||
|
||||
public:
|
||||
/// If non-null, points to the model that we are attached to.
|
||||
CModelAbstract* m_Parent;
|
||||
|
||||
/// True if both transform and and bone matrices are valid.
|
||||
@ -108,8 +169,23 @@ public:
|
||||
|
||||
player_id_t m_PlayerID;
|
||||
|
||||
// modulating color
|
||||
/// Modulating color
|
||||
CColor m_ShadingColor;
|
||||
|
||||
protected:
|
||||
|
||||
/// Selection box for this model.
|
||||
CBoundingBoxOriented m_SelectionBox;
|
||||
|
||||
/// Is the current selection box valid?
|
||||
bool m_SelectionBoxValid;
|
||||
|
||||
/// Pointer to a descriptor for a custom-defined selection box shape. If no custom selection box is required, this is NULL
|
||||
/// and the standard recursive-bounding-box-based selection box is used. Otherwise, a custom selection box described by this
|
||||
/// field will be used.
|
||||
/// @see SetCustomSelectionShape
|
||||
CustomSelectionShape* m_CustomSelectionShape;
|
||||
|
||||
};
|
||||
|
||||
#endif // INCLUDED_MODELABSTRACT
|
||||
|
@ -32,19 +32,49 @@
|
||||
|
||||
class CBoneState;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// SPropPoint: structure describing a prop point
|
||||
/**
|
||||
* Describes the position of a prop point within its parent model. A prop point is the location within a parent model
|
||||
* where the prop's origin will be attached.
|
||||
*
|
||||
* A prop point is specified by its transformation matrix (or separately by its position and rotation), which
|
||||
* can be relative to either the parent model's origin, or one of the parent's bones. If the parent model is boned,
|
||||
* then the @ref m_BoneIndex field may specify a bone to which the transformation matrix is relative (see
|
||||
* @ref CModel::m_BoneMatrices). Otherwise, the transformation matrix is assumed to be relative to the parent model's
|
||||
* origin.
|
||||
*
|
||||
* @see CModel::m_BoneMatrices
|
||||
*/
|
||||
struct SPropPoint
|
||||
{
|
||||
// name of the prop point
|
||||
/// Name of the prop point
|
||||
CStr m_Name;
|
||||
// position of the point
|
||||
|
||||
/**
|
||||
* Position of the point within the parent model, relative to either the parent model's origin or one of the parent
|
||||
* model's bones if applicable. Also specified as part of @ref m_Transform.
|
||||
* @see m_Transform
|
||||
*/
|
||||
CVector3D m_Position;
|
||||
// rotation of the point
|
||||
|
||||
/**
|
||||
* Rotation of the prop model that will be attached at this point. Also specified as part of @ref m_Transform.
|
||||
* @see m_Transform
|
||||
*/
|
||||
CQuaternion m_Rotation;
|
||||
// object to parent space transformation
|
||||
|
||||
/**
|
||||
* Object to parent space transformation. Combines both @ref m_Position and @ref m_Rotation in a single
|
||||
* transformation matrix. This transformation is relative to either the parent model's origin, or one of its
|
||||
* bones, depending on whether it is skeletal. If relative to a bone, then the bone in the parent model to
|
||||
* which this transformation is relative may be found by m_BoneIndex.
|
||||
* @see m_Position, m_Rotation
|
||||
*/
|
||||
CMatrix3D m_Transform;
|
||||
// index of parent bone; 0xff if unboned
|
||||
|
||||
/**
|
||||
* Index of parent bone to which this prop point is relative, if any. The value 0xFF specifies that either the parent
|
||||
* model is unboned, or that this prop point is relative to the parent model's origin rather than one if its bones.
|
||||
*/
|
||||
u8 m_BoneIndex;
|
||||
};
|
||||
|
||||
@ -246,3 +276,4 @@ private:
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -129,7 +129,7 @@ bool CObjectEntry::BuildVariation(const std::vector<std::set<CStr> >& selections
|
||||
model->SetTexture(texture);
|
||||
|
||||
// calculate initial object space bounds, based on vertex positions
|
||||
model->CalcObjectBounds();
|
||||
model->CalcStaticObjectBounds();
|
||||
|
||||
// load the animations
|
||||
for (std::multimap<CStr, CObjectBase::Anim>::iterator it = variation.anims.begin(); it != variation.anims.end(); ++it)
|
||||
|
@ -36,6 +36,11 @@ struct SOverlayLine
|
||||
CColor m_Color;
|
||||
std::vector<float> m_Coords; // (x, y, z) vertex coordinate triples; shape is not automatically closed
|
||||
u8 m_Thickness; // pixels
|
||||
|
||||
/// Utility function; pushes three vertex coordinates at once onto the coordinates array
|
||||
void PushCoords(const float x, const float y, const float z) { m_Coords.push_back(x); m_Coords.push_back(y); m_Coords.push_back(z); }
|
||||
/// Utility function; pushes a vertex location onto the coordinates array
|
||||
void PushCoords(const CVector3D& v) { PushCoords(v.X, v.Y, v.Z); }
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -77,7 +77,7 @@ void CParticleEmitter::UpdateArrayData()
|
||||
|
||||
ENSURE(m_Particles.size() <= m_Type->m_MaxParticles);
|
||||
|
||||
CBound bounds;
|
||||
CBoundingBoxAligned bounds;
|
||||
|
||||
for (size_t i = 0; i < m_Particles.size(); ++i)
|
||||
{
|
||||
@ -232,7 +232,7 @@ void CModelParticleEmitter::CalcBounds()
|
||||
// current computed particle positions plus the emitter type's largest
|
||||
// potential bounding box at the current position
|
||||
|
||||
m_Bounds = m_Type->CalculateBounds(m_Emitter->GetPosition(), m_Emitter->GetParticleBounds());
|
||||
m_WorldBounds = m_Type->CalculateBounds(m_Emitter->GetPosition(), m_Emitter->GetParticleBounds());
|
||||
}
|
||||
|
||||
void CModelParticleEmitter::ValidatePosition()
|
||||
|
@ -83,7 +83,7 @@ public:
|
||||
/**
|
||||
* Get the bounding box of the center points of particles at their current positions.
|
||||
*/
|
||||
CBound GetParticleBounds() { return m_ParticleBounds; }
|
||||
CBoundingBoxAligned GetParticleBounds() { return m_ParticleBounds; }
|
||||
|
||||
/**
|
||||
* Push a new particle onto the ring buffer. (May overwrite an old particle.)
|
||||
@ -133,7 +133,7 @@ public:
|
||||
|
||||
private:
|
||||
/// Bounding box of the current particle center points
|
||||
CBound m_ParticleBounds;
|
||||
CBoundingBoxAligned m_ParticleBounds;
|
||||
|
||||
VertexArray m_VertexArray;
|
||||
VertexArray::Attribute m_AttributePos;
|
||||
|
@ -579,9 +579,9 @@ void CParticleEmitterType::UpdateEmitterStep(CParticleEmitter& emitter, float dt
|
||||
}
|
||||
}
|
||||
|
||||
CBound CParticleEmitterType::CalculateBounds(CVector3D emitterPos, CBound emittedBounds)
|
||||
CBoundingBoxAligned CParticleEmitterType::CalculateBounds(CVector3D emitterPos, CBoundingBoxAligned emittedBounds)
|
||||
{
|
||||
CBound bounds = m_MaxBounds;
|
||||
CBoundingBoxAligned bounds = m_MaxBounds;
|
||||
bounds[0] += emitterPos;
|
||||
bounds[1] += emitterPos;
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "graphics/Texture.h"
|
||||
#include "lib/ogl.h"
|
||||
#include "lib/file/vfs/vfs_path.h"
|
||||
#include "maths/Bound.h"
|
||||
#include "maths/BoundingBoxAligned.h"
|
||||
|
||||
class CVector3D;
|
||||
class CParticleEmitter;
|
||||
@ -88,7 +88,7 @@ private:
|
||||
*/
|
||||
void UpdateEmitterStep(CParticleEmitter& emitter, float dt);
|
||||
|
||||
CBound CalculateBounds(CVector3D emitterPos, CBound emittedBounds);
|
||||
CBoundingBoxAligned CalculateBounds(CVector3D emitterPos, CBoundingBoxAligned emittedBounds);
|
||||
|
||||
CTexturePtr m_Texture;
|
||||
|
||||
@ -99,7 +99,7 @@ private:
|
||||
|
||||
float m_MaxLifetime;
|
||||
size_t m_MaxParticles;
|
||||
CBound m_MaxBounds;
|
||||
CBoundingBoxAligned m_MaxBounds;
|
||||
|
||||
typedef shared_ptr<IParticleVar> IParticleVarPtr;
|
||||
std::vector<IParticleVarPtr> m_Variables;
|
||||
|
@ -57,7 +57,7 @@ void CPatch::Initialize(CTerrain* parent,ssize_t x,ssize_t z)
|
||||
// CalcBounds: calculating the bounds of this patch
|
||||
void CPatch::CalcBounds()
|
||||
{
|
||||
m_Bounds.SetEmpty();
|
||||
m_WorldBounds.SetEmpty();
|
||||
|
||||
for (ssize_t j=0;j<PATCH_SIZE+1;j++)
|
||||
{
|
||||
@ -65,14 +65,14 @@ void CPatch::CalcBounds()
|
||||
{
|
||||
CVector3D pos;
|
||||
m_Parent->CalcPosition(m_X*PATCH_SIZE+i,m_Z*PATCH_SIZE+j,pos);
|
||||
m_Bounds+=pos;
|
||||
m_WorldBounds+=pos;
|
||||
}
|
||||
}
|
||||
|
||||
// If this a side patch, the sides go down to height 0, so add them
|
||||
// into the bounds
|
||||
if (GetSideFlags())
|
||||
m_Bounds[0].Y = std::min(m_Bounds[0].Y, 0.f);
|
||||
m_WorldBounds[0].Y = std::min(m_WorldBounds[0].Y, 0.f);
|
||||
}
|
||||
|
||||
int CPatch::GetSideFlags()
|
||||
|
@ -23,7 +23,7 @@
|
||||
#define INCLUDED_RENDERABLEOBJECT
|
||||
|
||||
|
||||
#include "maths/Bound.h"
|
||||
#include "maths/BoundingBoxAligned.h"
|
||||
#include "maths/Matrix3D.h"
|
||||
|
||||
|
||||
@ -86,20 +86,29 @@ public:
|
||||
if (m_RenderData) m_RenderData->m_UpdateFlags|=dirtyflags;
|
||||
}
|
||||
|
||||
// calculate (and store in m_Bounds) the world space bounds of this object
|
||||
// - must be implemented by all concrete subclasses
|
||||
/**
|
||||
* (Re)calculates and stores any bounds or bound-dependent data for this object. At this abstraction level, this is only the world-space
|
||||
* bounds stored in @ref m_WorldBounds; subclasses may use this method to (re)compute additional bounds if necessary, or any data that
|
||||
* depends on the bounds. Whenever bound-dependent data is requested through a public interface, @ref RecalculateBoundsIfNecessary should
|
||||
* be called first to ensure bound correctness, which will in turn call this method if it turns out that they're outdated.
|
||||
*
|
||||
* @see m_BoundsValid
|
||||
* @see RecalculateBoundsIfNecessary
|
||||
*/
|
||||
virtual void CalcBounds() = 0;
|
||||
|
||||
// return world space bounds of this object
|
||||
const CBound& GetBounds() {
|
||||
if (! m_BoundsValid) {
|
||||
CalcBounds();
|
||||
m_BoundsValid = true;
|
||||
}
|
||||
return m_Bounds;
|
||||
/// Returns the world-space axis-aligned bounds of this object.
|
||||
const CBoundingBoxAligned& GetWorldBounds() {
|
||||
RecalculateBoundsIfNecessary();
|
||||
return m_WorldBounds;
|
||||
}
|
||||
|
||||
void InvalidateBounds() { m_BoundsValid = false; }
|
||||
/**
|
||||
* Marks the bounds as invalid. This will trigger @ref RecalculateBoundsIfNecessary to recompute any bound-related data the next time
|
||||
* any bound-related data is requested through a public interface -- at least, if you've made sure to call it before returning the
|
||||
* stored data.
|
||||
*/
|
||||
virtual void InvalidateBounds() { m_BoundsValid = false; }
|
||||
|
||||
// Set the object renderdata and free previous renderdata, if any.
|
||||
void SetRenderData(CRenderData* renderdata) {
|
||||
@ -107,13 +116,23 @@ public:
|
||||
m_RenderData = renderdata;
|
||||
}
|
||||
|
||||
// return object renderdata - can be null if renderer hasn't yet
|
||||
// created the renderdata
|
||||
/// Return object renderdata - can be null if renderer hasn't yet created the renderdata
|
||||
CRenderData* GetRenderData() { return m_RenderData; }
|
||||
|
||||
protected:
|
||||
// object bounds
|
||||
CBound m_Bounds;
|
||||
/// Factored out so subclasses don't need to repeat this if they want to add additional getters for bounds-related methods
|
||||
/// (since they'll have to make sure to recalc the bounds if necessary before they return it).
|
||||
void RecalculateBoundsIfNecessary()
|
||||
{
|
||||
if (!m_BoundsValid) {
|
||||
CalcBounds();
|
||||
m_BoundsValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
/// World-space bounds of this object
|
||||
CBoundingBoxAligned m_WorldBounds;
|
||||
// local->world space transform
|
||||
CMatrix3D m_Transform;
|
||||
// world->local space transform
|
||||
@ -121,8 +140,17 @@ protected:
|
||||
// object renderdata
|
||||
CRenderData* m_RenderData;
|
||||
|
||||
private:
|
||||
// remembers whether m_bounds needs to be recalculated
|
||||
/**
|
||||
* Remembers whether any bounds need to be recalculated. Subclasses that add any data that depends on the bounds should
|
||||
* take care to consider the validity of the bounds and recalculate their data when necessary -- overriding @ref CalcBounds
|
||||
* to do so would be a good idea, since it's already set up to be called by @ref RecalculateBoundsIfNecessary whenever the
|
||||
* bounds are marked as invalid. The latter should then be called before returning any bounds or bounds-derived data through
|
||||
* a public interface (see the implementation of @ref GetWorldBounds for an example).
|
||||
*
|
||||
* @see CalcBounds
|
||||
* @see InvalidateBounds
|
||||
* @see RecalculateBoundsIfNecessary
|
||||
*/
|
||||
bool m_BoundsValid;
|
||||
};
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#ifndef INCLUDED_SKELETONANIM
|
||||
#define INCLUDED_SKELETONANIM
|
||||
|
||||
#include "maths/Bound.h"
|
||||
#include "maths/BoundingBoxAligned.h"
|
||||
|
||||
class CSkeletonAnimDef;
|
||||
|
||||
@ -47,7 +47,7 @@ public:
|
||||
float m_ActionPos;
|
||||
float m_ActionPos2;
|
||||
// object space bounds of the model when this animation is applied to it
|
||||
CBound m_ObjectBounds;
|
||||
CBoundingBoxAligned m_ObjectBounds;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -625,7 +625,7 @@ void CTerrain::MakeDirty(int dirtyFlags)
|
||||
}
|
||||
}
|
||||
|
||||
CBound CTerrain::GetVertexesBound(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1)
|
||||
CBoundingBoxAligned CTerrain::GetVertexesBound(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1)
|
||||
{
|
||||
i0 = clamp(i0, (ssize_t)0, m_MapSize-1);
|
||||
j0 = clamp(j0, (ssize_t)0, m_MapSize-1);
|
||||
@ -644,7 +644,7 @@ CBound CTerrain::GetVertexesBound(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1
|
||||
}
|
||||
}
|
||||
|
||||
CBound bound;
|
||||
CBoundingBoxAligned bound;
|
||||
bound[0].X = (float)(i0*CELL_SIZE);
|
||||
bound[0].Y = (float)(minH*HEIGHT_SCALE);
|
||||
bound[0].Z = (float)(j0*CELL_SIZE);
|
||||
|
@ -30,7 +30,7 @@ class CPatch;
|
||||
class CMiniPatch;
|
||||
class CFixedVector3D;
|
||||
class CStr8;
|
||||
class CBound;
|
||||
class CBoundingBoxAligned;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Terrain Constants:
|
||||
@ -140,7 +140,7 @@ public:
|
||||
/**
|
||||
* Returns a 3D bounding box encompassing the given vertex range (inclusive)
|
||||
*/
|
||||
CBound GetVertexesBound(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1);
|
||||
CBoundingBoxAligned GetVertexesBound(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1);
|
||||
|
||||
// get the base colour for the terrain (typically pure white - other colours
|
||||
// will interact badly with LOS - but used by the Actor Viewer tool)
|
||||
|
@ -100,15 +100,16 @@ CUnit* CUnitManager::PickUnit(const CVector3D& origin, const CVector3D& dir) con
|
||||
CUnit* unit = m_Units[i];
|
||||
float tmin, tmax;
|
||||
|
||||
if (unit->GetModel().GetBounds().RayIntersect(origin, dir, tmin, tmax))
|
||||
const CBoundingBoxOriented& selectionBox = unit->GetModel().GetSelectionBox();
|
||||
if (selectionBox.RayIntersect(origin, dir, tmin, tmax))
|
||||
{
|
||||
// Point of closest approach
|
||||
CVector3D obj;
|
||||
unit->GetModel().GetBounds().GetCentre(obj);
|
||||
CVector3D delta = obj - origin;
|
||||
// TODO: this next bit is virtually identical to Selection::PickEntitiesAtPoint; might be useful to factor it out and
|
||||
// reuse it
|
||||
CVector3D delta = selectionBox.m_Center - origin;
|
||||
float distance = delta.Dot(dir);
|
||||
CVector3D closest = origin + dir * distance;
|
||||
CVector3D offset = obj - closest;
|
||||
CVector3D offset = selectionBox.m_Center - closest;
|
||||
|
||||
float rel = offset.Length();
|
||||
if (rel < minrel) {
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "simulation2/components/ICmpGuiInterface.h"
|
||||
#include "simulation2/components/ICmpRangeManager.h"
|
||||
#include "simulation2/components/ICmpTemplateManager.h"
|
||||
#include "simulation2/components/ICmpSelectable.h"
|
||||
#include "simulation2/helpers/Selection.h"
|
||||
|
||||
#include "js/jsapi.h"
|
||||
@ -518,6 +519,10 @@ void QuickLoad(void* UNUSED(cbdata))
|
||||
{
|
||||
g_Game->GetTurnManager()->QuickLoad();
|
||||
}
|
||||
void SetBoundingBoxDebugOverlay(void* UNUSED(cbdata), bool enabled)
|
||||
{
|
||||
ICmpSelectable::ms_EnableDebugOverlays = enabled;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -590,4 +595,5 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
|
||||
scriptInterface.RegisterFunction<void, &DumpSimState>("DumpSimState");
|
||||
scriptInterface.RegisterFunction<void, unsigned int, &EnableTimeWarpRecording>("EnableTimeWarpRecording");
|
||||
scriptInterface.RegisterFunction<void, &RewindTimeWarp>("RewindTimeWarp");
|
||||
scriptInterface.RegisterFunction<void, bool, &SetBoundingBoxDebugOverlay>("SetBoundingBoxDebugOverlay");
|
||||
}
|
||||
|
@ -21,23 +21,25 @@
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "Bound.h"
|
||||
#include "BoundingBoxAligned.h"
|
||||
|
||||
#include "lib/ogl.h"
|
||||
|
||||
#include <float.h>
|
||||
|
||||
#include "graphics/Frustum.h"
|
||||
#include "maths/BoundingBoxOriented.h"
|
||||
#include "maths/Brush.h"
|
||||
#include "maths/Matrix3D.h"
|
||||
|
||||
const CBoundingBoxAligned CBoundingBoxAligned::EMPTY = CBoundingBoxAligned(); // initializes to an empty bound
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RayIntersect: intersect ray with this bound; return true
|
||||
// if ray hits (and store entry and exit times), or false
|
||||
// otherwise
|
||||
// note: incoming ray direction must be normalised
|
||||
bool CBound::RayIntersect(const CVector3D& origin,const CVector3D& dir,
|
||||
bool CBoundingBoxAligned::RayIntersect(const CVector3D& origin,const CVector3D& dir,
|
||||
float& tmin,float& tmax) const
|
||||
{
|
||||
float t1,t2;
|
||||
@ -118,7 +120,7 @@ bool CBound::RayIntersect(const CVector3D& origin,const CVector3D& dir,
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// SetEmpty: initialise this bound as empty
|
||||
void CBound::SetEmpty()
|
||||
void CBoundingBoxAligned::SetEmpty()
|
||||
{
|
||||
m_Data[0]=CVector3D( FLT_MAX, FLT_MAX, FLT_MAX);
|
||||
m_Data[1]=CVector3D(-FLT_MAX,-FLT_MAX,-FLT_MAX);
|
||||
@ -126,7 +128,7 @@ void CBound::SetEmpty()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// IsEmpty: tests whether this bound is empty
|
||||
bool CBound::IsEmpty() const
|
||||
bool CBoundingBoxAligned::IsEmpty() const
|
||||
{
|
||||
return (m_Data[0].X == FLT_MAX && m_Data[0].Y == FLT_MAX && m_Data[0].Z == FLT_MAX
|
||||
&& m_Data[1].X == -FLT_MAX && m_Data[1].Y == -FLT_MAX && m_Data[1].Z == -FLT_MAX);
|
||||
@ -136,7 +138,7 @@ bool CBound::IsEmpty() const
|
||||
// Transform: transform this bound by given matrix; return transformed bound
|
||||
// in 'result' parameter - slightly modified version of code in Graphic Gems
|
||||
// (can't remember which one it was, though)
|
||||
void CBound::Transform(const CMatrix3D& m,CBound& result) const
|
||||
void CBoundingBoxAligned::Transform(const CMatrix3D& m, CBoundingBoxAligned& result) const
|
||||
{
|
||||
ENSURE(this!=&result);
|
||||
|
||||
@ -161,10 +163,59 @@ void CBound::Transform(const CMatrix3D& m,CBound& result) const
|
||||
}
|
||||
}
|
||||
|
||||
void CBoundingBoxAligned::Transform(const CMatrix3D& transform, CBoundingBoxOriented& result) const
|
||||
{
|
||||
// The idea is this: compute the corners of this bounding box, transform them according to the specified matrix,
|
||||
// then derive the box center, orientation vectors, and half-sizes.
|
||||
// TODO: this implementation can be further optimized; see Philip's comments on http://trac.wildfiregames.com/ticket/914
|
||||
const CVector3D& pMin = m_Data[0];
|
||||
const CVector3D& pMax = m_Data[1];
|
||||
|
||||
// Find the corners of these bounds. We only need some of the corners to derive the information we need, so let's
|
||||
// not actually compute all of them. The corners are numbered starting from the minimum position (m_Data[0]), going
|
||||
// counter-clockwise in the bottom plane, and then in the same order for the top plane (starting from the corner
|
||||
// that's directly above the minimum position). Hence, corner0 is pMin and corner6 is pMax, so we don't need to
|
||||
// custom-create those.
|
||||
|
||||
CVector3D corner0; // corner0 is pMin, no need to copy it
|
||||
CVector3D corner1(pMax.X, pMin.Y, pMin.Z);
|
||||
CVector3D corner3(pMin.X, pMin.Y, pMax.Z);
|
||||
CVector3D corner4(pMin.X, pMax.Y, pMin.Z);
|
||||
CVector3D corner6; // corner6 is pMax, no need to copy it
|
||||
|
||||
// transform corners to world space
|
||||
corner0 = transform.Transform(pMin); // = corner0
|
||||
corner1 = transform.Transform(corner1);
|
||||
corner3 = transform.Transform(corner3);
|
||||
corner4 = transform.Transform(corner4);
|
||||
corner6 = transform.Transform(pMax); // = corner6
|
||||
|
||||
// Compute orientation vectors, half-size vector, and box center. We can get the orientation vectors by just taking
|
||||
// the directional vectors from a specific corner point (corner0) to the other corners, once in each direction. The
|
||||
// half-sizes are similarly computed by taking the distances of those sides and dividing them by 2. Finally, the
|
||||
// center is simply the middle between the transformed pMin and pMax corners.
|
||||
|
||||
const CVector3D sideU(corner1 - corner0);
|
||||
const CVector3D sideV(corner4 - corner0);
|
||||
const CVector3D sideW(corner3 - corner0);
|
||||
|
||||
result.m_Basis[0] = sideU.Normalized();
|
||||
result.m_Basis[1] = sideV.Normalized();
|
||||
result.m_Basis[2] = sideW.Normalized();
|
||||
|
||||
result.m_HalfSizes = CVector3D(
|
||||
sideU.Length()/2.f,
|
||||
sideV.Length()/2.f,
|
||||
sideW.Length()/2.f
|
||||
);
|
||||
|
||||
result.m_Center = (corner0 + corner6) * 0.5f;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Intersect with the given frustum in a conservative manner
|
||||
void CBound::IntersectFrustumConservative(const CFrustum& frustum)
|
||||
void CBoundingBoxAligned::IntersectFrustumConservative(const CFrustum& frustum)
|
||||
{
|
||||
CBrush brush(*this);
|
||||
CBrush buf;
|
||||
@ -176,7 +227,7 @@ void CBound::IntersectFrustumConservative(const CFrustum& frustum)
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void CBound::Expand(float amount)
|
||||
void CBoundingBoxAligned::Expand(float amount)
|
||||
{
|
||||
m_Data[0] -= CVector3D(amount, amount, amount);
|
||||
m_Data[1] += CVector3D(amount, amount, amount);
|
||||
@ -184,7 +235,7 @@ void CBound::Expand(float amount)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Render the bounding box
|
||||
void CBound::Render() const
|
||||
void CBoundingBoxAligned::Render() const
|
||||
{
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(0, 0); glVertex3f(m_Data[0].X, m_Data[0].Y, m_Data[0].Z);
|
@ -27,20 +27,34 @@
|
||||
|
||||
class CFrustum;
|
||||
class CMatrix3D;
|
||||
class CBoundingBoxOriented;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CBound: basic axis aligned bounding box class
|
||||
class CBound
|
||||
// basic axis aligned bounding box (AABB) class
|
||||
class CBoundingBoxAligned
|
||||
{
|
||||
public:
|
||||
CBound() { SetEmpty(); }
|
||||
CBound(const CVector3D& min,const CVector3D& max) {
|
||||
m_Data[0]=min; m_Data[1]=max;
|
||||
|
||||
CBoundingBoxAligned() { SetEmpty(); }
|
||||
CBoundingBoxAligned(const CVector3D& min, const CVector3D& max) {
|
||||
m_Data[0] = min;
|
||||
m_Data[1] = max;
|
||||
}
|
||||
|
||||
void Transform(const CMatrix3D& m,CBound& result) const;
|
||||
/**
|
||||
* Transforms these bounds according to the specified transformation matrix @m, and writes the axis-aligned bounds
|
||||
* of that result to @p result.
|
||||
*/
|
||||
void Transform(const CMatrix3D& m, CBoundingBoxAligned& result) const;
|
||||
|
||||
CVector3D& operator[](int index) { return m_Data[index]; }
|
||||
/**
|
||||
* Transform these bounds using the matrix @p transform, and write out the result as an oriented (i.e. non-axis-aligned) box.
|
||||
* The difference with @ref Transform(const CMatrix3D&, CBoundingBoxAligned&) is that that method is equivalent to first
|
||||
* computing this result, and then from that taking the axis-aligned bounding boxes again.
|
||||
*/
|
||||
void Transform(const CMatrix3D& m, CBoundingBoxOriented& result) const;
|
||||
|
||||
CVector3D& operator[](int index) { return m_Data[index]; }
|
||||
const CVector3D& operator[](int index) const { return m_Data[index]; }
|
||||
|
||||
void SetEmpty();
|
||||
@ -57,30 +71,43 @@ public:
|
||||
}
|
||||
|
||||
// operator+=: extend this bound to include given bound
|
||||
CBound& operator+=(const CBound& b)
|
||||
CBoundingBoxAligned& operator+=(const CBoundingBoxAligned& b)
|
||||
{
|
||||
Extend(b.m_Data[0], b.m_Data[1]);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// operator+=: extend this bound to include given point
|
||||
CBound& operator+=(const CVector3D& pt)
|
||||
CBoundingBoxAligned& operator+=(const CVector3D& pt)
|
||||
{
|
||||
Extend(pt, pt);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool RayIntersect(const CVector3D& origin,const CVector3D& dir,float& tmin,float& tmax) const;
|
||||
/**
|
||||
* Returns true if the ray originating in @p origin and with unit direction vector @p dir intersects this AABB, false otherwise.
|
||||
* Additionally, returns the distance in the positive direction from the origin of the ray to the entry and exit points in
|
||||
* the bounding box in @p tmin and @p tmax. If the origin is inside the box, then this is counted as an intersection and one
|
||||
* of @p tMin and @p tMax may be negative.
|
||||
*
|
||||
* See also Real-Time Rendering, Third Edition by T. Akenine-Moller, p. 741--742.
|
||||
*
|
||||
* @param origin Origin of the ray.
|
||||
* @param dir Direction vector of the ray, defining the positive direction of the ray. Must be of unit length.
|
||||
*/
|
||||
bool RayIntersect(const CVector3D& origin, const CVector3D& dir, float& tmin, float& tmax) const;
|
||||
|
||||
// return the volume of this bounding box
|
||||
float GetVolume() const {
|
||||
CVector3D v=m_Data[1]-m_Data[0];
|
||||
return std::max(v.X, 0.0f)*std::max(v.Y, 0.0f)*std::max(v.Z, 0.0f);
|
||||
float GetVolume() const
|
||||
{
|
||||
CVector3D v = m_Data[1] - m_Data[0];
|
||||
return (std::max(v.X, 0.0f) * std::max(v.Y, 0.0f) * std::max(v.Z, 0.0f));
|
||||
}
|
||||
|
||||
// return the centre of this bounding box
|
||||
void GetCentre(CVector3D& centre) const {
|
||||
centre=(m_Data[0]+m_Data[1])*0.5f;
|
||||
void GetCentre(CVector3D& centre) const
|
||||
{
|
||||
centre = (m_Data[0] + m_Data[1]) * 0.5f;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -109,7 +136,12 @@ public:
|
||||
void Render() const;
|
||||
|
||||
private:
|
||||
// Holds the minimal and maximal coordinate points in m_Data[0] and m_Data[1], respectively.
|
||||
CVector3D m_Data[2];
|
||||
|
||||
public:
|
||||
static const CBoundingBoxAligned EMPTY;
|
||||
|
||||
};
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
86
source/maths/BoundingBoxOriented.cpp
Normal file
86
source/maths/BoundingBoxOriented.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "BoundingBoxOriented.h"
|
||||
#include "maths/BoundingBoxAligned.h"
|
||||
|
||||
#include <float.h>
|
||||
|
||||
const CBoundingBoxOriented CBoundingBoxOriented::EMPTY = CBoundingBoxOriented();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
bool CBoundingBoxOriented::RayIntersect(const CVector3D& origin, const CVector3D& dir, float& tMin_out, float& tMax_out) const
|
||||
{
|
||||
// See Real-Time Rendering, Third Edition, p. 743
|
||||
float tMin = -FLT_MAX;
|
||||
float tMax = FLT_MAX;
|
||||
|
||||
CVector3D p = m_Center - origin;
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
float e = m_Basis[i].Dot(p);
|
||||
float f = m_Basis[i].Dot(dir);
|
||||
|
||||
if(fabs(f) > 1e-10f)
|
||||
{
|
||||
float invF = 1.f/f;
|
||||
float t1 = (e + m_HalfSizes[i]) * invF;
|
||||
float t2 = (e - m_HalfSizes[i]) * invF;
|
||||
|
||||
if (t1 > t2)
|
||||
{
|
||||
float tmp = t1;
|
||||
t1 = t2;
|
||||
t2 = tmp;
|
||||
}
|
||||
if (t1 > tMin) tMin = t1;
|
||||
if (t2 < tMax) tMax = t2;
|
||||
if (tMin > tMax) return false;
|
||||
if (tMax < 0) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(-e - m_HalfSizes[i] > 0 || -e + m_HalfSizes[i] < 0) return false;
|
||||
}
|
||||
}
|
||||
|
||||
tMin_out = tMin;
|
||||
tMax_out = tMax;
|
||||
return true;
|
||||
}
|
107
source/maths/BoundingBoxOriented.h
Normal file
107
source/maths/BoundingBoxOriented.h
Normal file
@ -0,0 +1,107 @@
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_BOX
|
||||
#define INCLUDED_BOX
|
||||
|
||||
#include "maths/Vector3D.h"
|
||||
|
||||
class CBoundingBoxAligned;
|
||||
|
||||
/*
|
||||
* Generic oriented box. Originally intended to be used an Oriented Bounding Box (OBB), as opposed to CBoundingBoxAligned which is
|
||||
* always aligned to the world-space axes (AABB). However, it can also be used to represent more generic shapes, such as
|
||||
* parallelepipeds, for other purposes.
|
||||
*/
|
||||
class CBoundingBoxOriented
|
||||
{
|
||||
public:
|
||||
|
||||
/// Empty constructor; creates an empty box
|
||||
CBoundingBoxOriented() { SetEmpty(); }
|
||||
|
||||
/**
|
||||
* Constructs a new oriented box centered at @p center and with normalized side vectors @p u, @p v and @p w. These vectors should
|
||||
* be mutually orthonormal for a proper rectangular box. The half-widths of the box in each dimension are given by the corresponding
|
||||
* components of @p halfSizes.
|
||||
*/
|
||||
CBoundingBoxOriented(const CVector3D& center, const CVector3D& u, const CVector3D& v, const CVector3D& w, const CVector3D& halfSizes)
|
||||
: m_Center(center), m_HalfSizes(halfSizes)
|
||||
{
|
||||
m_Basis[0] = u;
|
||||
m_Basis[1] = v;
|
||||
m_Basis[2] = w;
|
||||
}
|
||||
|
||||
/// Constructs a new box from an axis-aligned bounding box (AABB).
|
||||
explicit CBoundingBoxOriented(const CBoundingBoxAligned& bound);
|
||||
|
||||
/**
|
||||
* Returns true if the ray originating in @p origin and with unit direction vector @p dir intersects this box, false otherwise.
|
||||
* Additionally, returns the distance in the positive direction from the origin of the ray to the entry and exit points in the
|
||||
* box in @p tMin and @p tMax. If the origin is inside the box, then this is counted as an intersection and one of @p tMin and
|
||||
* @p tMax may be negative.
|
||||
*
|
||||
* Should not be used if IsEmpty() is true.
|
||||
* See also Real-Time Rendering, Third Edition by T. Akenine-Möller, p. 741--742.
|
||||
*
|
||||
*
|
||||
* @param origin Origin of the ray.
|
||||
* @param dir Direction vector of the ray, defining the positive direction of the ray. Must be of unit length.
|
||||
*/
|
||||
bool RayIntersect(const CVector3D& origin, const CVector3D& dir, float& tMin, float& tMax) const;
|
||||
|
||||
/**
|
||||
* Returns the corner at coordinate (@p u, @p v, @p w). Each of @p u, @p v and @p w must be exactly 1 or -1.
|
||||
* Should not be used if IsEmpty() is true.
|
||||
*/
|
||||
void GetCorner(int u, int v, int w, CVector3D& out) const
|
||||
{
|
||||
out = m_Center + m_Basis[0]*(u*m_HalfSizes[0]) + m_Basis[1]*(v*m_HalfSizes[1]) + m_Basis[2]*(w*m_HalfSizes[2]);
|
||||
}
|
||||
|
||||
void SetEmpty()
|
||||
{
|
||||
// everything is zero
|
||||
m_Center = CVector3D();
|
||||
m_Basis[0] = CVector3D();
|
||||
m_Basis[1] = CVector3D();
|
||||
m_Basis[2] = CVector3D();
|
||||
m_HalfSizes = CVector3D();
|
||||
}
|
||||
|
||||
bool IsEmpty() const
|
||||
{
|
||||
CVector3D empty;
|
||||
return (m_Center == empty &&
|
||||
m_Basis[0] == empty &&
|
||||
m_Basis[1] == empty &&
|
||||
m_Basis[2] == empty &&
|
||||
m_HalfSizes == empty);
|
||||
}
|
||||
|
||||
public:
|
||||
CVector3D m_Center; ///< Centroid location of the box
|
||||
CVector3D m_HalfSizes; ///< Half the sizes of the box in each dimension (u,v,w). Positive values are expected.
|
||||
/// Basis vectors (u,v,w) of the sides. Must always be normalized, and should be
|
||||
/// orthogonal for a proper rectangular cuboid.
|
||||
CVector3D m_Basis[3];
|
||||
|
||||
static const CBoundingBoxOriented EMPTY;
|
||||
};
|
||||
|
||||
#endif // INCLUDED_BOX
|
@ -26,13 +26,13 @@
|
||||
#include <float.h>
|
||||
|
||||
#include "Brush.h"
|
||||
#include "Bound.h"
|
||||
#include "BoundingBoxAligned.h"
|
||||
#include "graphics/Frustum.h"
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Convert the given bounds into a brush
|
||||
CBrush::CBrush(const CBound& bounds)
|
||||
CBrush::CBrush(const CBoundingBoxAligned& bounds)
|
||||
{
|
||||
m_Vertices.resize(8);
|
||||
|
||||
@ -58,7 +58,7 @@ CBrush::CBrush(const CBound& bounds)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Calculate bounds of this brush
|
||||
void CBrush::Bounds(CBound& result) const
|
||||
void CBrush::Bounds(CBoundingBoxAligned& result) const
|
||||
{
|
||||
result.SetEmpty();
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
#include "Vector3D.h"
|
||||
|
||||
class CBound;
|
||||
class CBoundingBoxAligned;
|
||||
class CFrustum;
|
||||
class CPlane;
|
||||
|
||||
@ -40,9 +40,9 @@ public:
|
||||
/**
|
||||
* CBrush: Construct a brush from a bounds object.
|
||||
*
|
||||
* @param bounds the CBound object to construct the brush from.
|
||||
* @param bounds the CBoundingBoxAligned object to construct the brush from.
|
||||
*/
|
||||
CBrush(const CBound& bounds);
|
||||
CBrush(const CBoundingBoxAligned& bounds);
|
||||
|
||||
/**
|
||||
* IsEmpty: Returns whether the brush is empty.
|
||||
@ -56,7 +56,7 @@ public:
|
||||
*
|
||||
* @param result the resulting bounding box is stored here
|
||||
*/
|
||||
void Bounds(CBound& result) const;
|
||||
void Bounds(CBoundingBoxAligned& result) const;
|
||||
|
||||
/**
|
||||
* Slice: Cut the object along the given plane, resulting in a smaller (or even empty)
|
||||
|
@ -35,6 +35,7 @@ class CMatrix3D
|
||||
public:
|
||||
// the matrix data itself - accessible as either longhand names
|
||||
// or via a flat or 2d array
|
||||
// NOTE: _xy means row x, column y, so don't be fooled by the way they're listed below
|
||||
union {
|
||||
struct {
|
||||
float _11, _21, _31, _41;
|
||||
@ -155,6 +156,12 @@ public:
|
||||
_14 == m._14 && _24 == m._24 && _34 == m._34 && _44 == m._44;
|
||||
}
|
||||
|
||||
// inequality
|
||||
bool operator!=(const CMatrix3D& m) const
|
||||
{
|
||||
return !(*this == m);
|
||||
}
|
||||
|
||||
// set this matrix to the identity matrix
|
||||
void SetIdentity();
|
||||
// set this matrix to the zero matrix
|
||||
|
@ -17,14 +17,14 @@
|
||||
|
||||
#include "lib/self_test.h"
|
||||
|
||||
#include "maths/Bound.h"
|
||||
#include "maths/BoundingBoxAligned.h"
|
||||
|
||||
class TestBound : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_empty()
|
||||
{
|
||||
CBound bound;
|
||||
CBoundingBoxAligned bound;
|
||||
TS_ASSERT(bound.IsEmpty());
|
||||
bound += CVector3D(1, 2, 3);
|
||||
TS_ASSERT(! bound.IsEmpty());
|
||||
@ -34,7 +34,7 @@ public:
|
||||
|
||||
void test_extend_vector()
|
||||
{
|
||||
CBound bound;
|
||||
CBoundingBoxAligned bound;
|
||||
CVector3D v (1, 2, 3);
|
||||
bound += v;
|
||||
|
||||
@ -45,9 +45,9 @@ public:
|
||||
|
||||
void test_extend_bound()
|
||||
{
|
||||
CBound bound;
|
||||
CBoundingBoxAligned bound;
|
||||
CVector3D v (1, 2, 3);
|
||||
CBound b (v, v);
|
||||
CBoundingBoxAligned b (v, v);
|
||||
bound += b;
|
||||
|
||||
CVector3D centre;
|
||||
|
@ -158,7 +158,7 @@ void ParticleRenderer::RenderBounds()
|
||||
{
|
||||
CParticleEmitter* emitter = m->emitters[i];
|
||||
|
||||
CBound bounds = emitter->m_Type->CalculateBounds(emitter->GetPosition(), emitter->GetParticleBounds());
|
||||
CBoundingBoxAligned bounds = emitter->m_Type->CalculateBounds(emitter->GetPosition(), emitter->GetParticleBounds());
|
||||
bounds.Render();
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ public:
|
||||
|
||||
CPatch* GetPatch() { return m_Patch; }
|
||||
|
||||
const CBound& GetWaterBounds() const { return m_WaterBounds; }
|
||||
const CBoundingBoxAligned& GetWaterBounds() const { return m_WaterBounds; }
|
||||
|
||||
private:
|
||||
friend struct SBlendStackItem;
|
||||
@ -145,7 +145,7 @@ private:
|
||||
std::vector<SSplat> m_BlendSplats;
|
||||
|
||||
// boundary of water in this patch
|
||||
CBound m_WaterBounds;
|
||||
CBoundingBoxAligned m_WaterBounds;
|
||||
|
||||
// Water vertex buffer
|
||||
CVertexBuffer::VBChunk* m_VBWater;
|
||||
|
@ -1041,7 +1041,7 @@ public:
|
||||
|
||||
bool Filter(CModel *model)
|
||||
{
|
||||
return m_Frustum.IsBoxVisible(CVector3D(0, 0, 0), model->GetBoundsRec());
|
||||
return m_Frustum.IsBoxVisible(CVector3D(0, 0, 0), model->GetWorldBoundsRec());
|
||||
}
|
||||
|
||||
private:
|
||||
@ -1198,7 +1198,7 @@ void CRenderer::SetObliqueFrustumClipping(const CVector4D& worldPlane)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// RenderReflections: render the water reflections to the reflection texture
|
||||
SScreenRect CRenderer::RenderReflections(const CBound& scissor)
|
||||
SScreenRect CRenderer::RenderReflections(const CBoundingBoxAligned& scissor)
|
||||
{
|
||||
PROFILE3_GPU("water reflections");
|
||||
|
||||
@ -1285,7 +1285,7 @@ SScreenRect CRenderer::RenderReflections(const CBound& scissor)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// RenderRefractions: render the water refractions to the refraction texture
|
||||
SScreenRect CRenderer::RenderRefractions(const CBound &scissor)
|
||||
SScreenRect CRenderer::RenderRefractions(const CBoundingBoxAligned &scissor)
|
||||
{
|
||||
PROFILE3_GPU("water refractions");
|
||||
|
||||
@ -1540,7 +1540,7 @@ void CRenderer::RenderSubmissions()
|
||||
|
||||
ogl_WarnIfError();
|
||||
|
||||
CBound waterScissor;
|
||||
CBoundingBoxAligned waterScissor;
|
||||
if (m_WaterManager->m_RenderWater)
|
||||
{
|
||||
waterScissor = m->terrainRenderer->ScissorWater(m_ViewCamera.GetViewProjection());
|
||||
@ -1756,10 +1756,10 @@ void CRenderer::SubmitNonRecursive(CModel* model)
|
||||
{
|
||||
if (model->GetFlags() & MODELFLAG_CASTSHADOWS) {
|
||||
// PROFILE( "updating shadow bounds" );
|
||||
m->shadow->AddShadowedBound(model->GetBounds());
|
||||
m->shadow->AddShadowedBound(model->GetWorldBounds());
|
||||
}
|
||||
|
||||
// Tricky: The call to GetBounds() above can invalidate the position
|
||||
// Tricky: The call to GetWorldBounds() above can invalidate the position
|
||||
model->ValidatePosition();
|
||||
|
||||
bool canUseInstancing = false;
|
||||
|
@ -348,8 +348,8 @@ protected:
|
||||
void RenderShadowMap();
|
||||
|
||||
// render water reflection and refraction textures
|
||||
SScreenRect RenderReflections(const CBound& scissor);
|
||||
SScreenRect RenderRefractions(const CBound& scissor);
|
||||
SScreenRect RenderReflections(const CBoundingBoxAligned& scissor);
|
||||
SScreenRect RenderRefractions(const CBoundingBoxAligned& scissor);
|
||||
|
||||
// debugging
|
||||
void DisplayFrustum();
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
#include "graphics/LightEnv.h"
|
||||
|
||||
#include "maths/Bound.h"
|
||||
#include "maths/BoundingBoxAligned.h"
|
||||
#include "maths/MathUtil.h"
|
||||
#include "maths/Matrix3D.h"
|
||||
|
||||
@ -67,7 +67,7 @@ struct ShadowMapInternals
|
||||
// transform light space into world space
|
||||
CMatrix3D InvLightTransform;
|
||||
// bounding box of shadowed objects in light space
|
||||
CBound ShadowBound;
|
||||
CBoundingBoxAligned ShadowBound;
|
||||
|
||||
// Camera transformed into light space
|
||||
CCamera LightspaceCamera;
|
||||
@ -203,9 +203,9 @@ void ShadowMap::SetupFrame(const CCamera& camera, const CVector3D& lightdir)
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// AddShadowedBound: add a world-space bounding box to the bounds of shadowed
|
||||
// objects
|
||||
void ShadowMap::AddShadowedBound(const CBound& bounds)
|
||||
void ShadowMap::AddShadowedBound(const CBoundingBoxAligned& bounds)
|
||||
{
|
||||
CBound lightspacebounds;
|
||||
CBoundingBoxAligned lightspacebounds;
|
||||
|
||||
bounds.Transform(m->LightTransform, lightspacebounds);
|
||||
m->ShadowBound += lightspacebounds;
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
#include "lib/ogl.h"
|
||||
|
||||
class CBound;
|
||||
class CBoundingBoxAligned;
|
||||
class CMatrix3D;
|
||||
|
||||
struct ShadowMapInternals;
|
||||
@ -80,7 +80,7 @@ public:
|
||||
*
|
||||
* @param bounds world space bounding box
|
||||
*/
|
||||
void AddShadowedBound(const CBound& bounds);
|
||||
void AddShadowedBound(const CBoundingBoxAligned& bounds);
|
||||
|
||||
/**
|
||||
* BeginRender: Set OpenGL state for rendering into the shadow map texture.
|
||||
|
@ -173,14 +173,14 @@ bool TerrainRenderer::CullPatches(const CFrustum* frustum)
|
||||
m->filteredPatches.clear();
|
||||
for (std::vector<CPatchRData*>::iterator it = m->visiblePatches.begin(); it != m->visiblePatches.end(); it++)
|
||||
{
|
||||
if (frustum->IsBoxVisible(CVector3D(0, 0, 0), (*it)->GetPatch()->GetBounds()))
|
||||
if (frustum->IsBoxVisible(CVector3D(0, 0, 0), (*it)->GetPatch()->GetWorldBounds()))
|
||||
m->filteredPatches.push_back(*it);
|
||||
}
|
||||
|
||||
m->filteredDecals.clear();
|
||||
for (std::vector<CDecalRData*>::iterator it = m->visibleDecals.begin(); it != m->visibleDecals.end(); it++)
|
||||
{
|
||||
if (frustum->IsBoxVisible(CVector3D(0, 0, 0), (*it)->GetDecal()->GetBounds()))
|
||||
if (frustum->IsBoxVisible(CVector3D(0, 0, 0), (*it)->GetDecal()->GetWorldBounds()))
|
||||
m->filteredDecals.push_back(*it);
|
||||
}
|
||||
|
||||
@ -557,13 +557,13 @@ void TerrainRenderer::RenderOutlines(bool filtered)
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// Scissor rectangle of water patches
|
||||
CBound TerrainRenderer::ScissorWater(const CMatrix3D &viewproj)
|
||||
CBoundingBoxAligned TerrainRenderer::ScissorWater(const CMatrix3D &viewproj)
|
||||
{
|
||||
CBound scissor;
|
||||
CBoundingBoxAligned scissor;
|
||||
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
|
||||
{
|
||||
CPatchRData* data = m->visiblePatches[i];
|
||||
const CBound& waterBounds = data->GetWaterBounds();
|
||||
const CBoundingBoxAligned& waterBounds = data->GetWaterBounds();
|
||||
if (waterBounds.IsEmpty())
|
||||
continue;
|
||||
|
||||
@ -571,7 +571,7 @@ CBound TerrainRenderer::ScissorWater(const CMatrix3D &viewproj)
|
||||
CVector4D v2 = viewproj.Transform(CVector4D(waterBounds[1].X, waterBounds[1].Y, waterBounds[0].Z, 1.0f));
|
||||
CVector4D v3 = viewproj.Transform(CVector4D(waterBounds[0].X, waterBounds[1].Y, waterBounds[1].Z, 1.0f));
|
||||
CVector4D v4 = viewproj.Transform(CVector4D(waterBounds[1].X, waterBounds[1].Y, waterBounds[1].Z, 1.0f));
|
||||
CBound screenBounds;
|
||||
CBoundingBoxAligned screenBounds;
|
||||
#define ADDBOUND(v1, v2, v3, v4) \
|
||||
if (v1[2] >= -v1[3]) \
|
||||
screenBounds += CVector3D(v1[0], v1[1], v1[2]) * (1.0f / v1[3]); \
|
||||
@ -603,7 +603,7 @@ CBound TerrainRenderer::ScissorWater(const CMatrix3D &viewproj)
|
||||
continue;
|
||||
scissor += screenBounds;
|
||||
}
|
||||
return CBound(CVector3D(clamp(scissor[0].X, -1.0f, 1.0f), clamp(scissor[0].Y, -1.0f, 1.0f), -1.0f),
|
||||
return CBoundingBoxAligned(CVector3D(clamp(scissor[0].X, -1.0f, 1.0f), clamp(scissor[0].Y, -1.0f, 1.0f), -1.0f),
|
||||
CVector3D(clamp(scissor[1].X, -1.0f, 1.0f), clamp(scissor[1].Y, -1.0f, 1.0f), 1.0f));
|
||||
}
|
||||
|
||||
|
@ -130,7 +130,7 @@ public:
|
||||
/**
|
||||
* Calculate a scissor rectangle for the visible water patches.
|
||||
*/
|
||||
CBound ScissorWater(const CMatrix3D& viewproj);
|
||||
CBoundingBoxAligned ScissorWater(const CMatrix3D& viewproj);
|
||||
|
||||
/**
|
||||
* Render priority text for all submitted patches, for debugging.
|
||||
|
@ -71,6 +71,10 @@ COMPONENT(CommandQueue)
|
||||
INTERFACE(Decay)
|
||||
COMPONENT(Decay)
|
||||
|
||||
// Note: The VisualActor component relies on this component being initialized before itself, in order to support using
|
||||
// an entity's footprint shape for the selection boxes. This dependency is not strictly necessary, but it does avoid
|
||||
// some extra plumbing code to set up on-demand initialization. If you find yourself forced to break this dependency,
|
||||
// see VisualActor's Init method for a description of how you can avoid it.
|
||||
INTERFACE(Footprint)
|
||||
COMPONENT(Footprint)
|
||||
|
||||
@ -142,6 +146,8 @@ COMPONENT(UnitMotionScripted)
|
||||
INTERFACE(Vision)
|
||||
COMPONENT(Vision)
|
||||
|
||||
// Note: this component relies on the Footprint component being initialized before itself. See the comments above for
|
||||
// the Footprint component to find out why.
|
||||
INTERFACE(Visual)
|
||||
COMPONENT(VisualActor) // must be after Ownership (dependency in Deserialize)
|
||||
|
||||
|
@ -140,7 +140,7 @@ public:
|
||||
CmpPtr<ICmpVisual> cmpVisual(GetSimContext(), GetEntityId());
|
||||
if (!cmpVisual.null())
|
||||
{
|
||||
CBound bound = cmpVisual->GetBounds();
|
||||
CBoundingBoxAligned bound = cmpVisual->GetBounds();
|
||||
m_TotalSinkDepth = std::max(m_TotalSinkDepth, bound[1].Y - bound[0].Y);
|
||||
}
|
||||
|
||||
|
@ -347,7 +347,7 @@ void CCmpProjectileManager::RenderSubmit(SceneCollector& collector, const CFrust
|
||||
|
||||
model.ValidatePosition();
|
||||
|
||||
if (culling && !frustum.IsBoxVisible(CVector3D(0, 0, 0), model.GetBounds()))
|
||||
if (culling && !frustum.IsBoxVisible(CVector3D(0, 0, 0), model.GetWorldBounds()))
|
||||
continue;
|
||||
|
||||
// TODO: do something about LOS (copy from CCmpVisualActor)
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include "ICmpPosition.h"
|
||||
#include "ICmpFootprint.h"
|
||||
#include "ICmpVisual.h"
|
||||
#include "simulation2/MessageTypes.h"
|
||||
#include "simulation2/helpers/Render.h"
|
||||
|
||||
@ -45,13 +46,21 @@ public:
|
||||
DEFAULT_COMPONENT_ALLOCATOR(Selectable)
|
||||
|
||||
SOverlayLine m_Overlay;
|
||||
SOverlayLine* m_DebugBoundingBoxOverlay;
|
||||
SOverlayLine* m_DebugSelectionBoxOverlay;
|
||||
|
||||
CCmpSelectable()
|
||||
: m_DebugBoundingBoxOverlay(NULL), m_DebugSelectionBoxOverlay(NULL)
|
||||
{
|
||||
m_Overlay.m_Thickness = 2;
|
||||
m_Overlay.m_Color = CColor(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
~CCmpSelectable(){
|
||||
delete m_DebugBoundingBoxOverlay;
|
||||
delete m_DebugSelectionBoxOverlay;
|
||||
}
|
||||
|
||||
static std::string GetSchema()
|
||||
{
|
||||
return
|
||||
@ -153,8 +162,35 @@ public:
|
||||
void RenderSubmit(SceneCollector& collector)
|
||||
{
|
||||
// (This is only called if a > 0)
|
||||
|
||||
collector.Submit(&m_Overlay);
|
||||
|
||||
if (ICmpSelectable::ms_EnableDebugOverlays)
|
||||
{
|
||||
// allocate debug overlays on-demand
|
||||
if (!m_DebugBoundingBoxOverlay) m_DebugBoundingBoxOverlay = new SOverlayLine;
|
||||
if (!m_DebugSelectionBoxOverlay) m_DebugSelectionBoxOverlay = new SOverlayLine;
|
||||
|
||||
CmpPtr<ICmpVisual> cmpVisual(GetSimContext(), GetEntityId());
|
||||
if (!cmpVisual.null())
|
||||
{
|
||||
SimRender::ConstructBoxOutline(cmpVisual->GetBounds(), *m_DebugBoundingBoxOverlay);
|
||||
m_DebugBoundingBoxOverlay->m_Thickness = 2;
|
||||
m_DebugBoundingBoxOverlay->m_Color = CColor(1.f, 0.f, 0.f, 1.f);
|
||||
|
||||
SimRender::ConstructBoxOutline(cmpVisual->GetSelectionBox(), *m_DebugSelectionBoxOverlay);
|
||||
m_DebugSelectionBoxOverlay->m_Thickness = 2;
|
||||
m_DebugSelectionBoxOverlay->m_Color = CColor(0.f, 1.f, 0.f, 1.f);
|
||||
|
||||
collector.Submit(m_DebugBoundingBoxOverlay);
|
||||
collector.Submit(m_DebugSelectionBoxOverlay);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// reclaim debug overlay line memory when no longer debugging (and make sure to set to zero after deletion)
|
||||
if (m_DebugBoundingBoxOverlay) SAFE_DELETE(m_DebugBoundingBoxOverlay);
|
||||
if (m_DebugSelectionBoxOverlay) SAFE_DELETE(m_DebugSelectionBoxOverlay);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "ICmpRangeManager.h"
|
||||
#include "ICmpVision.h"
|
||||
#include "simulation2/MessageTypes.h"
|
||||
#include "simulation2/components/ICmpFootprint.h"
|
||||
|
||||
#include "graphics/Frustum.h"
|
||||
#include "graphics/Model.h"
|
||||
@ -97,7 +98,38 @@ public:
|
||||
"</element>"
|
||||
"<element name='SilhouetteOccluder'>"
|
||||
"<data type='boolean'/>"
|
||||
"</element>";
|
||||
"</element>"
|
||||
"<optional>"
|
||||
"<element name='SelectionShape'>"
|
||||
"<choice>"
|
||||
"<element name='Bounds' a:help='Determines the selection box based on the model bounds'>"
|
||||
"<empty/>"
|
||||
"</element>"
|
||||
"<element name='Footprint' a:help='Determines the selection box based on the entity Footprint component'>"
|
||||
"<empty/>"
|
||||
"</element>"
|
||||
"<element name='Box' a:help='Sets the selection shape to a box of specified dimensions'>"
|
||||
"<attribute name='width'>"
|
||||
"<ref name='positiveDecimal' />"
|
||||
"</attribute>"
|
||||
"<attribute name='height'>"
|
||||
"<ref name='positiveDecimal' />"
|
||||
"</attribute>"
|
||||
"<attribute name='depth'>"
|
||||
"<ref name='positiveDecimal' />"
|
||||
"</attribute>"
|
||||
"</element>"
|
||||
"<element name='Cylinder' a:help='Sets the selection shape to a cylinder of specified dimensions'>"
|
||||
"<attribute name='radius'>"
|
||||
"<ref name='positiveDecimal' />"
|
||||
"</attribute>"
|
||||
"<attribute name='height'>"
|
||||
"<ref name='positiveDecimal' />"
|
||||
"</attribute>"
|
||||
"</element>"
|
||||
"</choice>"
|
||||
"</element>"
|
||||
"</optional>";
|
||||
}
|
||||
|
||||
virtual void Init(const CParamNode& paramNode)
|
||||
@ -126,8 +158,16 @@ public:
|
||||
if (paramNode.GetChild("SilhouetteOccluder").ToBool())
|
||||
modelFlags |= MODELFLAG_SILHOUETTE_OCCLUDER;
|
||||
|
||||
if (m_Unit->GetModel().ToCModel())
|
||||
m_Unit->GetModel().ToCModel()->AddFlagsRec(modelFlags);
|
||||
CModelAbstract& model = m_Unit->GetModel();
|
||||
if (model.ToCModel())
|
||||
model.ToCModel()->AddFlagsRec(modelFlags);
|
||||
|
||||
// Initialize the model's selection shape descriptor. This currently relies on the component initialization order; the
|
||||
// Footprint component must be initialized before this component (VisualActor) to support the ability to use the footprint
|
||||
// shape for the selection box (instead of the default recursive bounding box). See TypeList.h for the order in
|
||||
// which components are initialized; if for whatever reason you need to get rid of this dependency, you can always just
|
||||
// initialize the selection shape descriptor on-demand.
|
||||
InitSelectionShapeDescriptor(model, paramNode);
|
||||
|
||||
m_Unit->SetID(GetEntityId());
|
||||
}
|
||||
@ -245,11 +285,18 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual CBound GetBounds()
|
||||
virtual CBoundingBoxAligned GetBounds()
|
||||
{
|
||||
if (!m_Unit)
|
||||
return CBound();
|
||||
return m_Unit->GetModel().GetBounds();
|
||||
return CBoundingBoxAligned::EMPTY;
|
||||
return m_Unit->GetModel().GetWorldBounds();
|
||||
}
|
||||
|
||||
virtual CBoundingBoxOriented GetSelectionBox()
|
||||
{
|
||||
if (!m_Unit)
|
||||
return CBoundingBoxOriented::EMPTY;
|
||||
return m_Unit->GetModel().GetSelectionBox();
|
||||
}
|
||||
|
||||
virtual CVector3D GetPosition()
|
||||
@ -403,6 +450,13 @@ private:
|
||||
return GetEntityId();
|
||||
}
|
||||
|
||||
/// Helper method; initializes the model selection shape descriptor from XML. Factored out for readability of @ref Init.
|
||||
/// The @p model argument is technically not really necessary since naturally this method is intended to initialize this
|
||||
/// visual actor's model (I wouldn't know which other one you'd pass), but it's included here to enforce that the
|
||||
/// component's model must have been created before using this method (i.e. to prevent accidentally calls to this method
|
||||
/// before the model was constructed).
|
||||
void InitSelectionShapeDescriptor(CModelAbstract& model, const CParamNode& paramNode);
|
||||
|
||||
void Update(fixed turnLength);
|
||||
void Interpolate(float frameTime, float frameOffset);
|
||||
void RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling);
|
||||
@ -410,6 +464,78 @@ private:
|
||||
|
||||
REGISTER_COMPONENT_TYPE(VisualActor)
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
void CCmpVisualActor::InitSelectionShapeDescriptor(CModelAbstract& model, const CParamNode& paramNode)
|
||||
{
|
||||
// by default, we don't need a custom selection shape and we can just keep the default behaviour
|
||||
CModelAbstract::CustomSelectionShape* shapeDescriptor = NULL;
|
||||
|
||||
const CParamNode& shapeNode = paramNode.GetChild("SelectionShape");
|
||||
if (shapeNode.IsOk())
|
||||
{
|
||||
if (shapeNode.GetChild("Bounds").IsOk())
|
||||
{
|
||||
// default; no need to take action
|
||||
}
|
||||
else if (shapeNode.GetChild("Footprint").IsOk())
|
||||
{
|
||||
CmpPtr<ICmpFootprint> cmpFootprint(GetSimContext(), GetEntityId());
|
||||
if (!cmpFootprint.null())
|
||||
{
|
||||
ICmpFootprint::EShape fpShape; // fp stands for "footprint"
|
||||
entity_pos_t fpSize0, fpSize1, fpHeight; // fp stands for "footprint"
|
||||
cmpFootprint->GetShape(fpShape, fpSize0, fpSize1, fpHeight);
|
||||
|
||||
float size0 = fpSize0.ToFloat();
|
||||
float size1 = fpSize1.ToFloat();
|
||||
|
||||
// TODO: we should properly distinguish between CIRCLE and SQUARE footprint shapes here, but since cylinders
|
||||
// aren't implemented yet and are almost indistinguishable from boxes for small enough sizes anyway,
|
||||
// we'll just use boxes for either case. However, for circular footprints the size0 and size1 values both
|
||||
// represent the radius, so we do have to adjust them to match the size1 and size0's of square footprints
|
||||
// (which represent the full width and depth).
|
||||
if (fpShape == ICmpFootprint::CIRCLE)
|
||||
{
|
||||
size0 *= 2;
|
||||
size1 *= 2;
|
||||
}
|
||||
|
||||
shapeDescriptor = new CModelAbstract::CustomSelectionShape;
|
||||
shapeDescriptor->m_Type = CModelAbstract::CustomSelectionShape::BOX;
|
||||
shapeDescriptor->m_Size0 = size0;
|
||||
shapeDescriptor->m_Size1 = size1;
|
||||
shapeDescriptor->m_Height = fpHeight.ToFloat();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGERROR(L"[VisualActor] Cannot apply footprint-based SelectionShape; Footprint component not initialized.");
|
||||
}
|
||||
}
|
||||
else if (shapeNode.GetChild("Box").IsOk())
|
||||
{
|
||||
// TODO: we might need to support the ability to specify a different box center in the future
|
||||
shapeDescriptor = new CModelAbstract::CustomSelectionShape;
|
||||
shapeDescriptor->m_Type = CModelAbstract::CustomSelectionShape::BOX;
|
||||
shapeDescriptor->m_Size0 = shapeNode.GetChild("Box").GetChild("@width").ToFixed().ToFloat();
|
||||
shapeDescriptor->m_Size1 = shapeNode.GetChild("Box").GetChild("@depth").ToFixed().ToFloat();
|
||||
shapeDescriptor->m_Height = shapeNode.GetChild("Box").GetChild("@height").ToFixed().ToFloat();
|
||||
}
|
||||
else if (shapeNode.GetChild("Cylinder").IsOk())
|
||||
{
|
||||
LOGWARNING(L"[VisualActor] TODO: Cylinder selection shapes are not yet implemented; defaulting to recursive bounding boxes");
|
||||
}
|
||||
else
|
||||
{
|
||||
// shouldn't happen by virtue of validation against schema
|
||||
LOGERROR(L"[VisualActor] No selection shape specified");
|
||||
}
|
||||
}
|
||||
|
||||
// the model is now responsible for cleaning up the descriptor
|
||||
model.SetCustomSelectionShape(shapeDescriptor);
|
||||
}
|
||||
|
||||
void CCmpVisualActor::Update(fixed turnLength)
|
||||
{
|
||||
if (m_Unit == NULL)
|
||||
@ -504,7 +630,7 @@ void CCmpVisualActor::RenderSubmit(SceneCollector& collector, const CFrustum& fr
|
||||
|
||||
CModelAbstract& model = m_Unit->GetModel();
|
||||
|
||||
if (culling && !frustum.IsBoxVisible(CVector3D(0, 0, 0), model.GetBoundsRec()))
|
||||
if (culling && !frustum.IsBoxVisible(CVector3D(0, 0, 0), model.GetWorldBoundsRec()))
|
||||
return;
|
||||
|
||||
collector.SubmitRecursive(&model);
|
||||
|
@ -26,3 +26,5 @@
|
||||
BEGIN_INTERFACE_WRAPPER(Selectable)
|
||||
DEFINE_INTERFACE_METHOD_1("SetSelectionHighlight", void, ICmpSelectable, SetSelectionHighlight, CColor)
|
||||
END_INTERFACE_WRAPPER(Selectable)
|
||||
|
||||
bool ICmpSelectable::ms_EnableDebugOverlays = false;
|
@ -32,6 +32,11 @@ public:
|
||||
virtual void SetSelectionHighlight(CColor color) = 0;
|
||||
|
||||
DECLARE_INTERFACE_TYPE(Selectable)
|
||||
|
||||
// TODO: this is slightly ugly design; it would be nice to change the component system to support per-component-type data
|
||||
// and methods, where we can keep settings like these. Note that any such data store would need to be per-component-manager
|
||||
// and not entirely global, to support multiple simulation instances.
|
||||
static bool ms_EnableDebugOverlays; // ms for member static
|
||||
};
|
||||
|
||||
#endif // INCLUDED_ICMPSELECTABLE
|
||||
|
@ -20,7 +20,8 @@
|
||||
|
||||
#include "simulation2/system/Interface.h"
|
||||
|
||||
#include "maths/Bound.h"
|
||||
#include "maths/BoundingBoxOriented.h"
|
||||
#include "maths/BoundingBoxAligned.h"
|
||||
#include "maths/Fixed.h"
|
||||
#include "lib/file/vfs/vfs_path.h"
|
||||
|
||||
@ -34,7 +35,14 @@ public:
|
||||
* Get the world-space bounding box of the object's visual representation.
|
||||
* (Not safe for use in simulation code.)
|
||||
*/
|
||||
virtual CBound GetBounds() = 0;
|
||||
virtual CBoundingBoxAligned GetBounds() = 0;
|
||||
|
||||
/**
|
||||
* Get the oriented world-space bounding box of the object's visual representation, clipped at the Y=0 plane in object space
|
||||
* to prevent it from extending into the terrain. The primary difference with GetBounds is that this bounding box is not aligned
|
||||
* to the world axes, but arbitrarily rotated according to the model transform.
|
||||
*/
|
||||
virtual CBoundingBoxOriented GetSelectionBox() = 0;
|
||||
|
||||
/**
|
||||
* Get the world-space position of the base point of the object's visual representation.
|
||||
|
@ -24,9 +24,12 @@
|
||||
#include "simulation2/components/ICmpWaterManager.h"
|
||||
#include "graphics/Overlay.h"
|
||||
#include "graphics/Terrain.h"
|
||||
#include "maths/BoundingBoxAligned.h"
|
||||
#include "maths/BoundingBoxOriented.h"
|
||||
#include "maths/MathUtil.h"
|
||||
#include "maths/Vector2D.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "maths/Quaternion.h"
|
||||
|
||||
void SimRender::ConstructLineOnGround(const CSimContext& context, const std::vector<float>& xz,
|
||||
SOverlayLine& overlay, bool floating, float heightOffset)
|
||||
@ -159,6 +162,121 @@ void SimRender::ConstructSquareOnGround(const CSimContext& context, float x, flo
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void SimRender::ConstructBoxOutline(const CBoundingBoxOriented& box, SOverlayLine& overlayLine)
|
||||
{
|
||||
overlayLine.m_Coords.clear();
|
||||
|
||||
if (box.IsEmpty())
|
||||
return;
|
||||
|
||||
CVector3D corners[8];
|
||||
box.GetCorner(-1, -1, -1, corners[0]);
|
||||
box.GetCorner( 1, -1, -1, corners[1]);
|
||||
box.GetCorner( 1, -1, 1, corners[2]);
|
||||
box.GetCorner(-1, -1, 1, corners[3]);
|
||||
box.GetCorner(-1, 1, -1, corners[4]);
|
||||
box.GetCorner( 1, 1, -1, corners[5]);
|
||||
box.GetCorner( 1, 1, 1, corners[6]);
|
||||
box.GetCorner(-1, 1, 1, corners[7]);
|
||||
|
||||
overlayLine.PushCoords(corners[0]);
|
||||
overlayLine.PushCoords(corners[1]);
|
||||
overlayLine.PushCoords(corners[2]);
|
||||
overlayLine.PushCoords(corners[3]);
|
||||
overlayLine.PushCoords(corners[0]);
|
||||
|
||||
overlayLine.PushCoords(corners[4]);
|
||||
overlayLine.PushCoords(corners[5]);
|
||||
overlayLine.PushCoords(corners[6]);
|
||||
overlayLine.PushCoords(corners[7]);
|
||||
overlayLine.PushCoords(corners[4]);
|
||||
}
|
||||
|
||||
void SimRender::ConstructGimbal(const CVector3D& center, float radius, SOverlayLine& out, size_t numSteps)
|
||||
{
|
||||
ENSURE(numSteps > 0 && numSteps % 4 == 0); // must be a positive multiple of 4
|
||||
|
||||
out.m_Coords.clear();
|
||||
|
||||
size_t fullCircleSteps = numSteps;
|
||||
const float angleIncrement = 2.f*M_PI/fullCircleSteps;
|
||||
|
||||
const CVector3D X_UNIT(1, 0, 0);
|
||||
const CVector3D Y_UNIT(0, 1, 0);
|
||||
const CVector3D Z_UNIT(0, 0, 1);
|
||||
CVector3D rotationVector(0, 0, radius); // directional vector based in the center that we will be rotating to get the gimbal points
|
||||
|
||||
// first draw a quarter of XZ gimbal; then complete the XY gimbal; then continue the XZ gimbal and finally add the YZ gimbal
|
||||
// (that way we can keep a single continuous line)
|
||||
|
||||
// -- XZ GIMBAL (PART 1/2) -----------------------------------------------
|
||||
|
||||
CQuaternion xzRotation;
|
||||
xzRotation.FromAxisAngle(Y_UNIT, angleIncrement);
|
||||
|
||||
for (size_t i = 0; i < fullCircleSteps/4; ++i) // complete only a quarter of the way
|
||||
{
|
||||
out.PushCoords(center + rotationVector);
|
||||
rotationVector = xzRotation.Rotate(rotationVector);
|
||||
}
|
||||
|
||||
// -- XY GIMBAL ----------------------------------------------------------
|
||||
|
||||
// now complete the XY gimbal while the XZ gimbal is interrupted
|
||||
CQuaternion xyRotation;
|
||||
xyRotation.FromAxisAngle(Z_UNIT, angleIncrement);
|
||||
|
||||
for (size_t i = 0; i < fullCircleSteps; ++i) // note the <; the last point of the XY gimbal isn't added, because the XZ gimbal will add it
|
||||
{
|
||||
out.PushCoords(center + rotationVector);
|
||||
rotationVector = xyRotation.Rotate(rotationVector);
|
||||
}
|
||||
|
||||
// -- XZ GIMBAL (PART 2/2) -----------------------------------------------
|
||||
|
||||
// resume the XZ gimbal to completion
|
||||
for (size_t i = fullCircleSteps/4; i < fullCircleSteps; ++i) // exclude the last point of the circle so the YZ gimbal can add it
|
||||
{
|
||||
out.PushCoords(center + rotationVector);
|
||||
rotationVector = xzRotation.Rotate(rotationVector);
|
||||
}
|
||||
|
||||
// -- YZ GIMBAL ----------------------------------------------------------
|
||||
|
||||
CQuaternion yzRotation;
|
||||
yzRotation.FromAxisAngle(X_UNIT, angleIncrement);
|
||||
|
||||
for (size_t i = 0; i <= fullCircleSteps; ++i)
|
||||
{
|
||||
out.PushCoords(center + rotationVector);
|
||||
rotationVector = yzRotation.Rotate(rotationVector);
|
||||
}
|
||||
}
|
||||
|
||||
void SimRender::SmoothPointsAverage(std::vector<CVector2D>& points, bool closed)
|
||||
{
|
||||
PROFILE("SmoothPointsAverage");
|
||||
|
@ -25,6 +25,9 @@
|
||||
|
||||
class CSimContext;
|
||||
class CVector2D;
|
||||
class CVector3D;
|
||||
class CBoundingBoxAligned;
|
||||
class CBoundingBoxOriented;
|
||||
struct SOverlayLine;
|
||||
|
||||
namespace SimRender
|
||||
@ -53,6 +56,23 @@ void ConstructSquareOnGround(const CSimContext& context, float x, float z, float
|
||||
SOverlayLine& overlay,
|
||||
bool floating, float heightOffset = 0.25f);
|
||||
|
||||
/**
|
||||
* Constructs a solid outline of an arbitrarily-aligned box.
|
||||
*/
|
||||
void ConstructBoxOutline(const CBoundingBoxOriented& box, SOverlayLine& overlayLine);
|
||||
|
||||
/**
|
||||
* Constructs a solid outline of an axis-aligned bounding box.
|
||||
*/
|
||||
void ConstructBoxOutline(const CBoundingBoxAligned& bound, SOverlayLine& overlayLine);
|
||||
|
||||
/**
|
||||
* Constructs a simple gimbal outline of radius @p radius at @p center in @p out.
|
||||
* @param numSteps The amount of steps to trace a circle's complete outline. Must be a (strictly) positive multiple of four.
|
||||
* For small radii, you can get away with small values; setting this to 4 will create a diamond shape.
|
||||
*/
|
||||
void ConstructGimbal(const CVector3D& center, float radius, SOverlayLine& out, size_t numSteps = 16);
|
||||
|
||||
/**
|
||||
* Updates @p points so each point is averaged with its neighbours, resulting in
|
||||
* a somewhat smoother curve, assuming the points are roughly equally spaced.
|
||||
|
@ -51,19 +51,18 @@ std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simu
|
||||
if (cmpVisual.null())
|
||||
continue;
|
||||
|
||||
CBound bounds = cmpVisual->GetBounds();
|
||||
CBoundingBoxOriented selectionBox = cmpVisual->GetSelectionBox();
|
||||
if (selectionBox.IsEmpty())
|
||||
continue;
|
||||
|
||||
float tmin, tmax;
|
||||
if (!bounds.RayIntersect(origin, dir, tmin, tmax))
|
||||
if (!selectionBox.RayIntersect(origin, dir, tmin, tmax))
|
||||
continue;
|
||||
|
||||
// Find the perpendicular distance from the object's centre to the picker ray
|
||||
|
||||
CVector3D centre;
|
||||
bounds.GetCentre(centre);
|
||||
|
||||
CVector3D closest = origin + dir * (centre - origin).Dot(dir);
|
||||
float dist2 = (closest - centre).LengthSquared();
|
||||
CVector3D closest = origin + dir * (selectionBox.m_Center - origin).Dot(dir);
|
||||
float dist2 = (closest - selectionBox.m_Center).LengthSquared();
|
||||
|
||||
hits.push_back(std::make_pair(dist2, ent));
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ enum
|
||||
ID_ViewerShadows,
|
||||
ID_ViewerPolyCount,
|
||||
ID_ViewerAnimation,
|
||||
ID_ViewerBoundingBox,
|
||||
ID_ViewerAxesMarker,
|
||||
ID_ViewerPlay,
|
||||
ID_ViewerPause,
|
||||
ID_ViewerSlow
|
||||
@ -59,10 +61,15 @@ static wxWindow* Tooltipped(wxWindow* window, const wxString& tip)
|
||||
class ObjectBottomBar : public wxPanel
|
||||
{
|
||||
public:
|
||||
ObjectBottomBar(wxWindow* parent, ScenarioEditor& scenarioEditor, Observable<ObjectSettings>& objectSettings, Observable<AtObj>& mapSettings, ObjectSidebarImpl* p);
|
||||
ObjectBottomBar(
|
||||
wxWindow* parent,
|
||||
ScenarioEditor& scenarioEditor,
|
||||
Observable<ObjectSettings>& objectSettings,
|
||||
Observable<AtObj>& mapSettings,
|
||||
ObjectSidebarImpl* p
|
||||
);
|
||||
|
||||
void OnFirstDisplay();
|
||||
|
||||
void ShowActorViewer(bool show);
|
||||
|
||||
private:
|
||||
@ -75,11 +82,12 @@ private:
|
||||
bool m_ViewerGround;
|
||||
bool m_ViewerShadows;
|
||||
bool m_ViewerPolyCount;
|
||||
bool m_ViewerBoundingBox;
|
||||
bool m_ViewerAxesMarker;
|
||||
|
||||
wxPanel* m_ViewerPanel;
|
||||
|
||||
ObjectSidebarImpl* p;
|
||||
|
||||
ScenarioEditor& m_ScenarioEditor;
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
@ -108,14 +116,27 @@ struct ObjectSidebarImpl
|
||||
}
|
||||
};
|
||||
|
||||
ObjectSidebar::ObjectSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
|
||||
: Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer), p(new ObjectSidebarImpl())
|
||||
ObjectSidebar::ObjectSidebar(
|
||||
ScenarioEditor& scenarioEditor,
|
||||
wxWindow* sidebarContainer,
|
||||
wxWindow* bottomBarContainer
|
||||
)
|
||||
: Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer),
|
||||
p(new ObjectSidebarImpl())
|
||||
{
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(new wxStaticText(this, wxID_ANY, _("Filter")), wxSizerFlags().Align(wxALIGN_CENTER));
|
||||
sizer->Add(Tooltipped(new wxTextCtrl(this, ID_ObjectFilter),
|
||||
_("Enter text to filter object list")), wxSizerFlags().Expand().Proportion(1));
|
||||
sizer->Add(
|
||||
Tooltipped(
|
||||
new wxTextCtrl(this, ID_ObjectFilter),
|
||||
_("Enter text to filter object list")
|
||||
),
|
||||
wxSizerFlags().Expand().Proportion(1)
|
||||
);
|
||||
m_MainSizer->Add(sizer, wxSizerFlags().Expand());
|
||||
m_MainSizer->AddSpacer(3);
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
|
||||
wxArrayString strings;
|
||||
strings.Add(_("Entities"));
|
||||
@ -123,13 +144,27 @@ ObjectSidebar::ObjectSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarCo
|
||||
wxChoice* objectType = new wxChoice(this, ID_ObjectType, wxDefaultPosition, wxDefaultSize, strings);
|
||||
objectType->SetSelection(0);
|
||||
m_MainSizer->Add(objectType, wxSizerFlags().Expand());
|
||||
m_MainSizer->AddSpacer(3);
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
|
||||
p->m_ObjectListBox = new wxListBox(this, ID_SelectObject, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_SINGLE|wxLB_HSCROLL);
|
||||
m_MainSizer->Add(p->m_ObjectListBox, wxSizerFlags().Proportion(1).Expand());
|
||||
m_MainSizer->AddSpacer(3);
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
|
||||
m_MainSizer->Add(new wxButton(this, ID_ToggleViewer, _("Switch to Actor Viewer")), wxSizerFlags().Expand());
|
||||
|
||||
m_BottomBar = new ObjectBottomBar(bottomBarContainer, scenarioEditor, scenarioEditor.GetObjectSettings(), scenarioEditor.GetMapSettings(), p);
|
||||
// ------------------------------------------------------------------------------------------
|
||||
|
||||
m_BottomBar = new ObjectBottomBar(
|
||||
bottomBarContainer,
|
||||
scenarioEditor,
|
||||
scenarioEditor.GetObjectSettings(),
|
||||
scenarioEditor.GetMapSettings(),
|
||||
p
|
||||
);
|
||||
|
||||
p->m_ToolConn = scenarioEditor.GetToolManager().GetCurrentTool().RegisterObserver(0, &ObjectSidebar::OnToolChange, this);
|
||||
}
|
||||
@ -312,7 +347,13 @@ END_EVENT_TABLE();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ObjectBottomBar::ObjectBottomBar(wxWindow* parent, ScenarioEditor& scenarioEditor, Observable<ObjectSettings>& objectSettings, Observable<AtObj>& mapSettings, ObjectSidebarImpl* p)
|
||||
ObjectBottomBar::ObjectBottomBar(
|
||||
wxWindow* parent,
|
||||
ScenarioEditor& scenarioEditor,
|
||||
Observable<ObjectSettings>& objectSettings,
|
||||
Observable<AtObj>& mapSettings,
|
||||
ObjectSidebarImpl* p
|
||||
)
|
||||
: wxPanel(parent, wxID_ANY), p(p), m_ScenarioEditor(scenarioEditor)
|
||||
{
|
||||
m_ViewerWireframe = false;
|
||||
@ -320,21 +361,39 @@ ObjectBottomBar::ObjectBottomBar(wxWindow* parent, ScenarioEditor& scenarioEdito
|
||||
m_ViewerGround = true;
|
||||
m_ViewerShadows = true;
|
||||
m_ViewerPolyCount = false;
|
||||
m_ViewerBoundingBox = false;
|
||||
m_ViewerAxesMarker = false;
|
||||
|
||||
wxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxSizer* mainSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
// --- viewer options panel -------------------------------------------------------------------------------
|
||||
|
||||
m_ViewerPanel = new wxPanel(this, wxID_ANY);
|
||||
wxSizer* viewerSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
wxSizer* viewerButtonsSizer = new wxStaticBoxSizer(wxVERTICAL, m_ViewerPanel, _("Display settings"));
|
||||
viewerButtonsSizer->SetMinSize(140, -1);
|
||||
viewerButtonsSizer->Add(Tooltipped(new wxButton(m_ViewerPanel, ID_ViewerWireframe, _("Wireframe")), _("Toggle wireframe / solid rendering")), wxSizerFlags().Expand());
|
||||
viewerButtonsSizer->Add(Tooltipped(new wxButton(m_ViewerPanel, ID_ViewerMove, _("Move")), _("Toggle movement along ground when playing walk/run animations")), wxSizerFlags().Expand());
|
||||
viewerButtonsSizer->Add(Tooltipped(new wxButton(m_ViewerPanel, ID_ViewerGround, _("Ground")), _("Toggle the ground plane")), wxSizerFlags().Expand());
|
||||
viewerButtonsSizer->Add(Tooltipped(new wxButton(m_ViewerPanel, ID_ViewerShadows, _("Shadows")), _("Toggle shadow rendering")), wxSizerFlags().Expand());
|
||||
viewerButtonsSizer->Add(Tooltipped(new wxButton(m_ViewerPanel, ID_ViewerPolyCount, _("Poly count")), _("Toggle polygon-count statistics - turn off ground and shadows for more useful data")), wxSizerFlags().Expand());
|
||||
wxSizer* viewerButtonsSizer = new wxStaticBoxSizer(wxHORIZONTAL, m_ViewerPanel, _("Display settings"));
|
||||
{
|
||||
wxSizer* viewerButtonsLeft = new wxBoxSizer(wxVERTICAL);
|
||||
viewerButtonsLeft->SetMinSize(110, -1);
|
||||
viewerButtonsLeft->Add(Tooltipped(new wxButton(m_ViewerPanel, ID_ViewerWireframe, _("Wireframe")), _("Toggle wireframe / solid rendering")), wxSizerFlags().Expand());
|
||||
viewerButtonsLeft->Add(Tooltipped(new wxButton(m_ViewerPanel, ID_ViewerMove, _("Move")), _("Toggle movement along ground when playing walk/run animations")), wxSizerFlags().Expand());
|
||||
viewerButtonsLeft->Add(Tooltipped(new wxButton(m_ViewerPanel, ID_ViewerGround, _("Ground")), _("Toggle the ground plane")), wxSizerFlags().Expand());
|
||||
viewerButtonsLeft->Add(Tooltipped(new wxButton(m_ViewerPanel, ID_ViewerShadows, _("Shadows")), _("Toggle shadow rendering")), wxSizerFlags().Expand());
|
||||
viewerButtonsLeft->Add(Tooltipped(new wxButton(m_ViewerPanel, ID_ViewerPolyCount, _("Poly count")), _("Toggle polygon-count statistics - turn off ground and shadows for more useful data")), wxSizerFlags().Expand());
|
||||
viewerButtonsLeft->Add(Tooltipped(new wxButton(m_ViewerPanel, ID_ViewerBoundingBox, _("Bounding Boxes")), _("Toggle bounding boxes")), wxSizerFlags().Expand());
|
||||
|
||||
wxSizer* viewerButtonsRight = new wxBoxSizer(wxVERTICAL);
|
||||
viewerButtonsRight->SetMinSize(110,-1);
|
||||
viewerButtonsRight->Add(Tooltipped(new wxButton(m_ViewerPanel, ID_ViewerAxesMarker, _("Axes Marker")), _("Toggle the axes marker (R=X, G=Y, B=Z)")), wxSizerFlags().Expand());
|
||||
|
||||
viewerButtonsSizer->Add(viewerButtonsLeft, wxSizerFlags().Expand());
|
||||
viewerButtonsSizer->Add(viewerButtonsRight, wxSizerFlags().Expand());
|
||||
}
|
||||
|
||||
viewerSizer->Add(viewerButtonsSizer, wxSizerFlags().Expand());
|
||||
viewerSizer->AddSpacer(3);
|
||||
|
||||
// --- animations panel -------------------------------------------------------------------------------
|
||||
|
||||
wxSizer* viewerAnimSizer = new wxStaticBoxSizer(wxVERTICAL, m_ViewerPanel, _("Animation"));
|
||||
|
||||
@ -357,17 +416,28 @@ ObjectBottomBar::ObjectBottomBar(wxWindow* parent, ScenarioEditor& scenarioEdito
|
||||
|
||||
viewerSizer->Add(viewerAnimSizer, wxSizerFlags().Expand());
|
||||
|
||||
m_ViewerPanel->SetSizer(viewerSizer);
|
||||
sizer->Add(m_ViewerPanel, wxSizerFlags().Expand());
|
||||
// --- add viewer-specific options -------------------------------------------------------------------------------
|
||||
|
||||
m_ViewerPanel->SetSizer(viewerSizer);
|
||||
mainSizer->Add(m_ViewerPanel, wxSizerFlags().Expand());
|
||||
|
||||
m_ViewerPanel->Layout(); // prevents strange visibility glitch of the animation buttons on my machine (Vista 32-bit SP1) -- vtsj
|
||||
m_ViewerPanel->Show(false);
|
||||
|
||||
// --- add player/variation selection -------------------------------------------------------------------------------
|
||||
|
||||
wxSizer* playerSelectionSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxSizer* playerVariationSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// TODO: make this a wxChoice instead
|
||||
wxComboBox* playerSelect = new PlayerComboBox(this, objectSettings, mapSettings);
|
||||
playerVariationSizer->Add(playerSelect);
|
||||
playerSelectionSizer->Add(new wxStaticText(this, wxID_ANY, _("Player:")), wxSizerFlags().Align(wxALIGN_CENTER));
|
||||
playerSelectionSizer->AddSpacer(3);
|
||||
playerSelectionSizer->Add(playerSelect);
|
||||
|
||||
playerVariationSizer->Add(playerSelectionSizer);
|
||||
playerVariationSizer->AddSpacer(3);
|
||||
|
||||
|
||||
wxWindow* variationSelect = new VariationControl(this, objectSettings);
|
||||
variationSelect->SetMinSize(wxSize(160, -1));
|
||||
@ -375,9 +445,12 @@ ObjectBottomBar::ObjectBottomBar(wxWindow* parent, ScenarioEditor& scenarioEdito
|
||||
variationSizer->Add(variationSelect, wxSizerFlags().Proportion(1).Expand());
|
||||
playerVariationSizer->Add(variationSizer, wxSizerFlags().Proportion(1));
|
||||
|
||||
sizer->Add(playerVariationSizer, wxSizerFlags().Expand());
|
||||
mainSizer->AddSpacer(3);
|
||||
mainSizer->Add(playerVariationSizer, wxSizerFlags().Expand());
|
||||
|
||||
SetSizer(sizer);
|
||||
// ----------------------------------------------------------------------------------
|
||||
|
||||
SetSizer(mainSizer);
|
||||
}
|
||||
|
||||
void ObjectBottomBar::OnFirstDisplay()
|
||||
@ -402,6 +475,7 @@ void ObjectBottomBar::OnFirstDisplay()
|
||||
POST_MESSAGE(SetViewParamB, (AtlasMessage::eRenderView::ACTOR, L"ground", m_ViewerGround));
|
||||
POST_MESSAGE(SetViewParamB, (AtlasMessage::eRenderView::ACTOR, L"shadows", m_ViewerShadows));
|
||||
POST_MESSAGE(SetViewParamB, (AtlasMessage::eRenderView::ACTOR, L"stats", m_ViewerPolyCount));
|
||||
POST_MESSAGE(SetViewParamB, (AtlasMessage::eRenderView::ACTOR, L"bounding_box", m_ViewerBoundingBox));
|
||||
}
|
||||
|
||||
void ObjectBottomBar::ShowActorViewer(bool show)
|
||||
@ -434,6 +508,14 @@ void ObjectBottomBar::OnViewerSetting(wxCommandEvent& evt)
|
||||
m_ViewerPolyCount = !m_ViewerPolyCount;
|
||||
POST_MESSAGE(SetViewParamB, (AtlasMessage::eRenderView::ACTOR, L"stats", m_ViewerPolyCount));
|
||||
break;
|
||||
case ID_ViewerBoundingBox:
|
||||
m_ViewerBoundingBox = !m_ViewerBoundingBox;
|
||||
POST_MESSAGE(SetViewParamB, (AtlasMessage::eRenderView::ACTOR, L"bounding_box", m_ViewerBoundingBox));
|
||||
break;
|
||||
case ID_ViewerAxesMarker:
|
||||
m_ViewerAxesMarker = !m_ViewerAxesMarker;
|
||||
POST_MESSAGE(SetViewParamB, (AtlasMessage::eRenderView::ACTOR, L"axes_marker", m_ViewerAxesMarker));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -464,4 +546,6 @@ BEGIN_EVENT_TABLE(ObjectBottomBar, wxPanel)
|
||||
EVT_BUTTON(ID_ViewerPlay, ObjectBottomBar::OnSpeed)
|
||||
EVT_BUTTON(ID_ViewerPause, ObjectBottomBar::OnSpeed)
|
||||
EVT_BUTTON(ID_ViewerSlow, ObjectBottomBar::OnSpeed)
|
||||
EVT_BUTTON(ID_ViewerBoundingBox, ObjectBottomBar::OnViewerSetting)
|
||||
EVT_BUTTON(ID_ViewerAxesMarker, ObjectBottomBar::OnViewerSetting)
|
||||
END_EVENT_TABLE();
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "graphics/TerrainTextureManager.h"
|
||||
#include "graphics/TerritoryTexture.h"
|
||||
#include "graphics/UnitManager.h"
|
||||
#include "graphics/Overlay.h"
|
||||
#include "maths/MathUtil.h"
|
||||
#include "ps/Font.h"
|
||||
#include "ps/GameSetup/Config.h"
|
||||
@ -48,6 +49,7 @@
|
||||
#include "simulation2/components/ICmpTerrain.h"
|
||||
#include "simulation2/components/ICmpUnitMotion.h"
|
||||
#include "simulation2/components/ICmpVisual.h"
|
||||
#include "simulation2/helpers/Render.h"
|
||||
|
||||
struct ActorViewerImpl : public Scene
|
||||
{
|
||||
@ -75,6 +77,8 @@ public:
|
||||
bool WalkEnabled;
|
||||
bool GroundEnabled;
|
||||
bool ShadowsEnabled;
|
||||
bool SelectionBoxEnabled;
|
||||
bool AxesMarkerEnabled;
|
||||
|
||||
SColor4ub Background;
|
||||
|
||||
@ -89,6 +93,9 @@ public:
|
||||
CLOSTexture LOSTexture;
|
||||
CTerritoryTexture TerritoryTexture;
|
||||
|
||||
SOverlayLine SelectionBoxOverlay;
|
||||
SOverlayLine AxesMarkerOverlays[3];
|
||||
|
||||
// Simplistic implementation of the Scene interface
|
||||
virtual void EnumerateObjects(const CFrustum& frustum, SceneCollector* c)
|
||||
{
|
||||
@ -99,6 +106,48 @@ public:
|
||||
c->Submit(Terrain.GetPatch(pi, pj));
|
||||
}
|
||||
|
||||
CmpPtr<ICmpVisual> cmpVisual(Simulation2, Entity);
|
||||
|
||||
// add selection box outlines manually
|
||||
if (SelectionBoxEnabled && !cmpVisual.null())
|
||||
{
|
||||
SelectionBoxOverlay.m_Color = CColor(35/255.f, 86/255.f, 188/255.f, .75f); // pretty blue
|
||||
SelectionBoxOverlay.m_Thickness = 2;
|
||||
|
||||
SimRender::ConstructBoxOutline(cmpVisual->GetSelectionBox(), SelectionBoxOverlay);
|
||||
c->Submit(&SelectionBoxOverlay);
|
||||
}
|
||||
|
||||
// add origin axis thingy
|
||||
if (AxesMarkerEnabled && !cmpVisual.null())
|
||||
{
|
||||
AxesMarkerOverlays[0].m_Color = CColor(1, 0, 0, .5f); // X axis; red
|
||||
AxesMarkerOverlays[1].m_Color = CColor(0, 1, 0, .5f); // Y axis; green
|
||||
AxesMarkerOverlays[2].m_Color = CColor(0, 0, 1, .5f); // Z axis; blue
|
||||
|
||||
AxesMarkerOverlays[0].m_Thickness = 2;
|
||||
AxesMarkerOverlays[1].m_Thickness = 2;
|
||||
AxesMarkerOverlays[2].m_Thickness = 2;
|
||||
|
||||
AxesMarkerOverlays[0].m_Coords.clear();
|
||||
AxesMarkerOverlays[1].m_Coords.clear();
|
||||
AxesMarkerOverlays[2].m_Coords.clear();
|
||||
|
||||
CVector3D origin = cmpVisual->GetPosition() + CVector3D(0, 0.02f, 0); // offset from the ground a little bit to prevent fighting with the floor texture
|
||||
AxesMarkerOverlays[0].PushCoords(origin);
|
||||
AxesMarkerOverlays[1].PushCoords(origin);
|
||||
AxesMarkerOverlays[2].PushCoords(origin);
|
||||
|
||||
AxesMarkerOverlays[0].PushCoords(origin + CVector3D(1, 0, 0));
|
||||
AxesMarkerOverlays[1].PushCoords(origin + CVector3D(0, 1, 0));
|
||||
AxesMarkerOverlays[2].PushCoords(origin + CVector3D(0, 0, 1));
|
||||
|
||||
c->Submit(&AxesMarkerOverlays[0]);
|
||||
c->Submit(&AxesMarkerOverlays[1]);
|
||||
c->Submit(&AxesMarkerOverlays[2]);
|
||||
}
|
||||
|
||||
// send a RenderSubmit message so the components can submit their visuals to the renderer
|
||||
Simulation2.RenderSubmit(*c, frustum, false);
|
||||
}
|
||||
|
||||
@ -114,11 +163,13 @@ public:
|
||||
};
|
||||
|
||||
ActorViewer::ActorViewer()
|
||||
: m(*new ActorViewerImpl())
|
||||
: m(*new ActorViewerImpl())
|
||||
{
|
||||
m.WalkEnabled = false;
|
||||
m.GroundEnabled = true;
|
||||
m.ShadowsEnabled = g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWS);
|
||||
m.SelectionBoxEnabled = false;
|
||||
m.AxesMarkerEnabled = false;
|
||||
m.Background = SColor4ub(0, 0, 0, 255);
|
||||
|
||||
// Create a tiny empty piece of terrain, just so we can put shadows
|
||||
@ -307,6 +358,8 @@ void ActorViewer::SetBackgroundColour(const SColor4ub& colour)
|
||||
void ActorViewer::SetWalkEnabled(bool enabled) { m.WalkEnabled = enabled; }
|
||||
void ActorViewer::SetGroundEnabled(bool enabled) { m.GroundEnabled = enabled; }
|
||||
void ActorViewer::SetShadowsEnabled(bool enabled) { m.ShadowsEnabled = enabled; }
|
||||
void ActorViewer::SetBoundingBoxesEnabled(bool enabled) { m.SelectionBoxEnabled = enabled; }
|
||||
void ActorViewer::SetAxesMarkerEnabled(bool enabled) { m.AxesMarkerEnabled = enabled; }
|
||||
|
||||
void ActorViewer::SetStatsEnabled(bool enabled)
|
||||
{
|
||||
|
@ -41,6 +41,8 @@ public:
|
||||
void SetGroundEnabled(bool enabled);
|
||||
void SetShadowsEnabled(bool enabled);
|
||||
void SetStatsEnabled(bool enabled);
|
||||
void SetBoundingBoxesEnabled(bool enabled);
|
||||
void SetAxesMarkerEnabled(bool enabled);
|
||||
void Render();
|
||||
void Update(float dt);
|
||||
|
||||
|
@ -132,6 +132,10 @@ void ViewActor::SetParam(const std::wstring& name, bool value)
|
||||
m_ActorViewer->SetShadowsEnabled(value);
|
||||
else if (name == L"stats")
|
||||
m_ActorViewer->SetStatsEnabled(value);
|
||||
else if (name == L"bounding_box")
|
||||
m_ActorViewer->SetBoundingBoxesEnabled(value);
|
||||
else if (name == L"axes_marker")
|
||||
m_ActorViewer->SetAxesMarkerEnabled(value);
|
||||
}
|
||||
|
||||
void ViewActor::SetParam(const std::wstring& name, const AtlasMessage::Colour& value)
|
||||
|
Loading…
Reference in New Issue
Block a user