Ticket #914: selection_bbox_26sep11.patch

File selection_bbox_26sep11.patch, 44.2 KB (added by vts, 13 years ago)
  • binaries/data/mods/public/gui/session/session.xml

    diff --git a/binaries/data/mods/public/gui/session/session.xml b/binaries/data/mods/public/gui/session/session.xml
    index d7cb414..1534c2a 100644
    a b  
    8989        />
    9090
    9191        <!-- Dev/cheat commands -->
    92         <object name="devCommands" size="100%-156 50%-80 100%-8 50%+80" type="image" sprite="devCommandsBackground"
     92        <object name="devCommands" size="100%-156 50%-88 100%-8 50%+88" type="image" sprite="devCommandsBackground"
    9393                hidden="true" hotkey="session.devcommands.toggle">
    9494            <action on="Press">
    9595                toggleDeveloperOverlay();
     
    126126                <action on="Press">Engine.GuiInterfaceCall("SetRangeDebugOverlay", this.checked);</action>
    127127            </object>
    128128
    129             <object size="0 96 100%-18 112" type="text" style="devCommandsText">Restrict camera</object>
    130             <object size="100%-16 96 100% 112" type="checkbox" style="StoneCrossBox" checked="true">
     129            <object size="0 96 100%-18 112" type="text" style="devCommandsText">Bounding box overlay</object>
     130            <object size="100%-16 96 100% 112" type="checkbox" style="StoneCrossBox">
     131                <action on="Press">Engine.SetBoundingBoxDebugOverlay(this.checked);</action>
     132            </object>
     133           
     134            <object size="0 112 100%-18 128" type="text" style="devCommandsText">Restrict camera</object>
     135            <object size="100%-16 112 100% 128" type="checkbox" style="StoneCrossBox" checked="true">
    131136                <action on="Press">gameView.constrainCamera = this.checked;</action>
    132137            </object>
    133138
    134             <object size="0 112 100%-18 128" type="text" style="devCommandsText">Reveal map</object>
    135             <object name="devCommandsRevealMap" size="100%-16 112 100% 128" type="checkbox" style="StoneCrossBox">
     139            <object size="0 128 100%-18 144" type="text" style="devCommandsText">Reveal map</object>
     140            <object size="100%-16 128 100% 144" type="checkbox" name="devCommandsRevealMap" style="StoneCrossBox">
    136141                <action on="Press">Engine.PostNetworkCommand({"type": "reveal-map", "enable": this.checked});</action>
    137142            </object>
    138143
    139             <object size="0 128 100%-18 144" type="text" style="devCommandsText">Enable time warp</object>
    140             <object size="100%-16 128 100% 144" type="checkbox" name="devTimeWarp" style="StoneCrossBox">
     144            <object size="0 144 100%-18 160" type="text" style="devCommandsText">Enable time warp</object>
     145            <object size="100%-16 144 100% 160" type="checkbox" name="devTimeWarp" style="StoneCrossBox">
    141146                <action on="Press">Engine.EnableTimeWarpRecording(this.checked ? 10 : 0);</action>
    142147            </object>
    143148
    144             <object size="0 144 100%-18 160" type="text" style="devCommandsText">Promote selected units</object>
    145             <object size="100%-16 144 100% 160" type="button" style="StoneCrossBox">
     149            <object size="0 160 100%-18 176" type="text" style="devCommandsText">Promote selected units</object>
     150            <object size="100%-16 160 100% 176" type="button" style="StoneCrossBox">
    146151                <action on="Press">Engine.PostNetworkCommand({"type": "promote", "entities": g_Selection.toList()});</action>
    147152            </object>
    148153        </object>
  • source/graphics/GameView.cpp

    diff --git a/source/graphics/GameView.cpp b/source/graphics/GameView.cpp
    index 7e17a4a..4e8205c 100644
    a b void CGameView::EnumerateObjects(const CFrustum& frustum, SceneCollector* c)  
    493493            CPatch* patch=pTerrain->GetPatch(i,j);  // can't fail
    494494
    495495            // If the patch is underwater, calculate a bounding box that also contains the water plane
    496             CBound bounds = patch->GetBounds();
     496            CBound bounds = patch->GetWorldBounds();
    497497            float waterHeight = g_Renderer.GetWaterManager()->m_WaterHeight + 0.001f;
    498498            if(bounds[1].Y < waterHeight) {
    499499                bounds[1].Y = waterHeight;
  • source/graphics/Model.cpp

    diff --git a/source/graphics/Model.cpp b/source/graphics/Model.cpp
    index 704e2b2..0ac99be 100644
    a b void CModel::CalcBounds()  
    116116    if (! (m_Anim && m_Anim->m_AnimDef))
    117117    {
    118118        if (m_ObjectBounds.IsEmpty())
    119             CalcObjectBounds();
     119            CalcStaticObjectBounds();
    120120    }
    121121    else
    122122    {
    123123        if (m_Anim->m_ObjectBounds.IsEmpty())
    124             CalcAnimatedObjectBound(m_Anim->m_AnimDef, m_Anim->m_ObjectBounds);
     124            CalcAnimatedObjectBounds(m_Anim->m_AnimDef, m_Anim->m_ObjectBounds);
    125125        ENSURE(! m_Anim->m_ObjectBounds.IsEmpty()); // (if this happens, it'll be recalculating the bounds every time)
    126126        m_ObjectBounds = m_Anim->m_ObjectBounds;
    127127    }
    void CModel::CalcBounds()  
    129129    // Ensure the transform is set correctly before we use it
    130130    ValidatePosition();
    131131
     132    // Now transform the object-space bounds to world-space bounds
    132133    m_ObjectBounds.Transform(GetTransform(), m_Bounds);
    133134}
    134135
    135136/////////////////////////////////////////////////////////////////////////////////////////////////////////////
    136137// CalcObjectBounds: calculate object space bounds of this model, based solely on vertex positions
    137 void CModel::CalcObjectBounds()
     138void CModel::CalcStaticObjectBounds()
    138139{
    139140    m_ObjectBounds.SetEmpty();
    140141
    141     size_t numverts=m_pModelDef->GetNumVertices();
    142     SModelVertex* verts=m_pModelDef->GetVertices();
     142    size_t numVerts = m_pModelDef->GetNumVertices();
     143    SModelVertex* verts = m_pModelDef->GetVertices();
    143144
    144     for (size_t i=0;i<numverts;i++) {
    145         m_ObjectBounds+=verts[i].m_Coords;
     145    for (size_t i = 0; i < numVerts; i++) {
     146        m_ObjectBounds += verts[i].m_Coords;
    146147    }
    147148}
    148149
    149150/////////////////////////////////////////////////////////////////////////////////////////////////////////////
    150151// CalcAnimatedObjectBound: calculate bounds encompassing all vertex positions for given animation
    151 void CModel::CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result)
     152void CModel::CalcAnimatedObjectBounds(CSkeletonAnimDef* anim, CBound& result)
    152153{
    153154    result.SetEmpty();
    154155
    155156    // Set the current animation on which to perform calculations (if it's necessary)
    156157    if (anim != m_Anim->m_AnimDef)
    157158    {
    158         CSkeletonAnim dummyanim;
    159         dummyanim.m_AnimDef=anim;
    160         if (!SetAnimation(&dummyanim)) return;
     159        CSkeletonAnim dummyAnim;
     160        dummyAnim.m_AnimDef = anim;
     161        if (!SetAnimation(&dummyAnim)) return;
    161162    }
    162163
    163     size_t numverts=m_pModelDef->GetNumVertices();
    164     SModelVertex* verts=m_pModelDef->GetVertices();
     164    size_t numVertices = m_pModelDef->GetNumVertices();
     165    SModelVertex* vertices = m_pModelDef->GetVertices();
    165166
    166167    // Remove any transformations, so that we calculate the bounding box
    167168    // at the origin. The box is later re-transformed onto the object, without
    168169    // having to recalculate the size of the box.
    169     CMatrix3D transform, oldtransform = GetTransform();
    170     CModelAbstract* oldparent = m_Parent;
     170    CMatrix3D transform, oldTransform = GetTransform();
     171    CModelAbstract* oldParent = m_Parent;
    171172   
    172173    m_Parent = 0;
    173174    transform.SetIdentity();
    void CModel::CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result)  
    179180    float AnimTime = m_AnimTime;
    180181
    181182    // iterate through every frame of the animation
    182     for (size_t j=0;j<anim->GetNumFrames();j++) {
     183    for (size_t j = 0; j < anim->GetNumFrames(); j++)
     184    {
    183185        m_PositionValid = false;
    184186        ValidatePosition();
    185187
    186188        // extend bounds by vertex positions at the frame
    187         for (size_t i=0;i<numverts;i++)
     189        for (size_t i = 0; i < numVertices; i++)
    188190        {
    189             result += CModelDef::SkinPoint(verts[i], GetAnimatedBoneMatrices());
     191            result += CModelDef::SkinPoint(vertices[i], GetAnimatedBoneMatrices());
    190192        }
    191193        // advance to next frame
    192194        m_AnimTime += anim->GetFrameTime();
    193195    }
    194196
    195197    m_PositionValid = false;
    196     m_Parent = oldparent;
    197     SetTransform(oldtransform);
     198    m_Parent = oldParent;
     199    SetTransform(oldTransform);
    198200    m_AnimTime = AnimTime;
    199201}
    200202
    201203/////////////////////////////////////////////////////////////////////////////////////////////////////////////
    202 const CBound CModel::GetBoundsRec()
     204const CBound CModel::GetWorldBoundsRec()
    203205{
    204     CBound bounds = GetBounds();
     206    CBound bounds = GetWorldBounds();
    205207    for (size_t i = 0; i < m_Props.size(); ++i)
    206         bounds += m_Props[i].m_Model->GetBoundsRec();
     208        bounds += m_Props[i].m_Model->GetWorldBoundsRec();
    207209    return bounds;
    208210}
    209211
     212const CBound CModel::GetObjectSelectionBoundsRec()
     213{
     214    CBound objBounds = GetObjectBounds();       // updates the (children-not-included) object-space bounds if necessary
     215
     216    // now extend these bounds to include the submodels' selection bounds (if any)
     217    for (size_t i = 0; i < m_Props.size(); ++i)
     218    {
     219        CBound propSelectionBounds = m_Props[i].m_Model->GetObjectSelectionBoundsRec();
     220        if (propSelectionBounds.IsEmpty())
     221            continue;   // submodel does not wish to participate in selection box, exclude it
     222
     223        // Translate the prop's selection bounds to the prop position within this model
     224        CBound relativePropSelectionBounds(
     225            m_Props[i].m_Point->m_Position + propSelectionBounds[0],
     226            m_Props[i].m_Point->m_Position + propSelectionBounds[1]
     227        );
     228        objBounds += relativePropSelectionBounds;
     229    }
     230
     231    return objBounds;
     232}
     233
    210234/////////////////////////////////////////////////////////////////////////////////////////////////////////////
    211235// BuildAnimation: load raw animation frame animation from given file, and build a
    212236// animation specific to this model
  • source/graphics/Model.h

    diff --git a/source/graphics/Model.h b/source/graphics/Model.h
    index 84efeb6..9f43ba2 100644
    a b public:  
    7575
    7676    // setup model from given geometry
    7777    bool InitModel(const CModelDefPtr& modeldef);
    78     // calculate the world space bounds of this model
    79     virtual void CalcBounds();
    8078    // update this model's state; 'time' is the absolute time since the start of the animation, in MS
    8179    void UpdateTo(float time);
    8280
    public:  
    133131            m_Props[i].m_Model->SetEntityVariable(name, value);
    134132    }
    135133
    136     // calculate object space bounds of this model, based solely on vertex positions
    137     void CalcObjectBounds();
    138     // calculate bounds encompassing all vertex positions for given animation
    139     void CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result);
     134    // --- WORLD/OBJECT SPACE BOUNDS -----------------------------------------------------------------
     135
     136    /// Overridden to calculate both the world-space and object-space bounds of this model, and stores the result in
     137    /// m_Bounds and m_ObjectBounds, respectively.
     138    virtual void CalcBounds();
     139
     140    /// Returns the object-space bounds for this model, excluding its children.
     141    const CBound& GetObjectBounds()
     142    {
     143        RecalculateBoundsIfNecessary();             // recalculates both object-space and world-space bounds if necessary
     144        return m_ObjectBounds;
     145    }
     146
     147    virtual const CBound GetWorldBoundsRec();       // reimplemented here
    140148
    141     virtual const CBound GetBoundsRec();
     149    /// Auxiliary method; calculates object space bounds of this model, based solely on vertex positions, and stores
     150    /// the result in m_ObjectBounds. Called by CalcBounds (instead of CalcAnimatedObjectBounds) if it has been determined
     151    /// that the object-space bounds are static.
     152    void CalcStaticObjectBounds();
     153    /// Auxiliary method; calculate object-space bounds encompassing all vertex positions for given animation, and stores
     154    /// the result in m_ObjectBounds. Called by CalcBounds (instead of CalcStaticBounds) if it has been determined that the
     155    /// object-space bounds need to take animations into account.
     156    void CalcAnimatedObjectBounds(CSkeletonAnimDef* anim,CBound& result);
     157
     158    // --- SELECTION BOX/BOUNDS ----------------------------------------------------------------------
     159
     160    /// Reimplemented here since proper models should participate in selection boxes.
     161    virtual const CBound GetObjectSelectionBoundsRec();
    142162
    143163    /**
    144164     * Set transform of this object.
    private:  
    247267    // list of current props on model
    248268    std::vector<Prop> m_Props;
    249269
    250     /**
    251      * The prop point to which the ammo prop is attached, or NULL if none
    252      */
     270    /// The prop point to which the ammo prop is attached, or NULL if none
    253271    const SPropPoint* m_AmmoPropPoint;
    254272
    255     /**
    256      * If m_AmmoPropPoint is not NULL, then the index in m_Props of the ammo prop
    257      */
     273    /// If m_AmmoPropPoint is not NULL, then the index in m_Props of the ammo prop
    258274    size_t m_AmmoLoadedProp;
    259275
    260     // manager object which can load animations for us
     276    /// manager object which can load animations for us
    261277    CSkeletonAnimManager& m_SkeletonAnimManager;
    262278};
    263279
  • new file source/graphics/ModelAbstract.cpp

    diff --git a/source/graphics/ModelAbstract.cpp b/source/graphics/ModelAbstract.cpp
    new file mode 100644
    index 0000000..0953a88
    - +  
     1/* Copyright (C) 2011 Wildfire Games.
     2 * This file is part of 0 A.D.
     3 *
     4 * 0 A.D. is free software: you can redistribute it and/or modify
     5 * it under the terms of the GNU General Public License as published by
     6 * the Free Software Foundation, either version 2 of the License, or
     7 * (at your option) any later version.
     8 *
     9 * 0 A.D. is distributed in the hope that it will be useful,
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 * GNU General Public License for more details.
     13 *
     14 * You should have received a copy of the GNU General Public License
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
     16 */
     17
     18#include "precompiled.h"
     19
     20#include "ModelAbstract.h"
     21
     22const CBox& CModelAbstract::GetSelectionBox()
     23{
     24    if (!m_SelectionBoxValid)
     25    {
     26        CalcSelectionBox();
     27        m_SelectionBoxValid = true;
     28    }
     29    return m_SelectionBox;
     30}
     31
     32void CModelAbstract::CalcSelectionBox()
     33{
     34    // Get the object-space bounds that should be used to construct this model (and its children)'s selection box
     35    CBound objBounds = GetObjectSelectionBoundsRec();
     36    if (objBounds.IsEmpty())
     37    {
     38        m_SelectionBox.SetEmpty(); // model does not wish to participate in selection
     39        return;
     40    }
     41
     42    /// Prevent the bounding box from extending through the terrain; clip the lower plane at Y=0 in object space.
     43    if (objBounds[1].Y > 0.f) // should always be the case, unless the models are defined really weirdly
     44        objBounds[0].Y = std::max(0.f, objBounds[0].Y);
     45        //objBounds[0].Y = 0;
     46
     47    // Take the box from object space, and transform it into a box in world space (i.e. an OBB) instead of a bound (i.e. an AABB).
     48    CVector3D objCenter;
     49    objBounds.GetCentre(objCenter);
     50
     51    const CMatrix3D& transform = GetTransform();
     52
     53    // transform the object-space axis unit vectors to world space. Since this is basically multiplying the transformation matrix
     54    // by the unity matrix, the result is the columns of the transformation matrix. We're not interested in translations for this,
     55    // so just take the linear transform columns of the 4D transformations matrix.
     56    CVector3D worldU(transform._11, transform._21, transform._31);
     57    CVector3D worldV(transform._12, transform._22, transform._32);
     58    CVector3D worldW(transform._13, transform._23, transform._33);
     59   
     60    // after normalization, these should be mutually orthonormal (but e.g. during animations, they aren't always).
     61    worldU.Normalize();
     62    worldV.Normalize();
     63    worldW.Normalize();
     64
     65    m_SelectionBox.m_Center = objCenter + transform.GetTranslation(); // translated center position
     66    m_SelectionBox.m_Basis[0] = worldU;
     67    m_SelectionBox.m_Basis[1] = worldV;
     68    m_SelectionBox.m_Basis[2] = worldW;
     69    m_SelectionBox.m_HalfSizes = (objBounds[1] - objBounds[0]) * 0.5f; // element-wise subtraction and division by 2
     70
     71    ENSURE(   m_SelectionBox.m_HalfSizes[0] >= 0.f
     72           && m_SelectionBox.m_HalfSizes[1] >= 0.f
     73           && m_SelectionBox.m_HalfSizes[2] >= 0.f); // must all be positive
     74}
     75 No newline at end of file
  • source/graphics/ModelAbstract.h

    diff --git a/source/graphics/ModelAbstract.h b/source/graphics/ModelAbstract.h
    index 2c01968..9530a4a 100644
    a b  
    1818#ifndef INCLUDED_MODELABSTRACT
    1919#define INCLUDED_MODELABSTRACT
    2020
     21#include "maths/Box.h"
    2122#include "graphics/RenderableObject.h"
    2223#include "ps/Overlay.h"
    2324#include "simulation2/helpers/Player.h"
    class CModelAbstract : public CRenderableObject  
    3940public:
    4041    CModelAbstract() :
    4142        m_Parent(NULL), m_PositionValid(false),
    42         m_ShadingColor(1, 1, 1, 1), m_PlayerID(INVALID_PLAYER)
     43        m_ShadingColor(1, 1, 1, 1), m_PlayerID(INVALID_PLAYER), m_SelectionBoxValid(false)
    4344    {
     45        m_SelectionBox.SetEmpty();
    4446    }
    4547
    4648    virtual CModelAbstract* Clone() const = 0;
    public:  
    5860    // and this seems the easiest way to integrate with other code that wants
    5961    // type-specific processing)
    6062
    61     /**
    62      * Calls SetDirty on this model and all child objects.
    63      */
     63    /// Calls SetDirty on this model and all child objects.
    6464    virtual void SetDirtyRec(int dirtyflags) = 0;
    6565
     66    /// Returns world space bounds of this object and all child objects.
     67    virtual const CBound GetWorldBoundsRec() { return GetWorldBounds(); }
     68
    6669    /**
    67      * Returns world space bounds of this object and all child objects.
     70     * Returns the world-space selection box of this model. Used for selection ray hittesting. The returned selection box may be empty to indicate
     71     * that it does not wish to participate in the selection process.
    6872     */
    69     virtual const CBound GetBoundsRec() { return GetBounds(); }
     73    virtual const CBox& GetSelectionBox();
     74
     75    void CalcSelectionBox();
     76
     77    virtual void InvalidateBounds() {
     78        m_BoundsValid = false;
     79        m_SelectionBoxValid = false;        // the selection boxes depend on the bounds, so we need to also invalidate them here
     80    }
     81
     82    /// Returns the (object-space) bounds that should be used to construct a selection box for this model and its children.
     83    /// May return an empty bound to indicate that this model and its children should not be selectable themselves, or should
     84    /// not be included in its parent model's selection box.
     85    /// If you wish your model type to be included in selection boxes, override this method and have it return the object-space
     86    /// bounds of itself, augmented recursively (via this method) with the selection bounds from its children (if any).
     87    virtual const CBound GetObjectSelectionBoundsRec() { return CBound::EMPTY; }
    7088
    7189    /**
    7290     * Called when terrain has changed in the given inclusive bounds.
    public:  
    100118    virtual void SetShadingColor(const CColor& colour) { m_ShadingColor = colour; }
    101119    virtual CColor GetShadingColor() const { return m_ShadingColor; }
    102120
    103     /// If non-null points to the model that we are attached to.
     121
     122public:
     123    /// If non-null, points to the model that we are attached to.
    104124    CModelAbstract* m_Parent;
    105125
    106126    /// True if both transform and and bone matrices are valid.
    public:  
    110130
    111131    // modulating color
    112132    CColor m_ShadingColor;
     133
     134protected:
     135    /// Selection box for this model.
     136    CBox m_SelectionBox;
     137    /// Is the current selection box valid?
     138    bool m_SelectionBoxValid;
     139
    113140};
    114141
    115142#endif // INCLUDED_MODELABSTRACT
  • source/graphics/ObjectEntry.cpp

    diff --git a/source/graphics/ObjectEntry.cpp b/source/graphics/ObjectEntry.cpp
    index 25054ab..069ea31 100644
    a b bool CObjectEntry::BuildVariation(const std::vector<std::set<CStr> >& selections  
    129129    model->SetTexture(texture);
    130130
    131131    // calculate initial object space bounds, based on vertex positions
    132     model->CalcObjectBounds();
     132    model->CalcStaticObjectBounds();
    133133
    134134    // load the animations
    135135    for (std::multimap<CStr, CObjectBase::Anim>::iterator it = variation.anims.begin(); it != variation.anims.end(); ++it)
  • source/graphics/RenderableObject.h

    diff --git a/source/graphics/RenderableObject.h b/source/graphics/RenderableObject.h
    index 1b642f8..2fa3667 100644
    a b public:  
    6868        if (m_Transform == transform)
    6969            return;
    7070        // store transform, calculate inverse
    71         m_Transform=transform;
     71        m_Transform = transform;
    7272        m_Transform.GetInverse(m_InvTransform);
    7373        // normal recalculation likely required on transform change; flag it
    7474        SetDirty(RENDERDATA_UPDATE_VERTICES);
    public:  
    8686        if (m_RenderData) m_RenderData->m_UpdateFlags|=dirtyflags;
    8787    }
    8888
    89     // calculate (and store in m_Bounds) the world space bounds of this object
    90     // - must be implemented by all concrete subclasses
     89    /// Calculate (and store) any bounds or bound-dependent data for this object. At this level, this is only the world-space bounds stored
     90    /// in m_Bounds; subclasses may use this method to compute additional bounds if necessary (or any data that depends on the bounds).
     91    /// Whenever bound-dependent data is requested through a public interface, RecalculateBoundsIfNecessary should be called to ensure
     92    /// bound correctness, which will in turn call this method.
    9193    virtual void CalcBounds() = 0;
    9294
    9395    // return world space bounds of this object
    94     const CBound& GetBounds() {
    95         if (! m_BoundsValid) {
    96             CalcBounds();
    97             m_BoundsValid = true;
    98         }
     96    const CBound& GetWorldBounds() {
     97        RecalculateBoundsIfNecessary();
    9998        return m_Bounds;
    10099    }
    101100
    102     void InvalidateBounds() { m_BoundsValid = false; }
     101    virtual void InvalidateBounds() { m_BoundsValid = false; }
    103102
    104103    // Set the object renderdata and free previous renderdata, if any.
    105104    void SetRenderData(CRenderData* renderdata) {
    public:  
    112111    CRenderData* GetRenderData() { return m_RenderData; }
    113112
    114113protected:
     114    /// Factored out so subclasses don't need to repeat this if they want to add additional getters for bounds-related methods
     115    /// (since they'll have to make sure to recalc the bounds if necessary before they return it).
     116    void RecalculateBoundsIfNecessary()
     117    {
     118        if (!m_BoundsValid) {
     119            CalcBounds();
     120            m_BoundsValid = true;
     121        }
     122    }
     123
     124protected:
    115125    // object bounds
    116126    CBound m_Bounds;
    117127    // local->world space transform
    protected:  
    120130    CMatrix3D m_InvTransform;
    121131    // object renderdata
    122132    CRenderData* m_RenderData;
    123 
    124 private:
    125133    // remembers whether m_bounds needs to be recalculated
    126134    bool m_BoundsValid;
    127135};
  • source/graphics/UnitManager.cpp

    diff --git a/source/graphics/UnitManager.cpp b/source/graphics/UnitManager.cpp
    index d8604fe..0e3d7d1 100644
    a b CUnit* CUnitManager::PickUnit(const CVector3D& origin, const CVector3D& dir) con  
    100100        CUnit* unit = m_Units[i];
    101101        float tmin, tmax;
    102102       
    103         if (unit->GetModel().GetBounds().RayIntersect(origin, dir, tmin, tmax))
     103        if (unit->GetModel().GetWorldBounds().RayIntersect(origin, dir, tmin, tmax))
    104104        {
    105105            // Point of closest approach
    106106            CVector3D obj;
    107             unit->GetModel().GetBounds().GetCentre(obj);
     107            unit->GetModel().GetWorldBounds().GetCentre(obj);
    108108            CVector3D delta = obj - origin;
    109109            float distance = delta.Dot(dir);
    110110            CVector3D closest = origin + dir * distance;
  • source/gui/scripting/ScriptFunctions.cpp

    diff --git a/source/gui/scripting/ScriptFunctions.cpp b/source/gui/scripting/ScriptFunctions.cpp
    index 0054246..d64fae2 100644
    a b  
    4848#include "simulation2/components/ICmpGuiInterface.h"
    4949#include "simulation2/components/ICmpRangeManager.h"
    5050#include "simulation2/components/ICmpTemplateManager.h"
     51#include "simulation2/components/ICmpSelectable.h"
    5152#include "simulation2/helpers/Selection.h"
    5253
    5354#include "js/jsapi.h"
    void RewindTimeWarp(void* UNUSED(cbdata))  
    456457    g_Game->GetTurnManager()->RewindTimeWarp();
    457458}
    458459
     460void SetBoundingBoxDebugOverlay(void* UNUSED(cbdata), bool enabled)
     461{
     462    ICmpSelectable::s_EnableDebugOverlays = enabled;
     463}
     464
    459465} // namespace
    460466
    461467void GuiScriptingInit(ScriptInterface& scriptInterface)
    void GuiScriptingInit(ScriptInterface& scriptInterface)  
    520526    scriptInterface.RegisterFunction<void, &DumpSimState>("DumpSimState");
    521527    scriptInterface.RegisterFunction<void, unsigned int, &EnableTimeWarpRecording>("EnableTimeWarpRecording");
    522528    scriptInterface.RegisterFunction<void, &RewindTimeWarp>("RewindTimeWarp");
     529    scriptInterface.RegisterFunction<void, bool, &SetBoundingBoxDebugOverlay>("SetBoundingBoxDebugOverlay");
    523530}
  • source/maths/Bound.cpp

    diff --git a/source/maths/Bound.cpp b/source/maths/Bound.cpp
    index bf6d798..4213a63 100644
    a b  
    3232#include "maths/Matrix3D.h"
    3333
    3434
     35const CBound CBound::EMPTY = CBound(); // initializes to an empty bound
     36
    3537///////////////////////////////////////////////////////////////////////////////
    3638// RayIntersect: intersect ray with this bound; return true
    3739// if ray hits (and store entry and exit times), or false
  • source/maths/Bound.h

    diff --git a/source/maths/Bound.h b/source/maths/Bound.h
    index 7a22eee..ee0bf81 100644
    a b  
    1616 */
    1717
    1818/*
    19  * Axis-aligned bounding box
     19 * Axis-aligned bounding box (AABB)
    2020 */
    2121
    2222#ifndef INCLUDED_BOUND
    class CFrustum;  
    2929class CMatrix3D;
    3030
    3131///////////////////////////////////////////////////////////////////////////////
    32 // CBound: basic axis aligned bounding box class
     32// CBound: basic axis aligned bounding box class (AABB)
    3333class CBound
    3434{
    3535public:
     36   
    3637    CBound() { SetEmpty(); }
    37     CBound(const CVector3D& min,const CVector3D& max) {
    38         m_Data[0]=min; m_Data[1]=max;
     38    CBound(const CVector3D& min, const CVector3D& max) {
     39        m_Data[0] = min;
     40        m_Data[1] = max;
    3941    }
    4042
    41     void Transform(const CMatrix3D& m,CBound& result) const;
     43    void Transform(const CMatrix3D& m, CBound& result) const;
    4244
    4345    CVector3D& operator[](int index) {  return m_Data[index]; }
    4446    const CVector3D& operator[](int index) const { return m_Data[index]; }
    public:  
    7072        return *this;
    7173    }
    7274
    73     bool RayIntersect(const CVector3D& origin,const CVector3D& dir,float& tmin,float& tmax) const;
     75    /**
     76     * Returns true if the ray originating in @p origin and with unit direction vector @p dir intersects this AABB, false otherwise.
     77     * Additionally, returns the positive distances from the origin of the ray to the entry and exit points in the bounding box in
     78     * @p tmin and @p tmax. See also Real-Time Rendering, Third Edition by T. Akenine-Möller, p. 741--742.
     79     * @param origin Origin of the ray.
     80     * @param dir Direction vector of the ray. Must be of unit length.
     81     */
     82    bool RayIntersect(const CVector3D& origin, const CVector3D& dir, float& tmin, float& tmax) const;
    7483
    7584    // return the volume of this bounding box
    76     float GetVolume() const {
    77         CVector3D v=m_Data[1]-m_Data[0];
    78         return std::max(v.X, 0.0f)*std::max(v.Y, 0.0f)*std::max(v.Z, 0.0f);
     85    float GetVolume() const
     86    {
     87        CVector3D v = m_Data[1] - m_Data[0];
     88        return (std::max(v.X, 0.0f) * std::max(v.Y, 0.0f) * std::max(v.Z, 0.0f));
    7989    }
    8090
    8191    // return the centre of this bounding box
    82     void GetCentre(CVector3D& centre) const {
    83         centre=(m_Data[0]+m_Data[1])*0.5f;
     92    void GetCentre(CVector3D& centre) const
     93    {
     94        centre = (m_Data[0] + m_Data[1]) * 0.5f;
    8495    }
    8596
    8697    /**
    public:  
    109120    void Render() const;
    110121
    111122private:
     123    // Holds the minimal and maximal coordinate points in m_Data[0] and m_Data[1], respectively.
    112124    CVector3D m_Data[2];
     125
     126public:
     127    static const CBound EMPTY;
     128
    113129};
    114130//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    115131
  • new file source/maths/Box.cpp

    diff --git a/source/maths/Box.cpp b/source/maths/Box.cpp
    new file mode 100644
    index 0000000..7b52cd8
    - +  
     1/* Copyright (C) 2011 Wildfire Games.
     2 * This file is part of 0 A.D.
     3 *
     4 * 0 A.D. is free software: you can redistribute it and/or modify
     5 * it under the terms of the GNU General Public License as published by
     6 * the Free Software Foundation, either version 2 of the License, or
     7 * (at your option) any later version.
     8 *
     9 * 0 A.D. is distributed in the hope that it will be useful,
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 * GNU General Public License for more details.
     13 *
     14 * You should have received a copy of the GNU General Public License
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
     16 */
     17
     18#include "precompiled.h"
     19
     20#include "Box.h"
     21
     22#include <float.h>
     23
     24const CBox CBox::EMPTY = CBox();
     25
     26bool CBox::RayIntersect(const CVector3D& origin, const CVector3D& dir, float& tMin_out, float& tMax_out)
     27{
     28    // See Real-Time Rendering, Third Edition, p. 743
     29    float tMin = -FLT_MAX;
     30    float tMax = FLT_MAX;
     31
     32    CVector3D p = m_Center - origin;
     33
     34    for (int i = 0; i < 3; ++i)
     35    {
     36        float e = m_Basis[i].Dot(p);
     37        float f = m_Basis[i].Dot(dir);
     38
     39        if(fabs(f) > 1e-10f)
     40        {
     41            float invF = 1.f/f;
     42            float t1 = (e + m_HalfSizes[i]) * invF;
     43            float t2 = (e - m_HalfSizes[i]) * invF;
     44
     45            if (t1 > t2)
     46            {
     47                float tmp = t1;
     48                t1 = t2;
     49                t2 = tmp;
     50            }
     51            if (t1 > tMin) tMin = t1;
     52            if (t2 < tMax) tMax = t2;
     53            if (tMin > tMax) return false;
     54            if (tMax < 0) return false;
     55        }
     56        else
     57        {
     58            if(-e - m_HalfSizes[i] > 0 || -e + m_HalfSizes[i] < 0) return false;
     59        }
     60    }
     61
     62    tMin_out = tMin;
     63    tMax_out = tMax;
     64    return true;
     65}
     66 No newline at end of file
  • new file source/maths/Box.h

    diff --git a/source/maths/Box.h b/source/maths/Box.h
    new file mode 100644
    index 0000000..19cb0af
    - +  
     1/* Copyright (C) 2011 Wildfire Games.
     2 * This file is part of 0 A.D.
     3 *
     4 * 0 A.D. is free software: you can redistribute it and/or modify
     5 * it under the terms of the GNU General Public License as published by
     6 * the Free Software Foundation, either version 2 of the License, or
     7 * (at your option) any later version.
     8 *
     9 * 0 A.D. is distributed in the hope that it will be useful,
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 * GNU General Public License for more details.
     13 *
     14 * You should have received a copy of the GNU General Public License
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
     16 */
     17
     18
     19#ifndef INCLUDED_BOX
     20#define INCLUDED_BOX
     21
     22#include "maths/Vector3D.h"
     23#include "maths/Bound.h"
     24
     25/*
     26 * Generic oriented box. Originally intended to be used an Oriented Bounding Box (OBB), as opposed to CBound which is an axis-aligned
     27 * bounding box (AABB).
     28 */
     29class CBox
     30{
     31public:
     32   
     33    /**
     34     * Constructs a new oriented box centered at @p centered and with normalized side vectors @p u, @p v and @p w. These vectors should
     35     * be mutually orthonormal for a proper rectangular box. The half-widths of the box in each direction are given by @p hU, @p hV
     36     * and @p hW, respectively.
     37     */
     38    CBox(const CVector3D& center, const CVector3D& u, const CVector3D& v, const CVector3D& w, const CVector3D& halfSizes)
     39        : m_Center(center), m_HalfSizes(halfSizes)
     40    {
     41        m_Basis[0] = u;
     42        m_Basis[1] = v;
     43        m_Basis[2] = w;
     44    }
     45
     46    /**
     47     * Constructs a new oriented box from an axis-aligned bounding box (AABB).
     48     */
     49    CBox(const CBound& bound)
     50    {
     51        if (bound.IsEmpty())
     52        {
     53            SetEmpty();
     54        }
     55        else
     56        {
     57            bound.GetCentre(m_Center);
     58
     59            // the axes of an AABB are the world-space axes
     60            m_Basis[0].X = 1.f; m_Basis[0].Y = 0.f; m_Basis[0].Z = 0.f;
     61            m_Basis[1].X = 0.f; m_Basis[1].Y = 1.f; m_Basis[1].Z = 0.f;
     62            m_Basis[2].X = 0.f; m_Basis[2].Y = 0.f; m_Basis[2].Z = 1.f;
     63
     64            // element-wise division by two to get half sizes (remember, [1] and [0] are the max and min coord points)
     65            m_HalfSizes = (bound[1] - bound[0]) * 0.5f;
     66        }
     67    }
     68
     69    /**
     70     * Empty constructor
     71     */
     72    CBox()
     73    {
     74        SetEmpty();
     75    }
     76
     77    /**
     78     * Returns true if the ray originating in @p origin and with unit direction vector @p dir intersects this box, false otherwise.
     79     * Additionally, returns the positive distances from the origin of the ray to the entry and exit points in the box in
     80     * @p tmin and @p tmax. See also Real-Time Rendering, Third Edition by T. Akenine-Möller, p. 741--742.
     81     * Should not be used if IsEmpty() is true.
     82     *
     83     * @param origin Origin of the ray.
     84     * @param dir Direction vector of the ray. Must be of unit length.
     85     */
     86    bool RayIntersect(const CVector3D& origin, const CVector3D& dir, float& tMin, float& tMax);
     87
     88    /**
     89     * 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.
     90     * Should not be used if IsEmpty() is true.
     91     */
     92    void GetCorner(int u, int v, int w, CVector3D& out)
     93    {
     94        out = m_Center + m_Basis[0]*(u*m_HalfSizes[0]) + m_Basis[1]*(v*m_HalfSizes[1]) + m_Basis[2]*(w*m_HalfSizes[2]);
     95    }
     96
     97    void SetEmpty()
     98    {
     99        // everything is zero
     100        m_Center = CVector3D();
     101        m_Basis[0] = CVector3D();
     102        m_Basis[1] = CVector3D();
     103        m_Basis[2] = CVector3D();
     104        m_HalfSizes = CVector3D();
     105    }
     106
     107    bool IsEmpty()
     108    {
     109        return (   m_Center.X == 0 &&    m_Center.Y == 0 &&    m_Center.Z == 0 &&
     110                 m_Basis[0].X == 0 &&  m_Basis[0].Y == 0 &&  m_Basis[0].Z == 0 &&
     111                 m_Basis[1].X == 0 &&  m_Basis[1].Y == 0 &&  m_Basis[1].Z == 0 &&
     112                 m_Basis[2].X == 0 &&  m_Basis[2].Y == 0 &&  m_Basis[2].Z == 0 &&
     113                m_HalfSizes.X == 0 && m_HalfSizes.Y == 0 && m_HalfSizes.Z == 0);
     114    }
     115
     116public:
     117    CVector3D m_Center;
     118    CVector3D m_HalfSizes;
     119    CVector3D m_Basis[3]; // orthonormal basis vectors (u,v,w)
     120
     121    static const CBox EMPTY;
     122};
     123
     124#endif INCLUDED_BOX
     125 No newline at end of file
  • source/maths/Matrix3D.h

    diff --git a/source/maths/Matrix3D.h b/source/maths/Matrix3D.h
    index 0a14bd3..d42141f 100644
    a b class CMatrix3D  
    3535public:
    3636    // the matrix data itself - accessible as either longhand names
    3737    // or via a flat or 2d array
     38    // NOTE: _xy means row x, column y, so don't be fooled by the way they're listed below
    3839    union {
    3940        struct {
    4041            float _11, _21, _31, _41;
  • source/renderer/Renderer.cpp

    diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp
    index b8308bc..6215063 100644
    a b public:  
    10411041
    10421042    bool Filter(CModel *model)
    10431043    {
    1044         return m_Frustum.IsBoxVisible(CVector3D(0, 0, 0), model->GetBoundsRec());
     1044        return m_Frustum.IsBoxVisible(CVector3D(0, 0, 0), model->GetWorldBoundsRec());
    10451045    }
    10461046
    10471047private:
    void CRenderer::SubmitNonRecursive(CModel* model)  
    17531753{
    17541754    if (model->GetFlags() & MODELFLAG_CASTSHADOWS) {
    17551755//      PROFILE( "updating shadow bounds" );
    1756         m->shadow->AddShadowedBound(model->GetBounds());
     1756        m->shadow->AddShadowedBound(model->GetWorldBounds());
    17571757    }
    17581758
    17591759    // Tricky: The call to GetBounds() above can invalidate the position
  • source/renderer/TerrainRenderer.cpp

    diff --git a/source/renderer/TerrainRenderer.cpp b/source/renderer/TerrainRenderer.cpp
    index 13b71bc..56b1eca 100644
    a b bool TerrainRenderer::CullPatches(const CFrustum* frustum)  
    173173    m->filteredPatches.clear();
    174174    for (std::vector<CPatchRData*>::iterator it = m->visiblePatches.begin(); it != m->visiblePatches.end(); it++)
    175175    {
    176         if (frustum->IsBoxVisible(CVector3D(0, 0, 0), (*it)->GetPatch()->GetBounds()))
     176        if (frustum->IsBoxVisible(CVector3D(0, 0, 0), (*it)->GetPatch()->GetWorldBounds()))
    177177            m->filteredPatches.push_back(*it);
    178178    }
    179179
    180180    m->filteredDecals.clear();
    181181    for (std::vector<CDecalRData*>::iterator it = m->visibleDecals.begin(); it != m->visibleDecals.end(); it++)
    182182    {
    183         if (frustum->IsBoxVisible(CVector3D(0, 0, 0), (*it)->GetDecal()->GetBounds()))
     183        if (frustum->IsBoxVisible(CVector3D(0, 0, 0), (*it)->GetDecal()->GetWorldBounds()))
    184184            m->filteredDecals.push_back(*it);
    185185    }
    186186
  • source/simulation2/components/CCmpProjectileManager.cpp

    diff --git a/source/simulation2/components/CCmpProjectileManager.cpp b/source/simulation2/components/CCmpProjectileManager.cpp
    index e0ac1cc..c7ffccd 100644
    a b void CCmpProjectileManager::RenderSubmit(SceneCollector& collector, const CFrust  
    347347
    348348        model.ValidatePosition();
    349349
    350         if (culling && !frustum.IsBoxVisible(CVector3D(0, 0, 0), model.GetBounds()))
     350        if (culling && !frustum.IsBoxVisible(CVector3D(0, 0, 0), model.GetWorldBounds()))
    351351            continue;
    352352
    353353        // TODO: do something about LOS (copy from CCmpVisualActor)
  • source/simulation2/components/CCmpSelectable.cpp

    diff --git a/source/simulation2/components/CCmpSelectable.cpp b/source/simulation2/components/CCmpSelectable.cpp
    index e9aa3d2..651cb3f 100644
    a b  
    2222
    2323#include "ICmpPosition.h"
    2424#include "ICmpFootprint.h"
     25#include "ICmpVisual.h"
    2526#include "simulation2/MessageTypes.h"
    2627#include "simulation2/helpers/Render.h"
    2728
    public:  
    153154    void RenderSubmit(SceneCollector& collector)
    154155    {
    155156        // (This is only called if a > 0)
    156 
    157157        collector.Submit(&m_Overlay);
     158
     159        if (ICmpSelectable::s_EnableDebugOverlays)
     160        {
     161            static SOverlayLine* boundOverlayLine = new SOverlayLine;
     162            static SOverlayLine* selectionOverlayLine = new SOverlayLine;
     163
     164            CmpPtr<ICmpVisual> cmpVisual(GetSimContext(), GetEntityId());
     165            if (!cmpVisual.null())
     166            {
     167                CBound bound = cmpVisual->GetBounds();
     168                CVector3D tmin = bound[0];
     169                CVector3D tmax = bound[1];
     170
     171                boundOverlayLine->m_Thickness = 2;
     172                boundOverlayLine->m_Color = CColor(1.f, 0.f, 0.f, 1.f);
     173                boundOverlayLine->m_Coords.clear();
     174                // floor square
     175                boundOverlayLine->m_Coords.push_back(tmin.X); boundOverlayLine->m_Coords.push_back(tmin.Y); boundOverlayLine->m_Coords.push_back(tmin.Z);
     176                boundOverlayLine->m_Coords.push_back(tmax.X); boundOverlayLine->m_Coords.push_back(tmin.Y); boundOverlayLine->m_Coords.push_back(tmin.Z);
     177                boundOverlayLine->m_Coords.push_back(tmax.X); boundOverlayLine->m_Coords.push_back(tmin.Y); boundOverlayLine->m_Coords.push_back(tmax.Z);
     178                boundOverlayLine->m_Coords.push_back(tmin.X); boundOverlayLine->m_Coords.push_back(tmin.Y); boundOverlayLine->m_Coords.push_back(tmax.Z);
     179                boundOverlayLine->m_Coords.push_back(tmin.X); boundOverlayLine->m_Coords.push_back(tmin.Y); boundOverlayLine->m_Coords.push_back(tmin.Z);
     180                // roof square
     181                boundOverlayLine->m_Coords.push_back(tmin.X); boundOverlayLine->m_Coords.push_back(tmax.Y); boundOverlayLine->m_Coords.push_back(tmin.Z);
     182                boundOverlayLine->m_Coords.push_back(tmax.X); boundOverlayLine->m_Coords.push_back(tmax.Y); boundOverlayLine->m_Coords.push_back(tmin.Z);
     183                boundOverlayLine->m_Coords.push_back(tmax.X); boundOverlayLine->m_Coords.push_back(tmax.Y); boundOverlayLine->m_Coords.push_back(tmax.Z);
     184                boundOverlayLine->m_Coords.push_back(tmin.X); boundOverlayLine->m_Coords.push_back(tmax.Y); boundOverlayLine->m_Coords.push_back(tmax.Z);
     185                boundOverlayLine->m_Coords.push_back(tmin.X); boundOverlayLine->m_Coords.push_back(tmax.Y); boundOverlayLine->m_Coords.push_back(tmin.Z);
     186                // supports
     187                collector.Submit(boundOverlayLine);
     188
     189                // -------------------------------------------
     190
     191                CBox orientedBox = cmpVisual->GetSelectionBox();
     192                selectionOverlayLine->m_Thickness = 2;
     193                selectionOverlayLine->m_Color = CColor(0.f, 1.f, 0.f, 1.f);
     194                selectionOverlayLine->m_Coords.clear();
     195
     196                CVector3D corners[8];
     197                orientedBox.GetCorner(-1, -1, -1, corners[0]);
     198                orientedBox.GetCorner( 1, -1, -1, corners[1]);
     199                orientedBox.GetCorner( 1, -1,  1, corners[2]);
     200                orientedBox.GetCorner(-1, -1,  1, corners[3]);
     201                orientedBox.GetCorner(-1,  1, -1, corners[4]);
     202                orientedBox.GetCorner( 1,  1, -1, corners[5]);
     203                orientedBox.GetCorner( 1,  1,  1, corners[6]);
     204                orientedBox.GetCorner(-1,  1,  1, corners[7]);
     205
     206                selectionOverlayLine->m_Coords.push_back(corners[0].X); selectionOverlayLine->m_Coords.push_back(corners[0].Y); selectionOverlayLine->m_Coords.push_back(corners[0].Z);
     207                selectionOverlayLine->m_Coords.push_back(corners[1].X); selectionOverlayLine->m_Coords.push_back(corners[1].Y); selectionOverlayLine->m_Coords.push_back(corners[1].Z);
     208                selectionOverlayLine->m_Coords.push_back(corners[2].X); selectionOverlayLine->m_Coords.push_back(corners[2].Y); selectionOverlayLine->m_Coords.push_back(corners[2].Z);
     209                selectionOverlayLine->m_Coords.push_back(corners[3].X); selectionOverlayLine->m_Coords.push_back(corners[3].Y); selectionOverlayLine->m_Coords.push_back(corners[3].Z);
     210                selectionOverlayLine->m_Coords.push_back(corners[0].X); selectionOverlayLine->m_Coords.push_back(corners[0].Y); selectionOverlayLine->m_Coords.push_back(corners[0].Z);
     211
     212                selectionOverlayLine->m_Coords.push_back(corners[4].X); selectionOverlayLine->m_Coords.push_back(corners[4].Y); selectionOverlayLine->m_Coords.push_back(corners[4].Z);
     213                selectionOverlayLine->m_Coords.push_back(corners[5].X); selectionOverlayLine->m_Coords.push_back(corners[5].Y); selectionOverlayLine->m_Coords.push_back(corners[5].Z);
     214                selectionOverlayLine->m_Coords.push_back(corners[6].X); selectionOverlayLine->m_Coords.push_back(corners[6].Y); selectionOverlayLine->m_Coords.push_back(corners[6].Z);
     215                selectionOverlayLine->m_Coords.push_back(corners[7].X); selectionOverlayLine->m_Coords.push_back(corners[7].Y); selectionOverlayLine->m_Coords.push_back(corners[7].Z);
     216                selectionOverlayLine->m_Coords.push_back(corners[4].X); selectionOverlayLine->m_Coords.push_back(corners[4].Y); selectionOverlayLine->m_Coords.push_back(corners[4].Z);
     217
     218                collector.Submit(selectionOverlayLine);
     219            }
     220        }
    158221    }
    159222};
    160223
  • source/simulation2/components/CCmpVisualActor.cpp

    diff --git a/source/simulation2/components/CCmpVisualActor.cpp b/source/simulation2/components/CCmpVisualActor.cpp
    index f35cb80..ce29a98 100644
    a b public:  
    224224    {
    225225        if (!m_Unit)
    226226            return CBound();
    227         return m_Unit->GetModel().GetBounds();
     227        // TODO: this should probably be GetWorldBoundsRec(), so that it also includes any props
     228        return m_Unit->GetModel().GetWorldBounds();
     229    }
     230
     231    virtual CBox GetSelectionBox()
     232    {
     233        if (!m_Unit)
     234            return CBox();
     235        return m_Unit->GetModel().GetSelectionBox();
    228236    }
    229237
    230238    virtual CVector3D GetPosition()
    void CCmpVisualActor::RenderSubmit(SceneCollector& collector, const CFrustum& fr  
    482490
    483491    CModelAbstract& model = m_Unit->GetModel();
    484492
    485     if (culling && !frustum.IsBoxVisible(CVector3D(0, 0, 0), model.GetBoundsRec()))
     493    if (culling && !frustum.IsBoxVisible(CVector3D(0, 0, 0), model.GetWorldBoundsRec()))
    486494        return;
    487495
    488496    collector.SubmitRecursive(&model);
  • source/simulation2/components/ICmpSelectable.cpp

    diff --git a/source/simulation2/components/ICmpSelectable.cpp b/source/simulation2/components/ICmpSelectable.cpp
    index 20f0b09..1f92268 100644
    a b  
    2626BEGIN_INTERFACE_WRAPPER(Selectable)
    2727DEFINE_INTERFACE_METHOD_1("SetSelectionHighlight", void, ICmpSelectable, SetSelectionHighlight, CColor)
    2828END_INTERFACE_WRAPPER(Selectable)
     29
     30bool ICmpSelectable::s_EnableDebugOverlays = false;
     31 No newline at end of file
  • source/simulation2/components/ICmpSelectable.h

    diff --git a/source/simulation2/components/ICmpSelectable.h b/source/simulation2/components/ICmpSelectable.h
    index eadd554..484b455 100644
    a b public:  
    3232    virtual void SetSelectionHighlight(CColor color) = 0;
    3333
    3434    DECLARE_INTERFACE_TYPE(Selectable)
     35
     36    static bool s_EnableDebugOverlays;
    3537};
    3638
    3739#endif // INCLUDED_ICMPSELECTABLE
  • source/simulation2/components/ICmpVisual.h

    diff --git a/source/simulation2/components/ICmpVisual.h b/source/simulation2/components/ICmpVisual.h
    index 68f1108..4d7a12a 100644
    a b  
    2020
    2121#include "simulation2/system/Interface.h"
    2222
     23#include "maths/Box.h"
    2324#include "maths/Bound.h"
    2425#include "maths/Fixed.h"
    2526#include "lib/file/vfs/vfs_path.h"
    public:  
    3738    virtual CBound GetBounds() = 0;
    3839
    3940    /**
     41     * Get the oriented world-space bounding box of the object's visual representation, clipped at the Y=0 plane in object space
     42     * to prevent it from extending into the terrain. The primary difference with GetBounds is that this bounding box is not aligned
     43     * to the world axes, but arbitrarily rotated according to the model transform.
     44     */
     45    virtual CBox GetSelectionBox() = 0;
     46
     47    /**
    4048     * Get the world-space position of the base point of the object's visual representation.
    4149     * (Not safe for use in simulation code.)
    4250     */
  • source/simulation2/helpers/Selection.cpp

    diff --git a/source/simulation2/helpers/Selection.cpp b/source/simulation2/helpers/Selection.cpp
    index 0405894..9b61744 100644
    a b std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simu  
    5151        if (cmpVisual.null())
    5252            continue;
    5353
    54         CBound bounds = cmpVisual->GetBounds();
     54        //CBound bounds = cmpVisual->GetBounds();
     55        CBox selectionBox = cmpVisual->GetSelectionBox();
     56        if (selectionBox.IsEmpty())
     57            continue;
    5558
    5659        float tmin, tmax;
    57         if (!bounds.RayIntersect(origin, dir, tmin, tmax))
     60        if (!selectionBox.RayIntersect(origin, dir, tmin, tmax))
    5861            continue;
    5962
    6063        // Find the perpendicular distance from the object's centre to the picker ray
    6164
    62         CVector3D centre;
    63         bounds.GetCentre(centre);
    64 
    65         CVector3D closest = origin + dir * (centre - origin).Dot(dir);
    66         float dist2 = (closest - centre).LengthSquared();
     65        CVector3D closest = origin + dir * (selectionBox.m_Center - origin).Dot(dir);
     66        float dist2 = (closest - selectionBox.m_Center).LengthSquared();
    6767
    6868        hits.push_back(std::make_pair(dist2, ent));
    6969    }