This was SVN commit r10593.
This commit is contained in:
parent
9e97c73544
commit
85186c98b2
@ -97,7 +97,7 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Dev/cheat commands -->
|
<!-- 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">
|
hidden="true" hotkey="session.devcommands.toggle">
|
||||||
<action on="Press">
|
<action on="Press">
|
||||||
toggleDeveloperOverlay();
|
toggleDeveloperOverlay();
|
||||||
@ -134,23 +134,28 @@
|
|||||||
<action on="Press">Engine.GuiInterfaceCall("SetRangeDebugOverlay", this.checked);</action>
|
<action on="Press">Engine.GuiInterfaceCall("SetRangeDebugOverlay", this.checked);</action>
|
||||||
</object>
|
</object>
|
||||||
|
|
||||||
<object size="0 96 100%-18 112" type="text" style="devCommandsText">Restrict camera</object>
|
<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" checked="true">
|
<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>
|
<action on="Press">gameView.constrainCamera = this.checked;</action>
|
||||||
</object>
|
</object>
|
||||||
|
|
||||||
<object size="0 112 100%-18 128" type="text" style="devCommandsText">Reveal map</object>
|
<object size="0 128 100%-18 144" type="text" style="devCommandsText">Reveal map</object>
|
||||||
<object name="devCommandsRevealMap" size="100%-16 112 100% 128" type="checkbox" style="StoneCrossBox">
|
<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>
|
<action on="Press">Engine.PostNetworkCommand({"type": "reveal-map", "enable": this.checked});</action>
|
||||||
</object>
|
</object>
|
||||||
|
|
||||||
<object size="0 128 100%-18 144" type="text" style="devCommandsText">Enable time warp</object>
|
<object size="0 144 100%-18 160" type="text" style="devCommandsText">Enable time warp</object>
|
||||||
<object size="100%-16 128 100% 144" type="checkbox" name="devTimeWarp" style="StoneCrossBox">
|
<object size="100%-16 144 100% 160" type="checkbox" name="devTimeWarp" style="StoneCrossBox">
|
||||||
<action on="Press">Engine.EnableTimeWarpRecording(this.checked ? 10 : 0);</action>
|
<action on="Press">Engine.EnableTimeWarpRecording(this.checked ? 10 : 0);</action>
|
||||||
</object>
|
</object>
|
||||||
|
|
||||||
<object size="0 144 100%-18 160" type="text" style="devCommandsText">Promote selected units</object>
|
<object size="0 160 100%-18 176" type="text" style="devCommandsText">Promote selected units</object>
|
||||||
<object size="100%-16 144 100% 160" type="button" style="StoneCrossBox">
|
<object size="100%-16 160 100% 176" type="button" style="StoneCrossBox">
|
||||||
<action on="Press">Engine.PostNetworkCommand({"type": "promote", "entities": g_Selection.toList()});</action>
|
<action on="Press">Engine.PostNetworkCommand({"type": "promote", "entities": g_Selection.toList()});</action>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
|
@ -16,4 +16,9 @@
|
|||||||
<Type>wood.tree</Type>
|
<Type>wood.tree</Type>
|
||||||
</ResourceSupply>
|
</ResourceSupply>
|
||||||
<Selectable/>
|
<Selectable/>
|
||||||
|
<VisualActor>
|
||||||
|
<SelectionShape>
|
||||||
|
<Footprint />
|
||||||
|
</SelectionShape>
|
||||||
|
</VisualActor>
|
||||||
</Entity>
|
</Entity>
|
||||||
|
@ -80,7 +80,7 @@ void CCamera::SetProjectionTile(int tiles, int tile_x, int tile_y)
|
|||||||
//Updates the frustum planes. Should be called
|
//Updates the frustum planes. Should be called
|
||||||
//everytime the view or projection matrices are
|
//everytime the view or projection matrices are
|
||||||
//altered.
|
//altered.
|
||||||
void CCamera::UpdateFrustum(const CBound& scissor)
|
void CCamera::UpdateFrustum(const CBoundingBoxAligned& scissor)
|
||||||
{
|
{
|
||||||
CMatrix3D MatFinal;
|
CMatrix3D MatFinal;
|
||||||
CMatrix3D MatView;
|
CMatrix3D MatView;
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
#define INCLUDED_CAMERA
|
#define INCLUDED_CAMERA
|
||||||
|
|
||||||
#include "Frustum.h"
|
#include "Frustum.h"
|
||||||
#include "maths/Bound.h"
|
#include "maths/BoundingBoxAligned.h"
|
||||||
#include "maths/Matrix3D.h"
|
#include "maths/Matrix3D.h"
|
||||||
|
|
||||||
// view port
|
// view port
|
||||||
@ -56,7 +56,7 @@ class CCamera
|
|||||||
// Updates the frustum planes. Should be called
|
// Updates the frustum planes. Should be called
|
||||||
// everytime the view or projection matrices are
|
// everytime the view or projection matrices are
|
||||||
// altered.
|
// 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);
|
void ClipFrustum(const CPlane& clipPlane);
|
||||||
const CFrustum& GetFrustum() const { return m_ViewFrustum; }
|
const CFrustum& GetFrustum() const { return m_ViewFrustum; }
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ void CModelDecal::CalcBounds()
|
|||||||
{
|
{
|
||||||
ssize_t i0, j0, i1, j1;
|
ssize_t i0, j0, i1, j1;
|
||||||
CalcVertexExtents(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)
|
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 "precompiled.h"
|
||||||
|
|
||||||
#include "Frustum.h"
|
#include "Frustum.h"
|
||||||
#include "maths/Bound.h"
|
#include "maths/BoundingBoxAligned.h"
|
||||||
#include "maths/MathUtil.h"
|
#include "maths/MathUtil.h"
|
||||||
|
|
||||||
CFrustum::CFrustum ()
|
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
|
//basically for every plane we calculate the furthest point
|
||||||
//in the box to that plane. If that point is beyond the plane
|
//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
|
//10 planes should be enough
|
||||||
#define MAX_NUM_FRUSTUM_PLANES (10)
|
#define MAX_NUM_FRUSTUM_PLANES (10)
|
||||||
|
|
||||||
class CBound;
|
class CBoundingBoxAligned;
|
||||||
|
|
||||||
class CFrustum
|
class CFrustum
|
||||||
{
|
{
|
||||||
@ -55,7 +55,7 @@ public:
|
|||||||
bool IsPointVisible (const CVector3D &point) const;
|
bool IsPointVisible (const CVector3D &point) const;
|
||||||
bool DoesSegmentIntersect(const CVector3D& start, const CVector3D &end);
|
bool DoesSegmentIntersect(const CVector3D& start, const CVector3D &end);
|
||||||
bool IsSphereVisible (const CVector3D ¢er, float radius) const;
|
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]; }
|
CPlane& operator[](size_t idx) { return m_aPlanes[idx]; }
|
||||||
const CPlane& operator[](size_t idx) const { return m_aPlanes[idx]; }
|
const CPlane& operator[](size_t idx) const { return m_aPlanes[idx]; }
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
#include "graphics/UnitManager.h"
|
#include "graphics/UnitManager.h"
|
||||||
#include "lib/input.h"
|
#include "lib/input.h"
|
||||||
#include "lib/timer.h"
|
#include "lib/timer.h"
|
||||||
#include "maths/Bound.h"
|
#include "maths/BoundingBoxAligned.h"
|
||||||
#include "maths/MathUtil.h"
|
#include "maths/MathUtil.h"
|
||||||
#include "maths/Matrix3D.h"
|
#include "maths/Matrix3D.h"
|
||||||
#include "maths/Quaternion.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
|
CPatch* patch=pTerrain->GetPatch(i,j); // can't fail
|
||||||
|
|
||||||
// If the patch is underwater, calculate a bounding box that also contains the water plane
|
// 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;
|
float waterHeight = g_Renderer.GetWaterManager()->m_WaterHeight + 0.001f;
|
||||||
if(bounds[1].Y < waterHeight) {
|
if(bounds[1].Y < waterHeight) {
|
||||||
bounds[1].Y = waterHeight;
|
bounds[1].Y = waterHeight;
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
#include "HFTracer.h"
|
#include "HFTracer.h"
|
||||||
#include "Terrain.h"
|
#include "Terrain.h"
|
||||||
#include "maths/Bound.h"
|
#include "maths/BoundingBoxAligned.h"
|
||||||
#include "maths/Vector3D.h"
|
#include "maths/Vector3D.h"
|
||||||
|
|
||||||
// To cope well with points that are slightly off the edge of the map,
|
// 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
|
// intersect first against bounding box
|
||||||
CBound bound;
|
CBoundingBoxAligned bound;
|
||||||
bound[0] = CVector3D(-MARGIN_SIZE * m_CellSize, 0, -MARGIN_SIZE * m_CellSize);
|
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);
|
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 "Model.h"
|
||||||
#include "ModelDef.h"
|
#include "ModelDef.h"
|
||||||
#include "maths/Quaternion.h"
|
#include "maths/Quaternion.h"
|
||||||
#include "maths/Bound.h"
|
#include "maths/BoundingBoxAligned.h"
|
||||||
#include "SkeletonAnim.h"
|
#include "SkeletonAnim.h"
|
||||||
#include "SkeletonAnimDef.h"
|
#include "SkeletonAnimDef.h"
|
||||||
#include "SkeletonAnimManager.h"
|
#include "SkeletonAnimManager.h"
|
||||||
@ -34,7 +34,6 @@
|
|||||||
#include "lib/res/h_mgr.h"
|
#include "lib/res/h_mgr.h"
|
||||||
#include "lib/sysdep/rtl.h"
|
#include "lib/sysdep/rtl.h"
|
||||||
#include "ps/Profile.h"
|
#include "ps/Profile.h"
|
||||||
|
|
||||||
#include "ps/CLogger.h"
|
#include "ps/CLogger.h"
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -117,12 +116,12 @@ void CModel::CalcBounds()
|
|||||||
if (! (m_Anim && m_Anim->m_AnimDef))
|
if (! (m_Anim && m_Anim->m_AnimDef))
|
||||||
{
|
{
|
||||||
if (m_ObjectBounds.IsEmpty())
|
if (m_ObjectBounds.IsEmpty())
|
||||||
CalcObjectBounds();
|
CalcStaticObjectBounds();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (m_Anim->m_ObjectBounds.IsEmpty())
|
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)
|
ENSURE(! m_Anim->m_ObjectBounds.IsEmpty()); // (if this happens, it'll be recalculating the bounds every time)
|
||||||
m_ObjectBounds = m_Anim->m_ObjectBounds;
|
m_ObjectBounds = m_Anim->m_ObjectBounds;
|
||||||
}
|
}
|
||||||
@ -130,12 +129,13 @@ void CModel::CalcBounds()
|
|||||||
// Ensure the transform is set correctly before we use it
|
// Ensure the transform is set correctly before we use it
|
||||||
ValidatePosition();
|
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
|
// CalcObjectBounds: calculate object space bounds of this model, based solely on vertex positions
|
||||||
void CModel::CalcObjectBounds()
|
void CModel::CalcStaticObjectBounds()
|
||||||
{
|
{
|
||||||
m_ObjectBounds.SetEmpty();
|
m_ObjectBounds.SetEmpty();
|
||||||
|
|
||||||
@ -149,7 +149,7 @@ void CModel::CalcObjectBounds()
|
|||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// CalcAnimatedObjectBound: calculate bounds encompassing all vertex positions for given animation
|
// 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();
|
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)
|
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;
|
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
|
// BuildAnimation: load raw animation frame animation from given file, and build a
|
||||||
// animation specific to this model
|
// 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));
|
m_Anim->m_AnimDef->BuildBoneMatrices(m_AnimTime, m_BoneMatrices, !(m_Flags & MODELFLAG_NOLOOPANIMATION));
|
||||||
|
|
||||||
|
// add world-space transformation to m_BoneMatrices
|
||||||
const CMatrix3D& transform = GetTransform();
|
const CMatrix3D& transform = GetTransform();
|
||||||
for (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)
|
for (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)
|
||||||
m_BoneMatrices[i].Concatenate(transform);
|
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;
|
m_PositionValid = true;
|
||||||
|
|
||||||
// re-position and validate all props
|
// re-position and validate all props
|
||||||
@ -322,9 +377,15 @@ void CModel::ValidatePosition()
|
|||||||
|
|
||||||
CMatrix3D proptransform = prop.m_Point->m_Transform;;
|
CMatrix3D proptransform = prop.m_Point->m_Transform;;
|
||||||
if (prop.m_Point->m_BoneIndex != 0xff)
|
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]);
|
proptransform.Concatenate(m_BoneMatrices[prop.m_Point->m_BoneIndex]);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
// not relative to any bone; just apply world-space transformation (i.e. relative to object-space origin)
|
||||||
proptransform.Concatenate(m_Transform);
|
proptransform.Concatenate(m_Transform);
|
||||||
|
}
|
||||||
|
|
||||||
prop.m_Model->SetTransform(proptransform);
|
prop.m_Model->SetTransform(proptransform);
|
||||||
prop.m_Model->ValidatePosition();
|
prop.m_Model->ValidatePosition();
|
||||||
@ -410,6 +471,8 @@ void CModel::CopyAnimationFrom(CModel* source)
|
|||||||
void CModel::AddProp(const SPropPoint* point, CModelAbstract* model, CObjectEntry* objectentry)
|
void CModel::AddProp(const SPropPoint* point, CModelAbstract* model, CObjectEntry* objectentry)
|
||||||
{
|
{
|
||||||
// position model according to prop point position
|
// 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->SetTransform(point->m_Transform);
|
||||||
model->m_Parent = this;
|
model->m_Parent = this;
|
||||||
|
|
||||||
@ -426,6 +489,10 @@ void CModel::AddAmmoProp(const SPropPoint* point, CModelAbstract* model, CObject
|
|||||||
m_AmmoPropPoint = point;
|
m_AmmoPropPoint = point;
|
||||||
m_AmmoLoadedProp = m_Props.size() - 1;
|
m_AmmoLoadedProp = m_Props.size() - 1;
|
||||||
m_Props[m_AmmoLoadedProp].m_Hidden = true;
|
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()
|
void CModel::ShowAmmoProp()
|
||||||
@ -437,6 +504,10 @@ void CModel::ShowAmmoProp()
|
|||||||
for (size_t i = 0; i < m_Props.size(); ++i)
|
for (size_t i = 0; i < m_Props.size(); ++i)
|
||||||
if (m_Props[i].m_Point == m_AmmoPropPoint)
|
if (m_Props[i].m_Point == m_AmmoPropPoint)
|
||||||
m_Props[i].m_Hidden = (i != m_AmmoLoadedProp);
|
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()
|
void CModel::HideAmmoProp()
|
||||||
@ -448,6 +519,10 @@ void CModel::HideAmmoProp()
|
|||||||
for (size_t i = 0; i < m_Props.size(); ++i)
|
for (size_t i = 0; i < m_Props.size(); ++i)
|
||||||
if (m_Props[i].m_Point == m_AmmoPropPoint)
|
if (m_Props[i].m_Point == m_AmmoPropPoint)
|
||||||
m_Props[i].m_Hidden = (i == m_AmmoLoadedProp);
|
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()
|
CModelAbstract* CModel::FindFirstAmmoProp()
|
||||||
|
@ -54,11 +54,22 @@ public:
|
|||||||
{
|
{
|
||||||
Prop() : m_Point(0), m_Model(0), m_ObjectEntry(0), m_Hidden(false) {}
|
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;
|
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;
|
CModelAbstract* m_Model;
|
||||||
CObjectEntry* m_ObjectEntry;
|
CObjectEntry* m_ObjectEntry;
|
||||||
|
|
||||||
bool m_Hidden; // temporarily removed from rendering
|
bool m_Hidden; ///< Should this prop be temporarily removed from rendering?
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -75,8 +86,6 @@ public:
|
|||||||
|
|
||||||
// setup model from given geometry
|
// setup model from given geometry
|
||||||
bool InitModel(const CModelDefPtr& modeldef);
|
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
|
// update this model's state; 'time' is the absolute time since the start of the animation, in MS
|
||||||
void UpdateTo(float time);
|
void UpdateTo(float time);
|
||||||
|
|
||||||
@ -133,12 +142,35 @@ public:
|
|||||||
m_Props[i].m_Model->SetEntityVariable(name, value);
|
m_Props[i].m_Model->SetEntityVariable(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate object space bounds of this model, based solely on vertex positions
|
// --- WORLD/OBJECT SPACE BOUNDS -----------------------------------------------------------------
|
||||||
void CalcObjectBounds();
|
|
||||||
// calculate bounds encompassing all vertex positions for given animation
|
|
||||||
void CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result);
|
|
||||||
|
|
||||||
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.
|
* Set transform of this object.
|
||||||
@ -236,12 +268,20 @@ private:
|
|||||||
// object space bounds of model - accounts for bounds of all possible animations
|
// 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()
|
// that can play on a model. Not always up-to-date - currently CalcBounds()
|
||||||
// updates it when necessary.
|
// updates it when necessary.
|
||||||
CBound m_ObjectBounds;
|
CBoundingBoxAligned m_ObjectBounds;
|
||||||
// animation currently playing on this model, if any
|
// animation currently playing on this model, if any
|
||||||
CSkeletonAnim* m_Anim;
|
CSkeletonAnim* m_Anim;
|
||||||
// time (in MS) into the current animation
|
// time (in MS) into the current animation
|
||||||
float m_AnimTime;
|
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;
|
CMatrix3D* m_BoneMatrices;
|
||||||
// inverse matrices for the bind pose's bones; null if not skeletal
|
// inverse matrices for the bind pose's bones; null if not skeletal
|
||||||
CMatrix3D* m_InverseBindBoneMatrices;
|
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
|
#ifndef INCLUDED_MODELABSTRACT
|
||||||
#define INCLUDED_MODELABSTRACT
|
#define INCLUDED_MODELABSTRACT
|
||||||
|
|
||||||
|
#include "maths/BoundingBoxOriented.h"
|
||||||
#include "graphics/RenderableObject.h"
|
#include "graphics/RenderableObject.h"
|
||||||
#include "ps/Overlay.h"
|
#include "ps/Overlay.h"
|
||||||
#include "simulation2/helpers/Player.h"
|
#include "simulation2/helpers/Player.h"
|
||||||
@ -37,10 +38,36 @@ class CModelAbstract : public CRenderableObject
|
|||||||
NONCOPYABLE(CModelAbstract);
|
NONCOPYABLE(CModelAbstract);
|
||||||
|
|
||||||
public:
|
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;
|
virtual CModelAbstract* Clone() const = 0;
|
||||||
@ -58,15 +85,47 @@ public:
|
|||||||
// and this seems the easiest way to integrate with other code that wants
|
// and this seems the easiest way to integrate with other code that wants
|
||||||
// type-specific processing)
|
// 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;
|
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.
|
* 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)) { }
|
virtual void SetEntityVariable(const std::string& UNUSED(name), float UNUSED(value)) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure that both the transformation and the bone
|
* Ensure that both the transformation and the bone matrices are correct for this model and all its props.
|
||||||
* matrices are correct for this model and all its props.
|
|
||||||
*/
|
*/
|
||||||
virtual void ValidatePosition() = 0;
|
virtual void ValidatePosition() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark this model's position and bone matrices,
|
* Mark this model's position and bone matrices, and all props' positions as invalid.
|
||||||
* and all props' positions as invalid.
|
|
||||||
*/
|
*/
|
||||||
virtual void InvalidatePosition() = 0;
|
virtual void InvalidatePosition() = 0;
|
||||||
|
|
||||||
@ -100,7 +157,11 @@ public:
|
|||||||
virtual void SetShadingColor(const CColor& colour) { m_ShadingColor = colour; }
|
virtual void SetShadingColor(const CColor& colour) { m_ShadingColor = colour; }
|
||||||
virtual CColor GetShadingColor() const { return m_ShadingColor; }
|
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;
|
CModelAbstract* m_Parent;
|
||||||
|
|
||||||
/// True if both transform and and bone matrices are valid.
|
/// True if both transform and and bone matrices are valid.
|
||||||
@ -108,8 +169,23 @@ public:
|
|||||||
|
|
||||||
player_id_t m_PlayerID;
|
player_id_t m_PlayerID;
|
||||||
|
|
||||||
// modulating color
|
/// Modulating color
|
||||||
CColor m_ShadingColor;
|
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
|
#endif // INCLUDED_MODELABSTRACT
|
||||||
|
@ -32,19 +32,49 @@
|
|||||||
|
|
||||||
class CBoneState;
|
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
|
struct SPropPoint
|
||||||
{
|
{
|
||||||
// name of the prop point
|
/// Name of the prop point
|
||||||
CStr m_Name;
|
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;
|
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;
|
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;
|
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;
|
u8 m_BoneIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -246,3 +276,4 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ bool CObjectEntry::BuildVariation(const std::vector<std::set<CStr> >& selections
|
|||||||
model->SetTexture(texture);
|
model->SetTexture(texture);
|
||||||
|
|
||||||
// calculate initial object space bounds, based on vertex positions
|
// calculate initial object space bounds, based on vertex positions
|
||||||
model->CalcObjectBounds();
|
model->CalcStaticObjectBounds();
|
||||||
|
|
||||||
// load the animations
|
// load the animations
|
||||||
for (std::multimap<CStr, CObjectBase::Anim>::iterator it = variation.anims.begin(); it != variation.anims.end(); ++it)
|
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;
|
CColor m_Color;
|
||||||
std::vector<float> m_Coords; // (x, y, z) vertex coordinate triples; shape is not automatically closed
|
std::vector<float> m_Coords; // (x, y, z) vertex coordinate triples; shape is not automatically closed
|
||||||
u8 m_Thickness; // pixels
|
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);
|
ENSURE(m_Particles.size() <= m_Type->m_MaxParticles);
|
||||||
|
|
||||||
CBound bounds;
|
CBoundingBoxAligned bounds;
|
||||||
|
|
||||||
for (size_t i = 0; i < m_Particles.size(); ++i)
|
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
|
// current computed particle positions plus the emitter type's largest
|
||||||
// potential bounding box at the current position
|
// 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()
|
void CModelParticleEmitter::ValidatePosition()
|
||||||
|
@ -83,7 +83,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Get the bounding box of the center points of particles at their current positions.
|
* 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.)
|
* Push a new particle onto the ring buffer. (May overwrite an old particle.)
|
||||||
@ -133,7 +133,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
/// Bounding box of the current particle center points
|
/// Bounding box of the current particle center points
|
||||||
CBound m_ParticleBounds;
|
CBoundingBoxAligned m_ParticleBounds;
|
||||||
|
|
||||||
VertexArray m_VertexArray;
|
VertexArray m_VertexArray;
|
||||||
VertexArray::Attribute m_AttributePos;
|
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[0] += emitterPos;
|
||||||
bounds[1] += emitterPos;
|
bounds[1] += emitterPos;
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
#include "graphics/Texture.h"
|
#include "graphics/Texture.h"
|
||||||
#include "lib/ogl.h"
|
#include "lib/ogl.h"
|
||||||
#include "lib/file/vfs/vfs_path.h"
|
#include "lib/file/vfs/vfs_path.h"
|
||||||
#include "maths/Bound.h"
|
#include "maths/BoundingBoxAligned.h"
|
||||||
|
|
||||||
class CVector3D;
|
class CVector3D;
|
||||||
class CParticleEmitter;
|
class CParticleEmitter;
|
||||||
@ -88,7 +88,7 @@ private:
|
|||||||
*/
|
*/
|
||||||
void UpdateEmitterStep(CParticleEmitter& emitter, float dt);
|
void UpdateEmitterStep(CParticleEmitter& emitter, float dt);
|
||||||
|
|
||||||
CBound CalculateBounds(CVector3D emitterPos, CBound emittedBounds);
|
CBoundingBoxAligned CalculateBounds(CVector3D emitterPos, CBoundingBoxAligned emittedBounds);
|
||||||
|
|
||||||
CTexturePtr m_Texture;
|
CTexturePtr m_Texture;
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ private:
|
|||||||
|
|
||||||
float m_MaxLifetime;
|
float m_MaxLifetime;
|
||||||
size_t m_MaxParticles;
|
size_t m_MaxParticles;
|
||||||
CBound m_MaxBounds;
|
CBoundingBoxAligned m_MaxBounds;
|
||||||
|
|
||||||
typedef shared_ptr<IParticleVar> IParticleVarPtr;
|
typedef shared_ptr<IParticleVar> IParticleVarPtr;
|
||||||
std::vector<IParticleVarPtr> m_Variables;
|
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
|
// CalcBounds: calculating the bounds of this patch
|
||||||
void CPatch::CalcBounds()
|
void CPatch::CalcBounds()
|
||||||
{
|
{
|
||||||
m_Bounds.SetEmpty();
|
m_WorldBounds.SetEmpty();
|
||||||
|
|
||||||
for (ssize_t j=0;j<PATCH_SIZE+1;j++)
|
for (ssize_t j=0;j<PATCH_SIZE+1;j++)
|
||||||
{
|
{
|
||||||
@ -65,14 +65,14 @@ void CPatch::CalcBounds()
|
|||||||
{
|
{
|
||||||
CVector3D pos;
|
CVector3D pos;
|
||||||
m_Parent->CalcPosition(m_X*PATCH_SIZE+i,m_Z*PATCH_SIZE+j,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
|
// If this a side patch, the sides go down to height 0, so add them
|
||||||
// into the bounds
|
// into the bounds
|
||||||
if (GetSideFlags())
|
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()
|
int CPatch::GetSideFlags()
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
#define INCLUDED_RENDERABLEOBJECT
|
#define INCLUDED_RENDERABLEOBJECT
|
||||||
|
|
||||||
|
|
||||||
#include "maths/Bound.h"
|
#include "maths/BoundingBoxAligned.h"
|
||||||
#include "maths/Matrix3D.h"
|
#include "maths/Matrix3D.h"
|
||||||
|
|
||||||
|
|
||||||
@ -86,20 +86,29 @@ public:
|
|||||||
if (m_RenderData) m_RenderData->m_UpdateFlags|=dirtyflags;
|
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;
|
virtual void CalcBounds() = 0;
|
||||||
|
|
||||||
// return world space bounds of this object
|
/// Returns the world-space axis-aligned bounds of this object.
|
||||||
const CBound& GetBounds() {
|
const CBoundingBoxAligned& GetWorldBounds() {
|
||||||
if (! m_BoundsValid) {
|
RecalculateBoundsIfNecessary();
|
||||||
CalcBounds();
|
return m_WorldBounds;
|
||||||
m_BoundsValid = true;
|
|
||||||
}
|
|
||||||
return m_Bounds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
// Set the object renderdata and free previous renderdata, if any.
|
||||||
void SetRenderData(CRenderData* renderdata) {
|
void SetRenderData(CRenderData* renderdata) {
|
||||||
@ -107,13 +116,23 @@ public:
|
|||||||
m_RenderData = renderdata;
|
m_RenderData = renderdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
// return object renderdata - can be null if renderer hasn't yet
|
/// Return object renderdata - can be null if renderer hasn't yet created the renderdata
|
||||||
// created the renderdata
|
|
||||||
CRenderData* GetRenderData() { return m_RenderData; }
|
CRenderData* GetRenderData() { return m_RenderData; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// object bounds
|
/// Factored out so subclasses don't need to repeat this if they want to add additional getters for bounds-related methods
|
||||||
CBound m_Bounds;
|
/// (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
|
// local->world space transform
|
||||||
CMatrix3D m_Transform;
|
CMatrix3D m_Transform;
|
||||||
// world->local space transform
|
// world->local space transform
|
||||||
@ -121,8 +140,17 @@ protected:
|
|||||||
// object renderdata
|
// object renderdata
|
||||||
CRenderData* m_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;
|
bool m_BoundsValid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#ifndef INCLUDED_SKELETONANIM
|
#ifndef INCLUDED_SKELETONANIM
|
||||||
#define INCLUDED_SKELETONANIM
|
#define INCLUDED_SKELETONANIM
|
||||||
|
|
||||||
#include "maths/Bound.h"
|
#include "maths/BoundingBoxAligned.h"
|
||||||
|
|
||||||
class CSkeletonAnimDef;
|
class CSkeletonAnimDef;
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ public:
|
|||||||
float m_ActionPos;
|
float m_ActionPos;
|
||||||
float m_ActionPos2;
|
float m_ActionPos2;
|
||||||
// object space bounds of the model when this animation is applied to it
|
// object space bounds of the model when this animation is applied to it
|
||||||
CBound m_ObjectBounds;
|
CBoundingBoxAligned m_ObjectBounds;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#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);
|
i0 = clamp(i0, (ssize_t)0, m_MapSize-1);
|
||||||
j0 = clamp(j0, (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].X = (float)(i0*CELL_SIZE);
|
||||||
bound[0].Y = (float)(minH*HEIGHT_SCALE);
|
bound[0].Y = (float)(minH*HEIGHT_SCALE);
|
||||||
bound[0].Z = (float)(j0*CELL_SIZE);
|
bound[0].Z = (float)(j0*CELL_SIZE);
|
||||||
|
@ -30,7 +30,7 @@ class CPatch;
|
|||||||
class CMiniPatch;
|
class CMiniPatch;
|
||||||
class CFixedVector3D;
|
class CFixedVector3D;
|
||||||
class CStr8;
|
class CStr8;
|
||||||
class CBound;
|
class CBoundingBoxAligned;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Terrain Constants:
|
// Terrain Constants:
|
||||||
@ -140,7 +140,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Returns a 3D bounding box encompassing the given vertex range (inclusive)
|
* 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
|
// get the base colour for the terrain (typically pure white - other colours
|
||||||
// will interact badly with LOS - but used by the Actor Viewer tool)
|
// 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];
|
CUnit* unit = m_Units[i];
|
||||||
float tmin, tmax;
|
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
|
// Point of closest approach
|
||||||
CVector3D obj;
|
// TODO: this next bit is virtually identical to Selection::PickEntitiesAtPoint; might be useful to factor it out and
|
||||||
unit->GetModel().GetBounds().GetCentre(obj);
|
// reuse it
|
||||||
CVector3D delta = obj - origin;
|
CVector3D delta = selectionBox.m_Center - origin;
|
||||||
float distance = delta.Dot(dir);
|
float distance = delta.Dot(dir);
|
||||||
CVector3D closest = origin + dir * distance;
|
CVector3D closest = origin + dir * distance;
|
||||||
CVector3D offset = obj - closest;
|
CVector3D offset = selectionBox.m_Center - closest;
|
||||||
|
|
||||||
float rel = offset.Length();
|
float rel = offset.Length();
|
||||||
if (rel < minrel) {
|
if (rel < minrel) {
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
#include "simulation2/components/ICmpGuiInterface.h"
|
#include "simulation2/components/ICmpGuiInterface.h"
|
||||||
#include "simulation2/components/ICmpRangeManager.h"
|
#include "simulation2/components/ICmpRangeManager.h"
|
||||||
#include "simulation2/components/ICmpTemplateManager.h"
|
#include "simulation2/components/ICmpTemplateManager.h"
|
||||||
|
#include "simulation2/components/ICmpSelectable.h"
|
||||||
#include "simulation2/helpers/Selection.h"
|
#include "simulation2/helpers/Selection.h"
|
||||||
|
|
||||||
#include "js/jsapi.h"
|
#include "js/jsapi.h"
|
||||||
@ -518,6 +519,10 @@ void QuickLoad(void* UNUSED(cbdata))
|
|||||||
{
|
{
|
||||||
g_Game->GetTurnManager()->QuickLoad();
|
g_Game->GetTurnManager()->QuickLoad();
|
||||||
}
|
}
|
||||||
|
void SetBoundingBoxDebugOverlay(void* UNUSED(cbdata), bool enabled)
|
||||||
|
{
|
||||||
|
ICmpSelectable::ms_EnableDebugOverlays = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -590,4 +595,5 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
|
|||||||
scriptInterface.RegisterFunction<void, &DumpSimState>("DumpSimState");
|
scriptInterface.RegisterFunction<void, &DumpSimState>("DumpSimState");
|
||||||
scriptInterface.RegisterFunction<void, unsigned int, &EnableTimeWarpRecording>("EnableTimeWarpRecording");
|
scriptInterface.RegisterFunction<void, unsigned int, &EnableTimeWarpRecording>("EnableTimeWarpRecording");
|
||||||
scriptInterface.RegisterFunction<void, &RewindTimeWarp>("RewindTimeWarp");
|
scriptInterface.RegisterFunction<void, &RewindTimeWarp>("RewindTimeWarp");
|
||||||
|
scriptInterface.RegisterFunction<void, bool, &SetBoundingBoxDebugOverlay>("SetBoundingBoxDebugOverlay");
|
||||||
}
|
}
|
||||||
|
@ -21,23 +21,25 @@
|
|||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
|
|
||||||
#include "Bound.h"
|
#include "BoundingBoxAligned.h"
|
||||||
|
|
||||||
#include "lib/ogl.h"
|
#include "lib/ogl.h"
|
||||||
|
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
|
|
||||||
#include "graphics/Frustum.h"
|
#include "graphics/Frustum.h"
|
||||||
|
#include "maths/BoundingBoxOriented.h"
|
||||||
#include "maths/Brush.h"
|
#include "maths/Brush.h"
|
||||||
#include "maths/Matrix3D.h"
|
#include "maths/Matrix3D.h"
|
||||||
|
|
||||||
|
const CBoundingBoxAligned CBoundingBoxAligned::EMPTY = CBoundingBoxAligned(); // initializes to an empty bound
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// RayIntersect: intersect ray with this bound; return true
|
// RayIntersect: intersect ray with this bound; return true
|
||||||
// if ray hits (and store entry and exit times), or false
|
// if ray hits (and store entry and exit times), or false
|
||||||
// otherwise
|
// otherwise
|
||||||
// note: incoming ray direction must be normalised
|
// 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& tmin,float& tmax) const
|
||||||
{
|
{
|
||||||
float t1,t2;
|
float t1,t2;
|
||||||
@ -118,7 +120,7 @@ bool CBound::RayIntersect(const CVector3D& origin,const CVector3D& dir,
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// SetEmpty: initialise this bound as empty
|
// SetEmpty: initialise this bound as empty
|
||||||
void CBound::SetEmpty()
|
void CBoundingBoxAligned::SetEmpty()
|
||||||
{
|
{
|
||||||
m_Data[0]=CVector3D( FLT_MAX, FLT_MAX, FLT_MAX);
|
m_Data[0]=CVector3D( FLT_MAX, FLT_MAX, FLT_MAX);
|
||||||
m_Data[1]=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
|
// 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
|
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);
|
&& 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
|
// Transform: transform this bound by given matrix; return transformed bound
|
||||||
// in 'result' parameter - slightly modified version of code in Graphic Gems
|
// in 'result' parameter - slightly modified version of code in Graphic Gems
|
||||||
// (can't remember which one it was, though)
|
// (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);
|
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
|
// 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 brush(*this);
|
||||||
CBrush buf;
|
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[0] -= CVector3D(amount, amount, amount);
|
||||||
m_Data[1] += CVector3D(amount, amount, amount);
|
m_Data[1] += CVector3D(amount, amount, amount);
|
||||||
@ -184,7 +235,7 @@ void CBound::Expand(float amount)
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Render the bounding box
|
// Render the bounding box
|
||||||
void CBound::Render() const
|
void CBoundingBoxAligned::Render() const
|
||||||
{
|
{
|
||||||
glBegin(GL_QUADS);
|
glBegin(GL_QUADS);
|
||||||
glTexCoord2f(0, 0); glVertex3f(m_Data[0].X, m_Data[0].Y, m_Data[0].Z);
|
glTexCoord2f(0, 0); glVertex3f(m_Data[0].X, m_Data[0].Y, m_Data[0].Z);
|
@ -27,18 +27,32 @@
|
|||||||
|
|
||||||
class CFrustum;
|
class CFrustum;
|
||||||
class CMatrix3D;
|
class CMatrix3D;
|
||||||
|
class CBoundingBoxOriented;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// CBound: basic axis aligned bounding box class
|
// basic axis aligned bounding box (AABB) class
|
||||||
class CBound
|
class CBoundingBoxAligned
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CBound() { SetEmpty(); }
|
|
||||||
CBound(const CVector3D& min,const CVector3D& max) {
|
CBoundingBoxAligned() { SetEmpty(); }
|
||||||
m_Data[0]=min; m_Data[1]=max;
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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]; }
|
CVector3D& operator[](int index) { return m_Data[index]; }
|
||||||
const CVector3D& operator[](int index) const { return m_Data[index]; }
|
const CVector3D& operator[](int index) const { return m_Data[index]; }
|
||||||
@ -57,29 +71,42 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// operator+=: extend this bound to include given bound
|
// 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]);
|
Extend(b.m_Data[0], b.m_Data[1]);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// operator+=: extend this bound to include given point
|
// operator+=: extend this bound to include given point
|
||||||
CBound& operator+=(const CVector3D& pt)
|
CBoundingBoxAligned& operator+=(const CVector3D& pt)
|
||||||
{
|
{
|
||||||
Extend(pt, pt);
|
Extend(pt, pt);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
bool RayIntersect(const CVector3D& origin, const CVector3D& dir, float& tmin, float& tmax) const;
|
||||||
|
|
||||||
// return the volume of this bounding box
|
// return the volume of this bounding box
|
||||||
float GetVolume() const {
|
float GetVolume() const
|
||||||
|
{
|
||||||
CVector3D v = m_Data[1] - m_Data[0];
|
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 (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
|
// return the centre of this bounding box
|
||||||
void GetCentre(CVector3D& centre) const {
|
void GetCentre(CVector3D& centre) const
|
||||||
|
{
|
||||||
centre = (m_Data[0] + m_Data[1]) * 0.5f;
|
centre = (m_Data[0] + m_Data[1]) * 0.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +136,12 @@ public:
|
|||||||
void Render() const;
|
void Render() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Holds the minimal and maximal coordinate points in m_Data[0] and m_Data[1], respectively.
|
||||||
CVector3D m_Data[2];
|
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 <float.h>
|
||||||
|
|
||||||
#include "Brush.h"
|
#include "Brush.h"
|
||||||
#include "Bound.h"
|
#include "BoundingBoxAligned.h"
|
||||||
#include "graphics/Frustum.h"
|
#include "graphics/Frustum.h"
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Convert the given bounds into a brush
|
// Convert the given bounds into a brush
|
||||||
CBrush::CBrush(const CBound& bounds)
|
CBrush::CBrush(const CBoundingBoxAligned& bounds)
|
||||||
{
|
{
|
||||||
m_Vertices.resize(8);
|
m_Vertices.resize(8);
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ CBrush::CBrush(const CBound& bounds)
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Calculate bounds of this brush
|
// Calculate bounds of this brush
|
||||||
void CBrush::Bounds(CBound& result) const
|
void CBrush::Bounds(CBoundingBoxAligned& result) const
|
||||||
{
|
{
|
||||||
result.SetEmpty();
|
result.SetEmpty();
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
#include "Vector3D.h"
|
#include "Vector3D.h"
|
||||||
|
|
||||||
class CBound;
|
class CBoundingBoxAligned;
|
||||||
class CFrustum;
|
class CFrustum;
|
||||||
class CPlane;
|
class CPlane;
|
||||||
|
|
||||||
@ -40,9 +40,9 @@ public:
|
|||||||
/**
|
/**
|
||||||
* CBrush: Construct a brush from a bounds object.
|
* 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.
|
* IsEmpty: Returns whether the brush is empty.
|
||||||
@ -56,7 +56,7 @@ public:
|
|||||||
*
|
*
|
||||||
* @param result the resulting bounding box is stored here
|
* @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)
|
* Slice: Cut the object along the given plane, resulting in a smaller (or even empty)
|
||||||
|
@ -35,6 +35,7 @@ class CMatrix3D
|
|||||||
public:
|
public:
|
||||||
// the matrix data itself - accessible as either longhand names
|
// the matrix data itself - accessible as either longhand names
|
||||||
// or via a flat or 2d array
|
// 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 {
|
union {
|
||||||
struct {
|
struct {
|
||||||
float _11, _21, _31, _41;
|
float _11, _21, _31, _41;
|
||||||
@ -155,6 +156,12 @@ public:
|
|||||||
_14 == m._14 && _24 == m._24 && _34 == m._34 && _44 == m._44;
|
_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
|
// set this matrix to the identity matrix
|
||||||
void SetIdentity();
|
void SetIdentity();
|
||||||
// set this matrix to the zero matrix
|
// set this matrix to the zero matrix
|
||||||
|
@ -17,14 +17,14 @@
|
|||||||
|
|
||||||
#include "lib/self_test.h"
|
#include "lib/self_test.h"
|
||||||
|
|
||||||
#include "maths/Bound.h"
|
#include "maths/BoundingBoxAligned.h"
|
||||||
|
|
||||||
class TestBound : public CxxTest::TestSuite
|
class TestBound : public CxxTest::TestSuite
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void test_empty()
|
void test_empty()
|
||||||
{
|
{
|
||||||
CBound bound;
|
CBoundingBoxAligned bound;
|
||||||
TS_ASSERT(bound.IsEmpty());
|
TS_ASSERT(bound.IsEmpty());
|
||||||
bound += CVector3D(1, 2, 3);
|
bound += CVector3D(1, 2, 3);
|
||||||
TS_ASSERT(! bound.IsEmpty());
|
TS_ASSERT(! bound.IsEmpty());
|
||||||
@ -34,7 +34,7 @@ public:
|
|||||||
|
|
||||||
void test_extend_vector()
|
void test_extend_vector()
|
||||||
{
|
{
|
||||||
CBound bound;
|
CBoundingBoxAligned bound;
|
||||||
CVector3D v (1, 2, 3);
|
CVector3D v (1, 2, 3);
|
||||||
bound += v;
|
bound += v;
|
||||||
|
|
||||||
@ -45,9 +45,9 @@ public:
|
|||||||
|
|
||||||
void test_extend_bound()
|
void test_extend_bound()
|
||||||
{
|
{
|
||||||
CBound bound;
|
CBoundingBoxAligned bound;
|
||||||
CVector3D v (1, 2, 3);
|
CVector3D v (1, 2, 3);
|
||||||
CBound b (v, v);
|
CBoundingBoxAligned b (v, v);
|
||||||
bound += b;
|
bound += b;
|
||||||
|
|
||||||
CVector3D centre;
|
CVector3D centre;
|
||||||
|
@ -158,7 +158,7 @@ void ParticleRenderer::RenderBounds()
|
|||||||
{
|
{
|
||||||
CParticleEmitter* emitter = m->emitters[i];
|
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();
|
bounds.Render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ public:
|
|||||||
|
|
||||||
CPatch* GetPatch() { return m_Patch; }
|
CPatch* GetPatch() { return m_Patch; }
|
||||||
|
|
||||||
const CBound& GetWaterBounds() const { return m_WaterBounds; }
|
const CBoundingBoxAligned& GetWaterBounds() const { return m_WaterBounds; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend struct SBlendStackItem;
|
friend struct SBlendStackItem;
|
||||||
@ -145,7 +145,7 @@ private:
|
|||||||
std::vector<SSplat> m_BlendSplats;
|
std::vector<SSplat> m_BlendSplats;
|
||||||
|
|
||||||
// boundary of water in this patch
|
// boundary of water in this patch
|
||||||
CBound m_WaterBounds;
|
CBoundingBoxAligned m_WaterBounds;
|
||||||
|
|
||||||
// Water vertex buffer
|
// Water vertex buffer
|
||||||
CVertexBuffer::VBChunk* m_VBWater;
|
CVertexBuffer::VBChunk* m_VBWater;
|
||||||
|
@ -1041,7 +1041,7 @@ public:
|
|||||||
|
|
||||||
bool Filter(CModel *model)
|
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:
|
private:
|
||||||
@ -1198,7 +1198,7 @@ void CRenderer::SetObliqueFrustumClipping(const CVector4D& worldPlane)
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// RenderReflections: render the water reflections to the reflection texture
|
// 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");
|
PROFILE3_GPU("water reflections");
|
||||||
|
|
||||||
@ -1285,7 +1285,7 @@ SScreenRect CRenderer::RenderReflections(const CBound& scissor)
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// RenderRefractions: render the water refractions to the refraction texture
|
// 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");
|
PROFILE3_GPU("water refractions");
|
||||||
|
|
||||||
@ -1540,7 +1540,7 @@ void CRenderer::RenderSubmissions()
|
|||||||
|
|
||||||
ogl_WarnIfError();
|
ogl_WarnIfError();
|
||||||
|
|
||||||
CBound waterScissor;
|
CBoundingBoxAligned waterScissor;
|
||||||
if (m_WaterManager->m_RenderWater)
|
if (m_WaterManager->m_RenderWater)
|
||||||
{
|
{
|
||||||
waterScissor = m->terrainRenderer->ScissorWater(m_ViewCamera.GetViewProjection());
|
waterScissor = m->terrainRenderer->ScissorWater(m_ViewCamera.GetViewProjection());
|
||||||
@ -1756,10 +1756,10 @@ void CRenderer::SubmitNonRecursive(CModel* model)
|
|||||||
{
|
{
|
||||||
if (model->GetFlags() & MODELFLAG_CASTSHADOWS) {
|
if (model->GetFlags() & MODELFLAG_CASTSHADOWS) {
|
||||||
// PROFILE( "updating shadow bounds" );
|
// 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();
|
model->ValidatePosition();
|
||||||
|
|
||||||
bool canUseInstancing = false;
|
bool canUseInstancing = false;
|
||||||
|
@ -348,8 +348,8 @@ protected:
|
|||||||
void RenderShadowMap();
|
void RenderShadowMap();
|
||||||
|
|
||||||
// render water reflection and refraction textures
|
// render water reflection and refraction textures
|
||||||
SScreenRect RenderReflections(const CBound& scissor);
|
SScreenRect RenderReflections(const CBoundingBoxAligned& scissor);
|
||||||
SScreenRect RenderRefractions(const CBound& scissor);
|
SScreenRect RenderRefractions(const CBoundingBoxAligned& scissor);
|
||||||
|
|
||||||
// debugging
|
// debugging
|
||||||
void DisplayFrustum();
|
void DisplayFrustum();
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
#include "graphics/LightEnv.h"
|
#include "graphics/LightEnv.h"
|
||||||
|
|
||||||
#include "maths/Bound.h"
|
#include "maths/BoundingBoxAligned.h"
|
||||||
#include "maths/MathUtil.h"
|
#include "maths/MathUtil.h"
|
||||||
#include "maths/Matrix3D.h"
|
#include "maths/Matrix3D.h"
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ struct ShadowMapInternals
|
|||||||
// transform light space into world space
|
// transform light space into world space
|
||||||
CMatrix3D InvLightTransform;
|
CMatrix3D InvLightTransform;
|
||||||
// bounding box of shadowed objects in light space
|
// bounding box of shadowed objects in light space
|
||||||
CBound ShadowBound;
|
CBoundingBoxAligned ShadowBound;
|
||||||
|
|
||||||
// Camera transformed into light space
|
// Camera transformed into light space
|
||||||
CCamera LightspaceCamera;
|
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
|
// AddShadowedBound: add a world-space bounding box to the bounds of shadowed
|
||||||
// objects
|
// objects
|
||||||
void ShadowMap::AddShadowedBound(const CBound& bounds)
|
void ShadowMap::AddShadowedBound(const CBoundingBoxAligned& bounds)
|
||||||
{
|
{
|
||||||
CBound lightspacebounds;
|
CBoundingBoxAligned lightspacebounds;
|
||||||
|
|
||||||
bounds.Transform(m->LightTransform, lightspacebounds);
|
bounds.Transform(m->LightTransform, lightspacebounds);
|
||||||
m->ShadowBound += lightspacebounds;
|
m->ShadowBound += lightspacebounds;
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
#include "lib/ogl.h"
|
#include "lib/ogl.h"
|
||||||
|
|
||||||
class CBound;
|
class CBoundingBoxAligned;
|
||||||
class CMatrix3D;
|
class CMatrix3D;
|
||||||
|
|
||||||
struct ShadowMapInternals;
|
struct ShadowMapInternals;
|
||||||
@ -80,7 +80,7 @@ public:
|
|||||||
*
|
*
|
||||||
* @param bounds world space bounding box
|
* @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.
|
* BeginRender: Set OpenGL state for rendering into the shadow map texture.
|
||||||
|
@ -173,14 +173,14 @@ bool TerrainRenderer::CullPatches(const CFrustum* frustum)
|
|||||||
m->filteredPatches.clear();
|
m->filteredPatches.clear();
|
||||||
for (std::vector<CPatchRData*>::iterator it = m->visiblePatches.begin(); it != m->visiblePatches.end(); it++)
|
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->filteredPatches.push_back(*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
m->filteredDecals.clear();
|
m->filteredDecals.clear();
|
||||||
for (std::vector<CDecalRData*>::iterator it = m->visibleDecals.begin(); it != m->visibleDecals.end(); it++)
|
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);
|
m->filteredDecals.push_back(*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,13 +557,13 @@ void TerrainRenderer::RenderOutlines(bool filtered)
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////
|
||||||
// Scissor rectangle of water patches
|
// 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)
|
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
|
||||||
{
|
{
|
||||||
CPatchRData* data = m->visiblePatches[i];
|
CPatchRData* data = m->visiblePatches[i];
|
||||||
const CBound& waterBounds = data->GetWaterBounds();
|
const CBoundingBoxAligned& waterBounds = data->GetWaterBounds();
|
||||||
if (waterBounds.IsEmpty())
|
if (waterBounds.IsEmpty())
|
||||||
continue;
|
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 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 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));
|
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) \
|
#define ADDBOUND(v1, v2, v3, v4) \
|
||||||
if (v1[2] >= -v1[3]) \
|
if (v1[2] >= -v1[3]) \
|
||||||
screenBounds += CVector3D(v1[0], v1[1], v1[2]) * (1.0f / 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;
|
continue;
|
||||||
scissor += screenBounds;
|
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));
|
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.
|
* 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.
|
* Render priority text for all submitted patches, for debugging.
|
||||||
|
@ -71,6 +71,10 @@ COMPONENT(CommandQueue)
|
|||||||
INTERFACE(Decay)
|
INTERFACE(Decay)
|
||||||
COMPONENT(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)
|
INTERFACE(Footprint)
|
||||||
COMPONENT(Footprint)
|
COMPONENT(Footprint)
|
||||||
|
|
||||||
@ -142,6 +146,8 @@ COMPONENT(UnitMotionScripted)
|
|||||||
INTERFACE(Vision)
|
INTERFACE(Vision)
|
||||||
COMPONENT(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)
|
INTERFACE(Visual)
|
||||||
COMPONENT(VisualActor) // must be after Ownership (dependency in Deserialize)
|
COMPONENT(VisualActor) // must be after Ownership (dependency in Deserialize)
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ public:
|
|||||||
CmpPtr<ICmpVisual> cmpVisual(GetSimContext(), GetEntityId());
|
CmpPtr<ICmpVisual> cmpVisual(GetSimContext(), GetEntityId());
|
||||||
if (!cmpVisual.null())
|
if (!cmpVisual.null())
|
||||||
{
|
{
|
||||||
CBound bound = cmpVisual->GetBounds();
|
CBoundingBoxAligned bound = cmpVisual->GetBounds();
|
||||||
m_TotalSinkDepth = std::max(m_TotalSinkDepth, bound[1].Y - bound[0].Y);
|
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();
|
model.ValidatePosition();
|
||||||
|
|
||||||
if (culling && !frustum.IsBoxVisible(CVector3D(0, 0, 0), model.GetBounds()))
|
if (culling && !frustum.IsBoxVisible(CVector3D(0, 0, 0), model.GetWorldBounds()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// TODO: do something about LOS (copy from CCmpVisualActor)
|
// TODO: do something about LOS (copy from CCmpVisualActor)
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "ICmpPosition.h"
|
#include "ICmpPosition.h"
|
||||||
#include "ICmpFootprint.h"
|
#include "ICmpFootprint.h"
|
||||||
|
#include "ICmpVisual.h"
|
||||||
#include "simulation2/MessageTypes.h"
|
#include "simulation2/MessageTypes.h"
|
||||||
#include "simulation2/helpers/Render.h"
|
#include "simulation2/helpers/Render.h"
|
||||||
|
|
||||||
@ -45,13 +46,21 @@ public:
|
|||||||
DEFAULT_COMPONENT_ALLOCATOR(Selectable)
|
DEFAULT_COMPONENT_ALLOCATOR(Selectable)
|
||||||
|
|
||||||
SOverlayLine m_Overlay;
|
SOverlayLine m_Overlay;
|
||||||
|
SOverlayLine* m_DebugBoundingBoxOverlay;
|
||||||
|
SOverlayLine* m_DebugSelectionBoxOverlay;
|
||||||
|
|
||||||
CCmpSelectable()
|
CCmpSelectable()
|
||||||
|
: m_DebugBoundingBoxOverlay(NULL), m_DebugSelectionBoxOverlay(NULL)
|
||||||
{
|
{
|
||||||
m_Overlay.m_Thickness = 2;
|
m_Overlay.m_Thickness = 2;
|
||||||
m_Overlay.m_Color = CColor(0, 0, 0, 0);
|
m_Overlay.m_Color = CColor(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~CCmpSelectable(){
|
||||||
|
delete m_DebugBoundingBoxOverlay;
|
||||||
|
delete m_DebugSelectionBoxOverlay;
|
||||||
|
}
|
||||||
|
|
||||||
static std::string GetSchema()
|
static std::string GetSchema()
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
@ -153,8 +162,35 @@ public:
|
|||||||
void RenderSubmit(SceneCollector& collector)
|
void RenderSubmit(SceneCollector& collector)
|
||||||
{
|
{
|
||||||
// (This is only called if a > 0)
|
// (This is only called if a > 0)
|
||||||
|
|
||||||
collector.Submit(&m_Overlay);
|
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 "ICmpRangeManager.h"
|
||||||
#include "ICmpVision.h"
|
#include "ICmpVision.h"
|
||||||
#include "simulation2/MessageTypes.h"
|
#include "simulation2/MessageTypes.h"
|
||||||
|
#include "simulation2/components/ICmpFootprint.h"
|
||||||
|
|
||||||
#include "graphics/Frustum.h"
|
#include "graphics/Frustum.h"
|
||||||
#include "graphics/Model.h"
|
#include "graphics/Model.h"
|
||||||
@ -97,7 +98,38 @@ public:
|
|||||||
"</element>"
|
"</element>"
|
||||||
"<element name='SilhouetteOccluder'>"
|
"<element name='SilhouetteOccluder'>"
|
||||||
"<data type='boolean'/>"
|
"<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)
|
virtual void Init(const CParamNode& paramNode)
|
||||||
@ -126,8 +158,16 @@ public:
|
|||||||
if (paramNode.GetChild("SilhouetteOccluder").ToBool())
|
if (paramNode.GetChild("SilhouetteOccluder").ToBool())
|
||||||
modelFlags |= MODELFLAG_SILHOUETTE_OCCLUDER;
|
modelFlags |= MODELFLAG_SILHOUETTE_OCCLUDER;
|
||||||
|
|
||||||
if (m_Unit->GetModel().ToCModel())
|
CModelAbstract& model = m_Unit->GetModel();
|
||||||
m_Unit->GetModel().ToCModel()->AddFlagsRec(modelFlags);
|
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());
|
m_Unit->SetID(GetEntityId());
|
||||||
}
|
}
|
||||||
@ -245,11 +285,18 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual CBound GetBounds()
|
virtual CBoundingBoxAligned GetBounds()
|
||||||
{
|
{
|
||||||
if (!m_Unit)
|
if (!m_Unit)
|
||||||
return CBound();
|
return CBoundingBoxAligned::EMPTY;
|
||||||
return m_Unit->GetModel().GetBounds();
|
return m_Unit->GetModel().GetWorldBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual CBoundingBoxOriented GetSelectionBox()
|
||||||
|
{
|
||||||
|
if (!m_Unit)
|
||||||
|
return CBoundingBoxOriented::EMPTY;
|
||||||
|
return m_Unit->GetModel().GetSelectionBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual CVector3D GetPosition()
|
virtual CVector3D GetPosition()
|
||||||
@ -403,6 +450,13 @@ private:
|
|||||||
return GetEntityId();
|
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 Update(fixed turnLength);
|
||||||
void Interpolate(float frameTime, float frameOffset);
|
void Interpolate(float frameTime, float frameOffset);
|
||||||
void RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling);
|
void RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling);
|
||||||
@ -410,6 +464,78 @@ private:
|
|||||||
|
|
||||||
REGISTER_COMPONENT_TYPE(VisualActor)
|
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)
|
void CCmpVisualActor::Update(fixed turnLength)
|
||||||
{
|
{
|
||||||
if (m_Unit == NULL)
|
if (m_Unit == NULL)
|
||||||
@ -504,7 +630,7 @@ void CCmpVisualActor::RenderSubmit(SceneCollector& collector, const CFrustum& fr
|
|||||||
|
|
||||||
CModelAbstract& model = m_Unit->GetModel();
|
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;
|
return;
|
||||||
|
|
||||||
collector.SubmitRecursive(&model);
|
collector.SubmitRecursive(&model);
|
||||||
|
@ -26,3 +26,5 @@
|
|||||||
BEGIN_INTERFACE_WRAPPER(Selectable)
|
BEGIN_INTERFACE_WRAPPER(Selectable)
|
||||||
DEFINE_INTERFACE_METHOD_1("SetSelectionHighlight", void, ICmpSelectable, SetSelectionHighlight, CColor)
|
DEFINE_INTERFACE_METHOD_1("SetSelectionHighlight", void, ICmpSelectable, SetSelectionHighlight, CColor)
|
||||||
END_INTERFACE_WRAPPER(Selectable)
|
END_INTERFACE_WRAPPER(Selectable)
|
||||||
|
|
||||||
|
bool ICmpSelectable::ms_EnableDebugOverlays = false;
|
@ -32,6 +32,11 @@ public:
|
|||||||
virtual void SetSelectionHighlight(CColor color) = 0;
|
virtual void SetSelectionHighlight(CColor color) = 0;
|
||||||
|
|
||||||
DECLARE_INTERFACE_TYPE(Selectable)
|
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
|
#endif // INCLUDED_ICMPSELECTABLE
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
|
|
||||||
#include "simulation2/system/Interface.h"
|
#include "simulation2/system/Interface.h"
|
||||||
|
|
||||||
#include "maths/Bound.h"
|
#include "maths/BoundingBoxOriented.h"
|
||||||
|
#include "maths/BoundingBoxAligned.h"
|
||||||
#include "maths/Fixed.h"
|
#include "maths/Fixed.h"
|
||||||
#include "lib/file/vfs/vfs_path.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.
|
* Get the world-space bounding box of the object's visual representation.
|
||||||
* (Not safe for use in simulation code.)
|
* (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.
|
* 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 "simulation2/components/ICmpWaterManager.h"
|
||||||
#include "graphics/Overlay.h"
|
#include "graphics/Overlay.h"
|
||||||
#include "graphics/Terrain.h"
|
#include "graphics/Terrain.h"
|
||||||
|
#include "maths/BoundingBoxAligned.h"
|
||||||
|
#include "maths/BoundingBoxOriented.h"
|
||||||
#include "maths/MathUtil.h"
|
#include "maths/MathUtil.h"
|
||||||
#include "maths/Vector2D.h"
|
#include "maths/Vector2D.h"
|
||||||
#include "ps/Profile.h"
|
#include "ps/Profile.h"
|
||||||
|
#include "maths/Quaternion.h"
|
||||||
|
|
||||||
void SimRender::ConstructLineOnGround(const CSimContext& context, const std::vector<float>& xz,
|
void SimRender::ConstructLineOnGround(const CSimContext& context, const std::vector<float>& xz,
|
||||||
SOverlayLine& overlay, bool floating, float heightOffset)
|
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)
|
void SimRender::SmoothPointsAverage(std::vector<CVector2D>& points, bool closed)
|
||||||
{
|
{
|
||||||
PROFILE("SmoothPointsAverage");
|
PROFILE("SmoothPointsAverage");
|
||||||
|
@ -25,6 +25,9 @@
|
|||||||
|
|
||||||
class CSimContext;
|
class CSimContext;
|
||||||
class CVector2D;
|
class CVector2D;
|
||||||
|
class CVector3D;
|
||||||
|
class CBoundingBoxAligned;
|
||||||
|
class CBoundingBoxOriented;
|
||||||
struct SOverlayLine;
|
struct SOverlayLine;
|
||||||
|
|
||||||
namespace SimRender
|
namespace SimRender
|
||||||
@ -53,6 +56,23 @@ void ConstructSquareOnGround(const CSimContext& context, float x, float z, float
|
|||||||
SOverlayLine& overlay,
|
SOverlayLine& overlay,
|
||||||
bool floating, float heightOffset = 0.25f);
|
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
|
* Updates @p points so each point is averaged with its neighbours, resulting in
|
||||||
* a somewhat smoother curve, assuming the points are roughly equally spaced.
|
* 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())
|
if (cmpVisual.null())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CBound bounds = cmpVisual->GetBounds();
|
CBoundingBoxOriented selectionBox = cmpVisual->GetSelectionBox();
|
||||||
|
if (selectionBox.IsEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
float tmin, tmax;
|
float tmin, tmax;
|
||||||
if (!bounds.RayIntersect(origin, dir, tmin, tmax))
|
if (!selectionBox.RayIntersect(origin, dir, tmin, tmax))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Find the perpendicular distance from the object's centre to the picker ray
|
// Find the perpendicular distance from the object's centre to the picker ray
|
||||||
|
|
||||||
CVector3D centre;
|
CVector3D closest = origin + dir * (selectionBox.m_Center - origin).Dot(dir);
|
||||||
bounds.GetCentre(centre);
|
float dist2 = (closest - selectionBox.m_Center).LengthSquared();
|
||||||
|
|
||||||
CVector3D closest = origin + dir * (centre - origin).Dot(dir);
|
|
||||||
float dist2 = (closest - centre).LengthSquared();
|
|
||||||
|
|
||||||
hits.push_back(std::make_pair(dist2, ent));
|
hits.push_back(std::make_pair(dist2, ent));
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,8 @@ enum
|
|||||||
ID_ViewerShadows,
|
ID_ViewerShadows,
|
||||||
ID_ViewerPolyCount,
|
ID_ViewerPolyCount,
|
||||||
ID_ViewerAnimation,
|
ID_ViewerAnimation,
|
||||||
|
ID_ViewerBoundingBox,
|
||||||
|
ID_ViewerAxesMarker,
|
||||||
ID_ViewerPlay,
|
ID_ViewerPlay,
|
||||||
ID_ViewerPause,
|
ID_ViewerPause,
|
||||||
ID_ViewerSlow
|
ID_ViewerSlow
|
||||||
@ -59,10 +61,15 @@ static wxWindow* Tooltipped(wxWindow* window, const wxString& tip)
|
|||||||
class ObjectBottomBar : public wxPanel
|
class ObjectBottomBar : public wxPanel
|
||||||
{
|
{
|
||||||
public:
|
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 OnFirstDisplay();
|
||||||
|
|
||||||
void ShowActorViewer(bool show);
|
void ShowActorViewer(bool show);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -75,11 +82,12 @@ private:
|
|||||||
bool m_ViewerGround;
|
bool m_ViewerGround;
|
||||||
bool m_ViewerShadows;
|
bool m_ViewerShadows;
|
||||||
bool m_ViewerPolyCount;
|
bool m_ViewerPolyCount;
|
||||||
|
bool m_ViewerBoundingBox;
|
||||||
|
bool m_ViewerAxesMarker;
|
||||||
|
|
||||||
wxPanel* m_ViewerPanel;
|
wxPanel* m_ViewerPanel;
|
||||||
|
|
||||||
ObjectSidebarImpl* p;
|
ObjectSidebarImpl* p;
|
||||||
|
|
||||||
ScenarioEditor& m_ScenarioEditor;
|
ScenarioEditor& m_ScenarioEditor;
|
||||||
|
|
||||||
DECLARE_EVENT_TABLE();
|
DECLARE_EVENT_TABLE();
|
||||||
@ -108,14 +116,27 @@ struct ObjectSidebarImpl
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ObjectSidebar::ObjectSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
|
ObjectSidebar::ObjectSidebar(
|
||||||
: Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer), p(new ObjectSidebarImpl())
|
ScenarioEditor& scenarioEditor,
|
||||||
|
wxWindow* sidebarContainer,
|
||||||
|
wxWindow* bottomBarContainer
|
||||||
|
)
|
||||||
|
: Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer),
|
||||||
|
p(new ObjectSidebarImpl())
|
||||||
{
|
{
|
||||||
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
|
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
sizer->Add(new wxStaticText(this, wxID_ANY, _("Filter")), wxSizerFlags().Align(wxALIGN_CENTER));
|
sizer->Add(new wxStaticText(this, wxID_ANY, _("Filter")), wxSizerFlags().Align(wxALIGN_CENTER));
|
||||||
sizer->Add(Tooltipped(new wxTextCtrl(this, ID_ObjectFilter),
|
sizer->Add(
|
||||||
_("Enter text to filter object list")), wxSizerFlags().Expand().Proportion(1));
|
Tooltipped(
|
||||||
|
new wxTextCtrl(this, ID_ObjectFilter),
|
||||||
|
_("Enter text to filter object list")
|
||||||
|
),
|
||||||
|
wxSizerFlags().Expand().Proportion(1)
|
||||||
|
);
|
||||||
m_MainSizer->Add(sizer, wxSizerFlags().Expand());
|
m_MainSizer->Add(sizer, wxSizerFlags().Expand());
|
||||||
|
m_MainSizer->AddSpacer(3);
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
wxArrayString strings;
|
wxArrayString strings;
|
||||||
strings.Add(_("Entities"));
|
strings.Add(_("Entities"));
|
||||||
@ -123,13 +144,27 @@ ObjectSidebar::ObjectSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarCo
|
|||||||
wxChoice* objectType = new wxChoice(this, ID_ObjectType, wxDefaultPosition, wxDefaultSize, strings);
|
wxChoice* objectType = new wxChoice(this, ID_ObjectType, wxDefaultPosition, wxDefaultSize, strings);
|
||||||
objectType->SetSelection(0);
|
objectType->SetSelection(0);
|
||||||
m_MainSizer->Add(objectType, wxSizerFlags().Expand());
|
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);
|
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->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_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);
|
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)
|
: wxPanel(parent, wxID_ANY), p(p), m_ScenarioEditor(scenarioEditor)
|
||||||
{
|
{
|
||||||
m_ViewerWireframe = false;
|
m_ViewerWireframe = false;
|
||||||
@ -320,21 +361,39 @@ ObjectBottomBar::ObjectBottomBar(wxWindow* parent, ScenarioEditor& scenarioEdito
|
|||||||
m_ViewerGround = true;
|
m_ViewerGround = true;
|
||||||
m_ViewerShadows = true;
|
m_ViewerShadows = true;
|
||||||
m_ViewerPolyCount = false;
|
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);
|
m_ViewerPanel = new wxPanel(this, wxID_ANY);
|
||||||
wxSizer* viewerSizer = new wxBoxSizer(wxHORIZONTAL);
|
wxSizer* viewerSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
wxSizer* viewerButtonsSizer = new wxStaticBoxSizer(wxVERTICAL, m_ViewerPanel, _("Display settings"));
|
wxSizer* viewerButtonsSizer = new wxStaticBoxSizer(wxHORIZONTAL, m_ViewerPanel, _("Display settings"));
|
||||||
viewerButtonsSizer->SetMinSize(140, -1);
|
{
|
||||||
viewerButtonsSizer->Add(Tooltipped(new wxButton(m_ViewerPanel, ID_ViewerWireframe, _("Wireframe")), _("Toggle wireframe / solid rendering")), wxSizerFlags().Expand());
|
wxSizer* viewerButtonsLeft = new wxBoxSizer(wxVERTICAL);
|
||||||
viewerButtonsSizer->Add(Tooltipped(new wxButton(m_ViewerPanel, ID_ViewerMove, _("Move")), _("Toggle movement along ground when playing walk/run animations")), wxSizerFlags().Expand());
|
viewerButtonsLeft->SetMinSize(110, -1);
|
||||||
viewerButtonsSizer->Add(Tooltipped(new wxButton(m_ViewerPanel, ID_ViewerGround, _("Ground")), _("Toggle the ground plane")), wxSizerFlags().Expand());
|
viewerButtonsLeft->Add(Tooltipped(new wxButton(m_ViewerPanel, ID_ViewerWireframe, _("Wireframe")), _("Toggle wireframe / solid rendering")), wxSizerFlags().Expand());
|
||||||
viewerButtonsSizer->Add(Tooltipped(new wxButton(m_ViewerPanel, ID_ViewerShadows, _("Shadows")), _("Toggle shadow rendering")), wxSizerFlags().Expand());
|
viewerButtonsLeft->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_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_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->Add(viewerButtonsSizer, wxSizerFlags().Expand());
|
||||||
|
viewerSizer->AddSpacer(3);
|
||||||
|
|
||||||
|
// --- animations panel -------------------------------------------------------------------------------
|
||||||
|
|
||||||
wxSizer* viewerAnimSizer = new wxStaticBoxSizer(wxVERTICAL, m_ViewerPanel, _("Animation"));
|
wxSizer* viewerAnimSizer = new wxStaticBoxSizer(wxVERTICAL, m_ViewerPanel, _("Animation"));
|
||||||
|
|
||||||
@ -357,17 +416,28 @@ ObjectBottomBar::ObjectBottomBar(wxWindow* parent, ScenarioEditor& scenarioEdito
|
|||||||
|
|
||||||
viewerSizer->Add(viewerAnimSizer, wxSizerFlags().Expand());
|
viewerSizer->Add(viewerAnimSizer, wxSizerFlags().Expand());
|
||||||
|
|
||||||
m_ViewerPanel->SetSizer(viewerSizer);
|
// --- add viewer-specific options -------------------------------------------------------------------------------
|
||||||
sizer->Add(m_ViewerPanel, wxSizerFlags().Expand());
|
|
||||||
|
|
||||||
|
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);
|
m_ViewerPanel->Show(false);
|
||||||
|
|
||||||
|
// --- add player/variation selection -------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
wxSizer* playerSelectionSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
wxSizer* playerVariationSizer = new wxBoxSizer(wxVERTICAL);
|
wxSizer* playerVariationSizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
// TODO: make this a wxChoice instead
|
// TODO: make this a wxChoice instead
|
||||||
wxComboBox* playerSelect = new PlayerComboBox(this, objectSettings, mapSettings);
|
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);
|
wxWindow* variationSelect = new VariationControl(this, objectSettings);
|
||||||
variationSelect->SetMinSize(wxSize(160, -1));
|
variationSelect->SetMinSize(wxSize(160, -1));
|
||||||
@ -375,9 +445,12 @@ ObjectBottomBar::ObjectBottomBar(wxWindow* parent, ScenarioEditor& scenarioEdito
|
|||||||
variationSizer->Add(variationSelect, wxSizerFlags().Proportion(1).Expand());
|
variationSizer->Add(variationSelect, wxSizerFlags().Proportion(1).Expand());
|
||||||
playerVariationSizer->Add(variationSizer, wxSizerFlags().Proportion(1));
|
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()
|
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"ground", m_ViewerGround));
|
||||||
POST_MESSAGE(SetViewParamB, (AtlasMessage::eRenderView::ACTOR, L"shadows", m_ViewerShadows));
|
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"stats", m_ViewerPolyCount));
|
||||||
|
POST_MESSAGE(SetViewParamB, (AtlasMessage::eRenderView::ACTOR, L"bounding_box", m_ViewerBoundingBox));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectBottomBar::ShowActorViewer(bool show)
|
void ObjectBottomBar::ShowActorViewer(bool show)
|
||||||
@ -434,6 +508,14 @@ void ObjectBottomBar::OnViewerSetting(wxCommandEvent& evt)
|
|||||||
m_ViewerPolyCount = !m_ViewerPolyCount;
|
m_ViewerPolyCount = !m_ViewerPolyCount;
|
||||||
POST_MESSAGE(SetViewParamB, (AtlasMessage::eRenderView::ACTOR, L"stats", m_ViewerPolyCount));
|
POST_MESSAGE(SetViewParamB, (AtlasMessage::eRenderView::ACTOR, L"stats", m_ViewerPolyCount));
|
||||||
break;
|
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_ViewerPlay, ObjectBottomBar::OnSpeed)
|
||||||
EVT_BUTTON(ID_ViewerPause, ObjectBottomBar::OnSpeed)
|
EVT_BUTTON(ID_ViewerPause, ObjectBottomBar::OnSpeed)
|
||||||
EVT_BUTTON(ID_ViewerSlow, ObjectBottomBar::OnSpeed)
|
EVT_BUTTON(ID_ViewerSlow, ObjectBottomBar::OnSpeed)
|
||||||
|
EVT_BUTTON(ID_ViewerBoundingBox, ObjectBottomBar::OnViewerSetting)
|
||||||
|
EVT_BUTTON(ID_ViewerAxesMarker, ObjectBottomBar::OnViewerSetting)
|
||||||
END_EVENT_TABLE();
|
END_EVENT_TABLE();
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include "graphics/TerrainTextureManager.h"
|
#include "graphics/TerrainTextureManager.h"
|
||||||
#include "graphics/TerritoryTexture.h"
|
#include "graphics/TerritoryTexture.h"
|
||||||
#include "graphics/UnitManager.h"
|
#include "graphics/UnitManager.h"
|
||||||
|
#include "graphics/Overlay.h"
|
||||||
#include "maths/MathUtil.h"
|
#include "maths/MathUtil.h"
|
||||||
#include "ps/Font.h"
|
#include "ps/Font.h"
|
||||||
#include "ps/GameSetup/Config.h"
|
#include "ps/GameSetup/Config.h"
|
||||||
@ -48,6 +49,7 @@
|
|||||||
#include "simulation2/components/ICmpTerrain.h"
|
#include "simulation2/components/ICmpTerrain.h"
|
||||||
#include "simulation2/components/ICmpUnitMotion.h"
|
#include "simulation2/components/ICmpUnitMotion.h"
|
||||||
#include "simulation2/components/ICmpVisual.h"
|
#include "simulation2/components/ICmpVisual.h"
|
||||||
|
#include "simulation2/helpers/Render.h"
|
||||||
|
|
||||||
struct ActorViewerImpl : public Scene
|
struct ActorViewerImpl : public Scene
|
||||||
{
|
{
|
||||||
@ -75,6 +77,8 @@ public:
|
|||||||
bool WalkEnabled;
|
bool WalkEnabled;
|
||||||
bool GroundEnabled;
|
bool GroundEnabled;
|
||||||
bool ShadowsEnabled;
|
bool ShadowsEnabled;
|
||||||
|
bool SelectionBoxEnabled;
|
||||||
|
bool AxesMarkerEnabled;
|
||||||
|
|
||||||
SColor4ub Background;
|
SColor4ub Background;
|
||||||
|
|
||||||
@ -89,6 +93,9 @@ public:
|
|||||||
CLOSTexture LOSTexture;
|
CLOSTexture LOSTexture;
|
||||||
CTerritoryTexture TerritoryTexture;
|
CTerritoryTexture TerritoryTexture;
|
||||||
|
|
||||||
|
SOverlayLine SelectionBoxOverlay;
|
||||||
|
SOverlayLine AxesMarkerOverlays[3];
|
||||||
|
|
||||||
// Simplistic implementation of the Scene interface
|
// Simplistic implementation of the Scene interface
|
||||||
virtual void EnumerateObjects(const CFrustum& frustum, SceneCollector* c)
|
virtual void EnumerateObjects(const CFrustum& frustum, SceneCollector* c)
|
||||||
{
|
{
|
||||||
@ -99,6 +106,48 @@ public:
|
|||||||
c->Submit(Terrain.GetPatch(pi, pj));
|
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);
|
Simulation2.RenderSubmit(*c, frustum, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,6 +168,8 @@ ActorViewer::ActorViewer()
|
|||||||
m.WalkEnabled = false;
|
m.WalkEnabled = false;
|
||||||
m.GroundEnabled = true;
|
m.GroundEnabled = true;
|
||||||
m.ShadowsEnabled = g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWS);
|
m.ShadowsEnabled = g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWS);
|
||||||
|
m.SelectionBoxEnabled = false;
|
||||||
|
m.AxesMarkerEnabled = false;
|
||||||
m.Background = SColor4ub(0, 0, 0, 255);
|
m.Background = SColor4ub(0, 0, 0, 255);
|
||||||
|
|
||||||
// Create a tiny empty piece of terrain, just so we can put shadows
|
// 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::SetWalkEnabled(bool enabled) { m.WalkEnabled = enabled; }
|
||||||
void ActorViewer::SetGroundEnabled(bool enabled) { m.GroundEnabled = enabled; }
|
void ActorViewer::SetGroundEnabled(bool enabled) { m.GroundEnabled = enabled; }
|
||||||
void ActorViewer::SetShadowsEnabled(bool enabled) { m.ShadowsEnabled = 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)
|
void ActorViewer::SetStatsEnabled(bool enabled)
|
||||||
{
|
{
|
||||||
|
@ -41,6 +41,8 @@ public:
|
|||||||
void SetGroundEnabled(bool enabled);
|
void SetGroundEnabled(bool enabled);
|
||||||
void SetShadowsEnabled(bool enabled);
|
void SetShadowsEnabled(bool enabled);
|
||||||
void SetStatsEnabled(bool enabled);
|
void SetStatsEnabled(bool enabled);
|
||||||
|
void SetBoundingBoxesEnabled(bool enabled);
|
||||||
|
void SetAxesMarkerEnabled(bool enabled);
|
||||||
void Render();
|
void Render();
|
||||||
void Update(float dt);
|
void Update(float dt);
|
||||||
|
|
||||||
|
@ -132,6 +132,10 @@ void ViewActor::SetParam(const std::wstring& name, bool value)
|
|||||||
m_ActorViewer->SetShadowsEnabled(value);
|
m_ActorViewer->SetShadowsEnabled(value);
|
||||||
else if (name == L"stats")
|
else if (name == L"stats")
|
||||||
m_ActorViewer->SetStatsEnabled(value);
|
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)
|
void ViewActor::SetParam(const std::wstring& name, const AtlasMessage::Colour& value)
|
||||||
|
Loading…
Reference in New Issue
Block a user