Ticket #824: pretty_unit_rings_WIP_01mar12.patch

File pretty_unit_rings_WIP_01mar12.patch, 97.2 KB (added by vts, 12 years ago)
  • new file inaries/data/mods/public/shaders/overlay_selection.fp

    diff --git a/binaries/data/mods/public/shaders/overlay_selection.fp b/binaries/data/mods/public/shaders/overlay_selection.fp
    new file mode 100644
    index 0000000..f6c5708
    - +  
     1!!ARBfp1.0
     2TEMP base;
     3TEMP mask;
     4TEMP color;
     5
     6// Combine base texture and color, using mask texture
     7TEX base, fragment.texcoord[0], texture[0], 2D;
     8TEX mask, fragment.texcoord[0], texture[1], 2D;
     9LRP color.rgb, mask, fragment.color, base;
     10
     11// Multiply RGB by LOS texture (alpha channel)
     12TEMP los;
     13TEX los, fragment.texcoord[1], texture[2], 2D;
     14MUL result.color.rgb, color, los.a;
     15
     16// Use alpha from base texture, combined with the object color alpha.
     17MUL result.color.a, fragment.color.a, base.a;
     18
     19END
  • new file inaries/data/mods/public/shaders/overlay_selection.vp

    diff --git a/binaries/data/mods/public/shaders/overlay_selection.vp b/binaries/data/mods/public/shaders/overlay_selection.vp
    new file mode 100644
    index 0000000..b4a9abb
    - +  
     1!!ARBvp1.0
     2PARAM losTransform = program.local[0];
     3ATTRIB position = vertex.position;
     4
     5DP4 result.position.x, state.matrix.mvp.row[0], position;
     6DP4 result.position.y, state.matrix.mvp.row[1], position;
     7DP4 result.position.z, state.matrix.mvp.row[2], position;
     8DP4 result.position.w, state.matrix.mvp.row[3], position;
     9
     10MOV result.texcoord[0], vertex.texcoord[0];
     11MAD result.texcoord[1], position.xzzz, losTransform.x, losTransform.y;
     12
     13MOV result.color, vertex.color;
     14
     15END
  • new file inaries/data/mods/public/shaders/overlay_selection.xml

    diff --git a/binaries/data/mods/public/shaders/overlay_selection.xml b/binaries/data/mods/public/shaders/overlay_selection.xml
    new file mode 100644
    index 0000000..7cf03e6
    - +  
     1<?xml version="1.0" encoding="utf-8"?>
     2<program type="arb">
     3
     4    <vertex file="overlay_selection.vp">
     5        <stream name="pos"/>
     6        <stream name="uv0"/>
     7        <stream name="color"/>
     8        <uniform name="losTransform" loc="0" type="vec2"/>
     9    </vertex>
     10
     11    <fragment file="overlay_selection.fp">
     12        <uniform name="baseTex" loc="0" type="sampler2D"/>
     13        <uniform name="maskTex" loc="1" type="sampler2D"/>
     14        <uniform name="losTex" loc="2" type="sampler2D"/>
     15    </fragment>
     16
     17</program>
  • binaries/data/mods/public/simulation/components/GuiInterface.js

    diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js
    index 16ee222..c7fdb51 100644
    a b GuiInterface.prototype.IsStanceSelected = function(player, data)  
    375375GuiInterface.prototype.SetSelectionHighlight = function(player, cmd)
    376376{
    377377    var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
    378 
    379378    var playerColours = {}; // cache of owner -> colour map
    380379   
    381380    for each (var ent in cmd.entities)
    GuiInterface.prototype.SetSelectionHighlight = function(player, cmd)  
    384383        if (!cmpSelectable)
    385384            continue;
    386385
    387         if (cmd.alpha == 0)
    388         {
    389             cmpSelectable.SetSelectionHighlight({"r":0, "g":0, "b":0, "a":0});
    390             continue;
    391         }
    392 
    393386        // Find the entity's owner's colour:
    394 
    395387        var owner = -1;
    396388        var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
    397389        if (cmpOwnership)
  • binaries/data/mods/public/simulation/templates/special/territory_block.xml

    diff --git a/binaries/data/mods/public/simulation/templates/special/territory_block.xml b/binaries/data/mods/public/simulation/templates/special/territory_block.xml
    index 52432b2..f0893a0 100644
    a b  
    2323  </Position>
    2424  <Selectable>
    2525    <EditorOnly disable=""/>
     26    <Overlay>
     27      <Texture>
     28        <MainTexture>art/textures/selection/circle/64x64.png</MainTexture>
     29        <MainTextureMask>art/textures/selection/circle/64x64_mask.png</MainTextureMask>
     30      </Texture>
     31    </Overlay>
    2632  </Selectable>
    2733  <TerritoryInfluence>
    2834    <OverrideCost>64</OverrideCost>
  • binaries/data/mods/public/simulation/templates/special/territory_pull.xml

    diff --git a/binaries/data/mods/public/simulation/templates/special/territory_pull.xml b/binaries/data/mods/public/simulation/templates/special/territory_pull.xml
    index bd8c4ac..a12174a 100644
    a b  
    2323  </Position>
    2424  <Selectable>
    2525    <EditorOnly disable=""/>
     26    <Overlay>
     27      <Texture>
     28        <MainTexture>art/textures/selection/circle/64x64.png</MainTexture>
     29        <MainTextureMask>art/textures/selection/circle/64x64_mask.png</MainTextureMask>
     30      </Texture>
     31    </Overlay>
    2632  </Selectable>
    2733  <TerritoryInfluence>
    2834    <OverrideCost>0</OverrideCost>
  • binaries/data/mods/public/simulation/templates/template_gaia_flora_bush_berry.xml

    diff --git a/binaries/data/mods/public/simulation/templates/template_gaia_flora_bush_berry.xml b/binaries/data/mods/public/simulation/templates/template_gaia_flora_bush_berry.xml
    index e979c8e..775f6e0 100644
    a b  
    1818  </ResourceSupply>
    1919  <Selectable>
    2020    <EditorOnly disable=""/>
     21    <Overlay>
     22      <Texture>
     23        <MainTexture>art/textures/selection/circle/64x64.png</MainTexture>
     24        <MainTextureMask>art/textures/selection/circle/64x64_mask.png</MainTextureMask>
     25      </Texture>
     26    </Overlay>
    2127  </Selectable>
    2228  <Sound>
    2329    <SoundGroups>
  • binaries/data/mods/public/simulation/templates/template_gaia_flora_tree.xml

    diff --git a/binaries/data/mods/public/simulation/templates/template_gaia_flora_tree.xml b/binaries/data/mods/public/simulation/templates/template_gaia_flora_tree.xml
    index ca3e80f..d5ba7d7 100644
    a b  
    1717  </ResourceSupply>
    1818  <Selectable>
    1919    <EditorOnly disable=""/>
     20    <Overlay>
     21      <Texture>
     22        <MainTexture>art/textures/selection/circle/64x64.png</MainTexture>
     23        <MainTextureMask>art/textures/selection/circle/64x64_mask.png</MainTextureMask>
     24      </Texture>
     25    </Overlay>
    2026  </Selectable>
    2127  <Sound>
    2228    <SoundGroups>
  • binaries/data/mods/public/simulation/templates/template_gaia_geo_mineral.xml

    diff --git a/binaries/data/mods/public/simulation/templates/template_gaia_geo_mineral.xml b/binaries/data/mods/public/simulation/templates/template_gaia_geo_mineral.xml
    index f3a147a..d04ad80 100644
    a b  
    1717  </ResourceSupply>
    1818  <Selectable>
    1919    <EditorOnly disable=""/>
     20    <Overlay>
     21      <Outline>
     22        <LineTexture>art/textures/selection/outline_border.png</LineTexture>
     23        <LineTextureMask>art/textures/selection/outline_border_mask.png</LineTextureMask>
     24        <LineThickness>0.2</LineThickness>
     25      </Outline>
     26    </Overlay>
    2027  </Selectable>
    2128  <Sound>
    2229    <SoundGroups>
  • binaries/data/mods/public/simulation/templates/template_gaia_geo_rock.xml

    diff --git a/binaries/data/mods/public/simulation/templates/template_gaia_geo_rock.xml b/binaries/data/mods/public/simulation/templates/template_gaia_geo_rock.xml
    index a954293..b0cf874 100644
    a b  
    1717  </ResourceSupply>
    1818  <Selectable>
    1919    <EditorOnly disable=""/>
     20    <Overlay>
     21      <Texture>
     22        <MainTexture>art/textures/selection/circle/64x64.png</MainTexture>
     23        <MainTextureMask>art/textures/selection/circle/64x64_mask.png</MainTextureMask>
     24      </Texture>
     25    </Overlay>
    2026  </Selectable>
    2127  <Sound>
    2228    <SoundGroups>
  • binaries/data/mods/public/simulation/templates/template_gaia_ruins.xml

    diff --git a/binaries/data/mods/public/simulation/templates/template_gaia_ruins.xml b/binaries/data/mods/public/simulation/templates/template_gaia_ruins.xml
    index 86037bb..2b256cc 100644
    a b  
    2424  </ResourceSupply>
    2525  <Selectable>
    2626    <EditorOnly disable=""/>
     27    <Overlay>
     28      <Texture>
     29        <MainTexture>art/textures/selection/circle/64x64.png</MainTexture>
     30        <MainTextureMask>art/textures/selection/circle/64x64_mask.png</MainTextureMask>
     31      </Texture>
     32    </Overlay>
    2733  </Selectable>
    2834</Entity>
  • binaries/data/mods/public/simulation/templates/template_gaia_treasure.xml

    diff --git a/binaries/data/mods/public/simulation/templates/template_gaia_treasure.xml b/binaries/data/mods/public/simulation/templates/template_gaia_treasure.xml
    index cb45f0a..b2744f4 100644
    a b  
    2424  </ResourceSupply>
    2525  <Selectable>
    2626    <EditorOnly disable=""/>
     27    <Overlay>
     28      <Texture>
     29        <MainTexture>art/textures/selection/circle/64x64.png</MainTexture>
     30        <MainTextureMask>art/textures/selection/circle/64x64_mask.png</MainTextureMask>
     31      </Texture>
     32    </Overlay>
    2733  </Selectable>
    2834</Entity>
  • binaries/data/mods/public/simulation/templates/template_structure.xml

    diff --git a/binaries/data/mods/public/simulation/templates/template_structure.xml b/binaries/data/mods/public/simulation/templates/template_structure.xml
    index d876613..a5a9827 100644
    a b  
    6666    <LineCostClass>default</LineCostClass>
    6767    <LinePassabilityClass>default</LinePassabilityClass>
    6868  </RallyPointRenderer>
     69  <Selectable>
     70    <Overlay>
     71      <Outline>
     72        <LineTexture>art/textures/selection/outline_border.png</LineTexture>
     73        <LineTextureMask>art/textures/selection/outline_border_mask.png</LineTextureMask>
     74        <LineThickness>0.4</LineThickness>
     75      </Outline>
     76    </Overlay>
     77  </Selectable>
    6978  <Sound>
    7079    <SoundGroups>
    7180      <select>interface/select/building/sel_universal.xml</select>
  • binaries/data/mods/public/simulation/templates/template_unit.xml

    diff --git a/binaries/data/mods/public/simulation/templates/template_unit.xml b/binaries/data/mods/public/simulation/templates/template_unit.xml
    index d524a39..20c9d38 100644
    a b  
    8080      <metal>20</metal>
    8181    </Capacities>
    8282  </ResourceGatherer>
     83  <Selectable>
     84    <Overlay>
     85      <Texture>
     86        <MainTexture>art/textures/selection/circle/64x64.png</MainTexture>
     87        <MainTextureMask>art/textures/selection/circle/64x64_mask.png</MainTextureMask>
     88      </Texture>
     89    </Overlay>
     90  </Selectable>
    8391  <StatusBars>
    8492    <BarWidth>2.0</BarWidth>
    8593    <BarHeight>0.333</BarHeight>
  • source/graphics/ModelAbstract.h

    diff --git a/binaries/system/ActorEditor.exe b/binaries/system/ActorEditor.exe
    deleted file mode 100644
    index 15cdfc5..0000000
    Binary files a/binaries/system/ActorEditor.exe and /dev/null differ
    diff --git a/source/graphics/ModelAbstract.h b/source/graphics/ModelAbstract.h
    index 1686847..60baec3 100644
    a b public:  
    8989    virtual void SetDirtyRec(int dirtyflags) = 0;
    9090
    9191    /// Returns world space bounds of this object and all child objects.
    92     virtual const CBoundingBoxAligned GetWorldBoundsRec() { return GetWorldBounds(); }
     92    virtual const CBoundingBoxAligned GetWorldBoundsRec() { return GetWorldBounds(); } // default implementation
    9393
    9494    /**
    9595     * Returns the world-space selection box of this model. Used primarily for hittesting against against a selection ray. The
  • source/graphics/Overlay.h

    diff --git a/source/graphics/Overlay.h b/source/graphics/Overlay.h
    index ce6f7c4..7af3282 100644
    a b  
    1818#ifndef INCLUDED_GRAPHICS_OVERLAY
    1919#define INCLUDED_GRAPHICS_OVERLAY
    2020
     21#include "precompiled.h"
     22
     23#include "lib/ogl.h"
    2124#include "graphics/RenderableObject.h"
    2225#include "graphics/Texture.h"
     26#include "maths/Vector2D.h"
    2327#include "maths/Vector3D.h"
    2428#include "ps/Overlay.h" // CColor  (TODO: that file has nothing to do with overlays, it should be renamed)
     29#include "simulation2/components/ICmpFootprint.h"
    2530
    2631class CTerrain;
    2732
    struct SOverlayLine  
    3742    std::vector<float> m_Coords; // (x, y, z) vertex coordinate triples; shape is not automatically closed
    3843    u8 m_Thickness; // pixels
    3944
    40     /// Utility function; pushes three vertex coordinates at once onto the coordinates array
    4145    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); }
    42     /// Utility function; pushes a vertex location onto the coordinates array
    4346    void PushCoords(const CVector3D& v) { PushCoords(v.X, v.Y, v.Z); }
    4447};
    4548
    struct SOverlayTexturedLine  
    6467    };
    6568
    6669    SOverlayTexturedLine()
    67         : m_Terrain(NULL), m_Thickness(1.0f), m_Closed(false), m_AlwaysVisible(false), m_StartCapType(LINECAP_FLAT), m_EndCapType(LINECAP_FLAT)
    68     {}
     70        : m_SimContext(NULL), m_Thickness(1.0f), m_Closed(false), m_AlwaysVisible(false),
     71          m_StartCapType(LINECAP_FLAT), m_EndCapType(LINECAP_FLAT)
     72    { }
    6973
    70     CTerrain* m_Terrain;
    7174    CTexturePtr m_TextureBase;
    7275    CTexturePtr m_TextureMask;
    7376    CColor m_Color; ///< Color to apply to the line texture
    struct SOverlayTexturedLine  
    7982    LineCapType m_StartCapType; ///< LineCapType to be used at the start of the line
    8083    LineCapType m_EndCapType; ///< LineCapType to be used at the end of the line
    8184
     85    const CSimContext* m_SimContext; /// Simulation context applicable for this overlay line; used to obtain terrain information
    8286    shared_ptr<CRenderData> m_RenderData; ///< Cached renderer data (shared_ptr so that copies/deletes are automatic)
    8387
    8488    /**
    struct SOverlayTexturedLine  
    8690     * If the input string is unrecognized, a warning is issued and a default value is returned.
    8791     */
    8892    static LineCapType StrToLineCapType(const std::wstring& str);
     93
     94    void PushCoords(const float x, const float z) { m_Coords.push_back(x); m_Coords.push_back(z); }
     95    void PushCoords(const CVector2D& v) { PushCoords(v.X, v.Y); }
     96    void PushCoords(const std::vector<CVector2D>& points)
     97    {
     98        for (std::size_t i = 0; i < points.size(); ++i)
     99            PushCoords(points[i]);
     100    }
    89101};
    90102
    91103/**
    struct SOverlaySprite  
    99111    float m_X0, m_Y0, m_X1, m_Y1; // billboard corner coordinates, relative to base position
    100112};
    101113
     114/**
     115 * Rectangular single-quad terrain overlay, with world space coordinates.
     116 */
     117struct SOverlayQuad
     118{
     119    CTexturePtr m_Texture;
     120    CTexturePtr m_TextureMask;
     121    CVector3D m_Corners[4];
     122    CColor m_Color;
     123};
     124
    102125// TODO: OverlayText
    103126
    104127#endif // INCLUDED_GRAPHICS_OVERLAY
  • source/graphics/RenderableObject.h

    diff --git a/source/graphics/RenderableObject.h b/source/graphics/RenderableObject.h
    index c3784fd..01bb923 100644
    a b class CRenderableObject  
    5757
    5858public:
    5959    // constructor
    60     CRenderableObject() : m_RenderData(0), m_BoundsValid(false) {
     60    CRenderableObject() : m_RenderData(0), m_BoundsValid(false)
     61    {
    6162        m_Transform.SetIdentity();
    6263    }
    6364    // destructor
    6465    virtual ~CRenderableObject() { delete m_RenderData; }
    6566
    6667    // set object transform
    67     virtual void SetTransform(const CMatrix3D& transform) {
     68    virtual void SetTransform(const CMatrix3D& transform)
     69    {
    6870        if (m_Transform == transform)
    6971            return;
    7072        // store transform, calculate inverse
    public:  
    8284
    8385    // mark some part of the renderdata as dirty, and requiring
    8486    // an update on next render
    85     void SetDirty(u32 dirtyflags) {
    86         if (m_RenderData) m_RenderData->m_UpdateFlags|=dirtyflags;
     87    void SetDirty(u32 dirtyflags)
     88    {
     89        if (m_RenderData)
     90            m_RenderData->m_UpdateFlags |= dirtyflags;
    8791    }
    8892
    8993    /**
    public:  
    98102    virtual void CalcBounds() = 0;
    99103
    100104    /// Returns the world-space axis-aligned bounds of this object.
    101     const CBoundingBoxAligned& GetWorldBounds() {
     105    const CBoundingBoxAligned& GetWorldBounds()
     106    {
    102107        RecalculateBoundsIfNecessary();
    103108        return m_WorldBounds;
    104109    }
    public:  
    111116    virtual void InvalidateBounds() { m_BoundsValid = false; }
    112117
    113118    // Set the object renderdata and free previous renderdata, if any.
    114     void SetRenderData(CRenderData* renderdata) {
     119    void SetRenderData(CRenderData* renderdata)
     120    {
    115121        delete m_RenderData;
    116122        m_RenderData = renderdata;
    117123    }
  • source/maths/FixedVector2D.h

    diff --git a/source/maths/FixedVector2D.h b/source/maths/FixedVector2D.h
    index c58f66f..739469e 100644
    a b public:  
    2727    fixed X, Y;
    2828
    2929    CFixedVector2D() { }
    30 
    3130    CFixedVector2D(fixed X, fixed Y) : X(X), Y(Y) { }
    3231
    3332    /// Vector equality
  • source/maths/MathUtil.h

    diff --git a/source/maths/MathUtil.h b/source/maths/MathUtil.h
    index 741e57e..b00a70b 100644
    a b  
    1818#ifndef INCLUDED_MATHUTIL
    1919#define INCLUDED_MATHUTIL
    2020
     21#include <cmath>
     22
    2123#define DEGTORAD(a)                 ((a) * ((float)M_PI/180.0f))
    2224#define RADTODEG(a)                 ((a) * (180.0f/(float)M_PI))
    2325#define SQR(x)                      ((x) * (x))
  • source/maths/Vector2D.h

    diff --git a/source/maths/Vector2D.h b/source/maths/Vector2D.h
    index 977584c..55787d8 100644
    a b public:  
    121121        Y /= mag;
    122122    }
    123123
    124     CVector2D Normalized()
     124    CVector2D Normalized() const
    125125    {
    126126        float mag = Length();
    127127        return CVector2D(X / mag, Y / mag);
    public:  
    130130    /**
    131131     * Returns a version of this vector rotated counterclockwise by @p angle radians.
    132132     */
    133     CVector2D Rotated(float angle)
     133    CVector2D Rotated(float angle) const
    134134    {
    135135        float c = cosf(angle);
    136136        float s = sinf(angle);
  • source/renderer/OverlayRenderer.cpp

    diff --git a/source/renderer/OverlayRenderer.cpp b/source/renderer/OverlayRenderer.cpp
    index 938574c..9382ec0 100644
    a b  
    1919
    2020#include "OverlayRenderer.h"
    2121
    22 #include "maths/MathUtil.h"
    23 #include "maths/Quaternion.h"
    24 #include "maths/Vector2D.h"
     22#include "boost/unordered_map.hpp"
    2523#include "graphics/LOSTexture.h"
    2624#include "graphics/Overlay.h"
    2725#include "graphics/Terrain.h"
    2826#include "graphics/TextureManager.h"
    2927#include "lib/ogl.h"
     28#include "maths/MathUtil.h"
     29#include "maths/Quaternion.h"
    3030#include "ps/Game.h"
    3131#include "ps/Profile.h"
    3232#include "renderer/Renderer.h"
     
    3434#include "renderer/VertexBufferManager.h"
    3535#include "simulation2/Simulation2.h"
    3636#include "simulation2/components/ICmpWaterManager.h"
     37#include "simulation2/system/SimContext.h"
     38
     39const float OverlayRenderer::OVERLAY_VOFFSET = 0.2f;
     40
     41struct QuadBatchKey
     42{
     43    QuadBatchKey (CTexturePtr texture, CTexturePtr textureMask) : m_Texture(texture), m_TextureMask(textureMask){ }
     44
     45    struct hash : std::unary_function<QuadBatchKey, size_t> {
     46        size_t operator()(const QuadBatchKey& d) const;
     47    };
     48
     49    struct eq : std::binary_function<QuadBatchKey, QuadBatchKey, bool> {
     50        bool operator()(const QuadBatchKey& d1, const QuadBatchKey& d2) const;
     51    };
     52
     53    CTexturePtr m_Texture;
     54    CTexturePtr m_TextureMask;
     55};
     56
     57class QuadBatchData : public CRenderData
     58{
     59public:
     60    struct SVertex
     61    {
     62        SVertex(const CVector3D& pos, GLshort u, GLshort v, const CColor& color) : m_Position(pos), m_Color(color)
     63        { m_UVs[0] = u; m_UVs[1] = v; }
     64
     65        CVector3D m_Position;
     66        GLshort m_UVs[2];
     67        CColor m_Color;
     68    };
     69    cassert(sizeof(SVertex) == 32);
     70
     71    QuadBatchData() : m_VB(NULL), m_VBIndices(NULL) { }
     72    ~QuadBatchData()
     73    {
     74        // TODO: gets called by boost::unordered_map during PrepareForRendering, not desirable
     75        if (m_VB)
     76            g_VBMan.Release(m_VB);
     77        if (m_VBIndices)
     78            g_VBMan.Release(m_VBIndices);
     79    }
     80
     81    std::vector<SOverlayQuad*> m_Quads;
     82    CVertexBuffer::VBChunk* m_VB;
     83    CVertexBuffer::VBChunk* m_VBIndices;
     84};
    3785
    3886struct OverlayRendererInternals
    3987{
     88    typedef boost::unordered_map<QuadBatchKey, QuadBatchData, QuadBatchKey::hash, QuadBatchKey::eq> QuadBatchMap;
     89
    4090    std::vector<SOverlayLine*> lines;
    4191    std::vector<SOverlayTexturedLine*> texlines;
    4292    std::vector<SOverlaySprite*> sprites;
     93    std::vector<SOverlayQuad*> quads;
     94
     95    QuadBatchMap quadBatchData;
    4396};
    4497
    4598class CTexturedLineRData : public CRenderData
    4699{
    47100public:
    48     CTexturedLineRData(SOverlayTexturedLine* line) :
    49         m_Line(line), m_VB(NULL), m_VBIndices(NULL), m_Raise(.2f)
     101    CTexturedLineRData(SOverlayTexturedLine* line) : m_Line(line), m_VB(NULL), m_VBIndices(NULL)
    50102    { }
    51103
    52104    ~CTexturedLineRData()
    public:  
    62114        SVertex(CVector3D pos, float u, float v) : m_Position(pos) { m_UVs[0] = u; m_UVs[1] = v; }
    63115        CVector3D m_Position;
    64116        GLfloat m_UVs[2];
    65         float _padding[3]; // 5 floats up till now, so pad with another 3 floats to get a power of 2
     117        float _padding[3]; // get a pow2 struct size
    66118    };
    67119    cassert(sizeof(SVertex) == 32);
    68120
    public:  
    91143    SOverlayTexturedLine* m_Line;
    92144    CVertexBuffer::VBChunk* m_VB;
    93145    CVertexBuffer::VBChunk* m_VBIndices;
    94 
    95     float m_Raise; // small vertical offset of line from terrain to prevent visual glitches
    96146};
    97147
    98148OverlayRenderer::OverlayRenderer()
    void OverlayRenderer::Submit(SOverlaySprite* overlay)  
    128178    m->sprites.push_back(overlay);
    129179}
    130180
     181void OverlayRenderer::Submit(SOverlayQuad* overlay)
     182{
     183    m->quads.push_back(overlay);
     184}
     185
    131186void OverlayRenderer::EndFrame()
    132187{
    133188    m->lines.clear();
    134189    m->texlines.clear();
    135190    m->sprites.clear();
     191    m->quads.clear();
    136192    // this should leave the capacity unchanged, which is okay since it
    137193    // won't be very large or very variable
     194   
     195    // empty the batch rendering data structures, but keep their key mappings around for the next frames
     196    for (OverlayRendererInternals::QuadBatchMap::iterator it = m->quadBatchData.begin(); it != m->quadBatchData.end(); it++)
     197    {
     198        QuadBatchData& batchRenderData = (it->second);
     199
     200        batchRenderData.m_Quads.clear();
     201        if (batchRenderData.m_VB)
     202        {
     203            g_VBMan.Release(batchRenderData.m_VB);
     204            batchRenderData.m_VB = NULL;
     205        }
     206        if (batchRenderData.m_VBIndices)
     207        {
     208            g_VBMan.Release(batchRenderData.m_VBIndices);
     209            batchRenderData.m_VBIndices = NULL;
     210        }
     211    }
    138212}
    139213
    140214void OverlayRenderer::PrepareForRendering()
    void OverlayRenderer::PrepareForRendering()  
    157231            // any of the parameters after first submitting the line.
    158232        }
    159233    }
     234
     235    // group quad overlays by their texture/mask combination for efficient rendering
     236    for (size_t i = 0; i < m->quads.size(); ++i)
     237    {
     238        SOverlayQuad* const quad = m->quads[i];
     239
     240        QuadBatchKey textures(quad->m_Texture, quad->m_TextureMask);
     241        QuadBatchData& batchRenderData = m->quadBatchData[textures]; // will create entry if it doesn't already exist
     242
     243        // add overlay to list of quads
     244        batchRenderData.m_Quads.push_back(quad);
     245    }
     246
     247    // Add vertices
     248    for (OverlayRendererInternals::QuadBatchMap::iterator it = m->quadBatchData.begin(); it != m->quadBatchData.end(); ++it)
     249    {
     250        QuadBatchData& batchRenderData = (it->second);
     251        if (batchRenderData.m_Quads.size() == 0)
     252            continue;
     253
     254        const CVector3D vOffset(0, OVERLAY_VOFFSET, 0);
     255
     256        std::vector<u16> indices;
     257        std::vector<QuadBatchData::SVertex> vertices;
     258
     259        for (size_t i = 0; i < batchRenderData.m_Quads.size(); i++)
     260        {
     261            const SOverlayQuad* quad = batchRenderData.m_Quads[i];
     262           
     263            size_t numVerticesBefore = vertices.size();
     264            vertices.push_back(QuadBatchData::SVertex(quad->m_Corners[0] + vOffset, 0, 0, quad->m_Color));
     265            vertices.push_back(QuadBatchData::SVertex(quad->m_Corners[1] + vOffset, 0, 1, quad->m_Color));
     266            vertices.push_back(QuadBatchData::SVertex(quad->m_Corners[2] + vOffset, 1, 1, quad->m_Color));
     267            vertices.push_back(QuadBatchData::SVertex(quad->m_Corners[3] + vOffset, 1, 0, quad->m_Color));
     268
     269            indices.push_back(numVerticesBefore);
     270            indices.push_back(numVerticesBefore + 1);
     271            indices.push_back(numVerticesBefore + 2);
     272            indices.push_back(numVerticesBefore);
     273            indices.push_back(numVerticesBefore + 2);
     274            indices.push_back(numVerticesBefore + 3);
     275        }
     276
     277        ENSURE(batchRenderData.m_VB == NULL);
     278
     279        batchRenderData.m_VB = g_VBMan.Allocate(sizeof(QuadBatchData::SVertex), vertices.size(), GL_DYNAMIC_DRAW, GL_ARRAY_BUFFER);
     280        if (batchRenderData.m_VB) // allocation might fail
     281        {
     282            batchRenderData.m_VB->m_Owner->UpdateChunkVertices(batchRenderData.m_VB, &vertices[0]); // copy data into VBO
     283
     284            for (size_t k = 0; k < indices.size(); ++k)
     285                indices[k] += batchRenderData.m_VB->m_Index;
     286
     287            batchRenderData.m_VBIndices = g_VBMan.Allocate(sizeof(u16), indices.size(), GL_DYNAMIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
     288            if (batchRenderData.m_VBIndices)
     289                batchRenderData.m_VBIndices->m_Owner->UpdateChunkVertices(batchRenderData.m_VBIndices, &indices[0]);
     290        }
     291    }
    160292}
    161293
    162294void OverlayRenderer::RenderOverlaysBeforeWater()
    void OverlayRenderer::RenderOverlaysAfterWater()  
    196328{
    197329    PROFILE3_GPU("overlays (after)");
    198330
     331    RenderTexturedOverlayLines();
     332    RenderQuadOverlays();
     333}
     334
     335void OverlayRenderer::RenderTexturedOverlayLines()
     336{
    199337#if CONFIG2_GLES
    200 #warning TODO: implement OverlayRenderer::RenderOverlaysAfterWater for GLES
     338#warning TODO: implement OverlayRenderer::RenderTexturedOverlayLines for GLES
    201339    return;
    202340#endif
     341    if (m->texlines.empty())
     342        return;
    203343
    204     if (!m->texlines.empty())
    205     {
    206         glEnable(GL_TEXTURE_2D);
    207         glEnable(GL_BLEND);
    208         glDepthMask(0);
     344    glEnable(GL_TEXTURE_2D);
     345    glEnable(GL_BLEND);
     346    glDepthMask(0);
    209347
    210         const char* shaderName;
    211         if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER)
    212             shaderName = "overlayline";
    213         else
    214             shaderName = "fixed:overlayline";
     348    const char* shaderName;
     349    if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER)
     350        shaderName = "overlayline";
     351    else
     352        shaderName = "fixed:overlayline";
    215353
    216         std::map<CStr, CStr> defAlwaysVisible;
    217         defAlwaysVisible.insert(std::make_pair(CStr("IGNORE_LOS"), CStr("1")));
     354    std::map<CStr, CStr> defAlwaysVisible;
     355    defAlwaysVisible.insert(std::make_pair(CStr("IGNORE_LOS"), CStr("1")));
    218356
    219         CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
     357    CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
    220358
    221         CShaderManager& shaderManager = g_Renderer.GetShaderManager();
    222         CShaderProgramPtr shaderTexLineNormal(shaderManager.LoadProgram(shaderName));
    223         CShaderProgramPtr shaderTexLineAlwaysVisible(shaderManager.LoadProgram(shaderName, defAlwaysVisible));
     359    CShaderManager& shaderManager = g_Renderer.GetShaderManager();
     360    CShaderProgramPtr shaderTexLineNormal(shaderManager.LoadProgram(shaderName));
     361    CShaderProgramPtr shaderTexLineAlwaysVisible(shaderManager.LoadProgram(shaderName, defAlwaysVisible));
    224362
    225         // ----------------------------------------------------------------------------------------
     363    // ----------------------------------------------------------------------------------------
    226364
    227         shaderTexLineNormal->Bind();
    228         shaderTexLineNormal->BindTexture("losTex", los.GetTexture());
    229         shaderTexLineNormal->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
     365    shaderTexLineNormal->Bind();
     366    shaderTexLineNormal->BindTexture("losTex", los.GetTexture());
     367    shaderTexLineNormal->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
    230368
    231         // batch render only the non-always-visible overlay lines using the normal shader
    232         RenderTexturedOverlayLines(shaderTexLineNormal, false);
     369    // batch render only the non-always-visible overlay lines using the normal shader
     370    RenderTexturedOverlayLines(shaderTexLineNormal, false);
    233371
    234         shaderTexLineNormal->Unbind();
     372    shaderTexLineNormal->Unbind();
    235373
    236         // ----------------------------------------------------------------------------------------
     374    // ----------------------------------------------------------------------------------------
    237375
    238         shaderTexLineAlwaysVisible->Bind();
    239         // TODO: losTex and losTransform are unused in the always visible shader, but I'm not sure if it's worthwhile messing
    240         // with it just to remove these calls
    241         shaderTexLineAlwaysVisible->BindTexture("losTex", los.GetTexture());
    242         shaderTexLineAlwaysVisible->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
     376    shaderTexLineAlwaysVisible->Bind();
     377    // TODO: losTex and losTransform are unused in the always visible shader, but I'm not sure if it's worthwhile messing
     378    // with it just to remove these calls
     379    shaderTexLineAlwaysVisible->BindTexture("losTex", los.GetTexture());
     380    shaderTexLineAlwaysVisible->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
    243381
    244         // batch render only the always-visible overlay lines using the LoS-ignored shader
    245         RenderTexturedOverlayLines(shaderTexLineAlwaysVisible, true);
     382    // batch render only the always-visible overlay lines using the LoS-ignored shader
     383    RenderTexturedOverlayLines(shaderTexLineAlwaysVisible, true);
    246384
    247         shaderTexLineAlwaysVisible->Unbind();
     385    shaderTexLineAlwaysVisible->Unbind();
    248386
    249         // TODO: the shader should probably be responsible for unbinding its textures
    250         g_Renderer.BindTexture(1, 0);
    251         g_Renderer.BindTexture(0, 0);
     387    // ----------------------------------------------------------------------------------------
    252388
    253         CVertexBuffer::Unbind();
     389    // TODO: the shader should probably be responsible for unbinding its textures
     390    g_Renderer.BindTexture(1, 0);
     391    g_Renderer.BindTexture(0, 0);
    254392
    255         glDepthMask(1);
    256         glDisable(GL_BLEND);
    257     }
     393    CVertexBuffer::Unbind();
     394
     395    glDepthMask(1);
     396    glDisable(GL_BLEND);
    258397}
    259398
    260399void OverlayRenderer::RenderTexturedOverlayLines(CShaderProgramPtr shaderTexLine, bool alwaysVisible)
    void OverlayRenderer::RenderTexturedOverlayLines(CShaderProgramPtr shaderTexLine  
    300439    }
    301440}
    302441
     442void OverlayRenderer::RenderQuadOverlays()
     443{
     444    if (m->quadBatchData.empty())
     445        return;
     446
     447    glEnable(GL_TEXTURE_2D);
     448    glEnable(GL_BLEND);
     449    glDepthMask(0);
     450
     451    const char* shaderName = "overlay_selection";
     452    // TODO: create an FFP version of this shader
     453
     454    CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
     455
     456    CShaderManager& shaderManager = g_Renderer.GetShaderManager();
     457    CShaderProgramPtr shader(shaderManager.LoadProgram(shaderName));
     458
     459    shader->Bind();
     460    shader->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
     461    shader->BindTexture("losTex", los.GetTexture());
     462
     463    // ----------------------------------------------------------------------------------------------------
     464
     465    for (OverlayRendererInternals::QuadBatchMap::iterator it = m->quadBatchData.begin(); it != m->quadBatchData.end(); it++)
     466    {
     467        QuadBatchData& batchRenderData = it->second;
     468        ENSURE(batchRenderData.m_VB == NULL || batchRenderData.m_Quads.size() > 0); // if a VB was allocated, there must be at least one quad
     469
     470        if (!batchRenderData.m_VB)
     471            continue;
     472
     473        const QuadBatchKey& maskPair = it->first;
     474
     475        shader->BindTexture("baseTex", maskPair.m_Texture->GetHandle());
     476        shader->BindTexture("maskTex", maskPair.m_TextureMask->GetHandle());
     477
     478        int streamflags = shader->GetStreamFlags();
     479
     480        GLsizei stride = sizeof(QuadBatchData::SVertex);
     481        QuadBatchData::SVertex* vertexBase = reinterpret_cast<QuadBatchData::SVertex*>(batchRenderData.m_VB->m_Owner->Bind());
     482
     483        if (streamflags & STREAM_POS)
     484            shader->VertexPointer(3, GL_FLOAT, stride, &vertexBase->m_Position[0]);
     485
     486        if (streamflags & STREAM_UV0)
     487            shader->TexCoordPointer(GL_TEXTURE0, 2, GL_SHORT, stride, &vertexBase->m_UVs[0]);
     488
     489        if (streamflags & STREAM_UV1)
     490            shader->TexCoordPointer(GL_TEXTURE1, 2, GL_SHORT, stride, &vertexBase->m_UVs[0]);
     491
     492        if (streamflags & STREAM_COLOR)
     493            shader->ColorPointer(4, GL_FLOAT, stride, &vertexBase->m_Color.r);
     494
     495        shader->AssertPointersBound();
     496       
     497        u8* indexBase = batchRenderData.m_VBIndices->m_Owner->Bind();
     498        glDrawElements(GL_TRIANGLES, (GLsizei) batchRenderData.m_VBIndices->m_Count, GL_UNSIGNED_SHORT, indexBase + sizeof(u16) * batchRenderData.m_VBIndices->m_Index);
     499
     500        g_Renderer.GetStats().m_OverlayTris += batchRenderData.m_VB->m_Count/2;
     501    }
     502
     503    // ----------------------------------------------------------------------------------------------------
     504
     505    shader->Unbind();
     506
     507    // TODO: the shader should probably be responsible for unbinding its textures
     508    g_Renderer.BindTexture(1, 0);
     509    g_Renderer.BindTexture(0, 0);
     510
     511    CVertexBuffer::Unbind();
     512
     513    glDepthMask(1);
     514    glDisable(GL_BLEND);
     515}
     516
    303517void OverlayRenderer::RenderForegroundOverlays(const CCamera& viewCamera)
    304518{
    305519    PROFILE3_GPU("overlays (fg)");
    void CTexturedLineRData::Update()  
    361575        m_VBIndices = NULL;
    362576    }
    363577
    364     CTerrain* terrain = m_Line->m_Terrain;
    365     CmpPtr<ICmpWaterManager> cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
     578    if (!m_Line->m_SimContext)
     579    {
     580        debug_warn(L"[OverlayRenderer] No SimContext set for textured overlay line, cannot render (no terrain data)");
     581        return;
     582    }
     583
     584    const CTerrain& terrain = m_Line->m_SimContext->GetTerrain();
     585    CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Line->m_SimContext, SYSTEM_ENTITY);
    366586
    367587    float v = 0.f;
    368588    std::vector<SVertex> vertices;
    void CTexturedLineRData::Update()  
    398618    // TODO: if we ever support more than one water level per map, recompute this per point
    399619    float w = cmpWaterManager->GetExactWaterLevel(p0.X, p0.Z);
    400620
    401     p0.Y = terrain->GetExactGroundLevel(p0.X, p0.Z);
     621    p0.Y = terrain.GetExactGroundLevel(p0.X, p0.Z);
    402622    if (p0.Y < w)
    403623        p0.Y = w;
    404624
    405     p1.Y = terrain->GetExactGroundLevel(p1.X, p1.Z);
     625    p1.Y = terrain.GetExactGroundLevel(p1.X, p1.Z);
    406626    if (p1.Y < w)
    407627    {
    408628        p1.Y = w;
    409629        p1floating = true;
    410630    }
    411631
    412     p2.Y = terrain->GetExactGroundLevel(p2.X, p2.Z);
     632    p2.Y = terrain.GetExactGroundLevel(p2.X, p2.Z);
    413633    if (p2.Y < w)
    414634    {
    415635        p2.Y = w;
    void CTexturedLineRData::Update()  
    426646        if (p1floating)
    427647            norm = CVector3D(0, 1, 0);
    428648        else
    429             norm = m_Line->m_Terrain->CalcExactNormal(p1.X, p1.Z);
     649            norm = terrain.CalcExactNormal(p1.X, p1.Z);
    430650
    431651        CVector3D b = ((p1 - p0).Normalized() + (p2 - p1).Normalized()).Cross(norm);
    432652
    void CTexturedLineRData::Update()  
    445665        // What the code below does is push the indices for a quad composed of two triangles in each iteration. The two triangles
    446666        // of each quad are indexed using the winding orders (BR, BL, TR) and (TR, BL, TR) (where BR is bottom-right of this
    447667        // iteration's quad, TR top-right etc).
    448         SVertex vertex1(p1 + b + norm*m_Raise, 0.f, v);
    449         SVertex vertex2(p1 - b + norm*m_Raise, 1.f, v);
     668        SVertex vertex1(p1 + b + norm*OverlayRenderer::OVERLAY_VOFFSET, 0.f, v);
     669        SVertex vertex2(p1 - b + norm*OverlayRenderer::OVERLAY_VOFFSET, 1.f, v);
    450670        vertices.push_back(vertex1);
    451671        vertices.push_back(vertex2);
    452672
    void CTexturedLineRData::Update()  
    495715        else
    496716            p2 = CVector3D(m_Line->m_Coords[((i+2) % n)*2], 0, m_Line->m_Coords[((i+2) % n)*2+1]);
    497717
    498         p2.Y = terrain->GetExactGroundLevel(p2.X, p2.Z);
     718        p2.Y = terrain.GetExactGroundLevel(p2.X, p2.Z);
    499719        if (p2.Y < w)
    500720        {
    501721            p2.Y = w;
    void CTexturedLineRData::Update()  
    538758
    539759        for (unsigned i = 0; i < capIndices.size(); i++)
    540760            capIndices[i] += vertices.size();
     761
    541762        vertices.insert(vertices.end(), capVertices.begin(), capVertices.end());
    542763        indices.insert(indices.end(), capIndices.begin(), capIndices.end());
    543764
    void CTexturedLineRData::Update()  
    558779
    559780        for (unsigned i = 0; i < capIndices.size(); i++)
    560781            capIndices[i] += vertices.size();
     782
    561783        vertices.insert(vertices.end(), capVertices.begin(), capVertices.end());
    562784        indices.insert(indices.end(), capIndices.begin(), capIndices.end());
    563785    }
    void CTexturedLineRData::CreateLineCap(const CVector3D& corner1, const CVector3D  
    698920    }
    699921
    700922}
     923
     924size_t QuadBatchKey::hash::operator()(const QuadBatchKey& d) const
     925{
     926    size_t seed = 0;
     927    boost::hash_combine(seed, d.m_Texture);
     928    boost::hash_combine(seed, d.m_TextureMask);
     929    return seed;
     930}
     931
     932bool QuadBatchKey::eq::operator()(const QuadBatchKey& d1, const QuadBatchKey& d2) const
     933{
     934    return (d1.m_Texture == d2.m_Texture
     935         && d1.m_TextureMask == d2.m_TextureMask);
     936}
     937 No newline at end of file
  • source/renderer/OverlayRenderer.h

    diff --git a/source/renderer/OverlayRenderer.h b/source/renderer/OverlayRenderer.h
    index 6c1dfdc..4e8eb7e 100644
    a b  
    2323struct SOverlayLine;
    2424struct SOverlayTexturedLine;
    2525struct SOverlaySprite;
     26struct SOverlayQuad;
    2627class CCamera;
    2728
    2829struct OverlayRendererInternals;
    public:  
    3940
    4041    /**
    4142     * Add a line overlay for rendering in this frame.
     43     * @param overlay Must be non-null.
    4244     */
    4345    void Submit(SOverlayLine* overlay);
    4446
    4547    /**
    4648     * Add a textured line overlay for rendering in this frame.
     49     * @param overlay Must be non-null.
    4750     */
    4851    void Submit(SOverlayTexturedLine* overlay);
    4952
    5053    /**
    5154     * Add a sprite overlay for rendering in this frame.
     55     * @param overlay Must be non-null.
    5256     */
    5357    void Submit(SOverlaySprite* overlay);
    5458
    5559    /**
     60     * Add a textured quad overlay for rendering in this frame.
     61     * @param overlay Must be non-null.
     62     */
     63    void Submit(SOverlayQuad* overlay);
     64
     65    /**
     66     * Add a selection ring overlay for rendering in this frame.
     67     */
     68    //void Submit(SOverlaySelectionOutline* selectionRing);
     69
     70    /**
    5671     * Prepare internal data structures for rendering.
    5772     * Must be called after all Submit calls for a frame, and before
    5873     * any rendering calls.
    public:  
    85100     */
    86101    void RenderForegroundOverlays(const CCamera& viewCamera);
    87102
     103    /// Small vertical offset of overlays from terrain to prevent visual glitches
     104    static const float OVERLAY_VOFFSET;
     105
    88106private:
    89107   
    90108    /**
     109     * Helper method; renders all overlay lines currently registered in the internals. Batch renders textured overlay lines
     110     * according to their visibility status by delegating to RenderTexturedOverlayLines(CShaderProgramPtr, bool).
     111     */
     112    void RenderTexturedOverlayLines();
     113
     114    /**
    91115     * Helper method; renders those overlay lines currently registered in the internals (i.e. in m->texlines) for which the
    92116     * always visible flag equals @alwaysVisible. Used for batch rendering the overlay lines by their alwaysVisible status,
    93117     * because this requires a separate shader to be used.
    94118     */
    95119    void RenderTexturedOverlayLines(CShaderProgramPtr shader, bool alwaysVisible);
    96120
     121    /**
     122     * Helper method; batch-renders all registered quad overlays according to their texture.
     123     */
     124    void RenderQuadOverlays();
     125
    97126private:
    98127    OverlayRendererInternals* m;
    99128};
  • source/renderer/Renderer.cpp

    diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp
    index 7dfce3c..13f7aeb 100644
    a b void CRenderer::Submit(SOverlaySprite* overlay)  
    17291729    m->overlayRenderer.Submit(overlay);
    17301730}
    17311731
     1732void CRenderer::Submit(SOverlayQuad* overlay)
     1733{
     1734    m->overlayRenderer.Submit(overlay);
     1735}
     1736
     1737/*void CRenderer::Submit(SOverlaySelectionOutline* overlay)
     1738{
     1739    m->overlayRenderer.Submit(overlay);
     1740}*/
     1741
    17321742void CRenderer::Submit(CModelDecal* decal)
    17331743{
    17341744    m->terrainRenderer->Submit(decal);
  • source/renderer/Renderer.h

    diff --git a/source/renderer/Renderer.h b/source/renderer/Renderer.h
    index 74e6d46..239efe1 100644
    a b protected:  
    325325    void Submit(SOverlayLine* overlay);
    326326    void Submit(SOverlayTexturedLine* overlay);
    327327    void Submit(SOverlaySprite* overlay);
     328    void Submit(SOverlayQuad* overlay);
    328329    void Submit(CModelDecal* decal);
    329330    void Submit(CParticleEmitter* emitter);
    330331    void SubmitNonRecursive(CModel* model);
  • source/renderer/Scene.h

    diff --git a/source/renderer/Scene.h b/source/renderer/Scene.h
    index 2018135..99c99f7 100644
    a b class CTerritoryTexture;  
    3939struct SOverlayLine;
    4040struct SOverlayTexturedLine;
    4141struct SOverlaySprite;
     42struct SOverlayQuad;
    4243
    4344class SceneCollector;
    4445
    public:  
    104105    virtual void Submit(SOverlaySprite* overlay) = 0;
    105106
    106107    /**
     108     * Submit a textured quad overlay.
     109     */
     110    virtual void Submit(SOverlayQuad* overlay) = 0;
     111
     112    /**
    107113     * Submit a terrain decal.
    108114     */
    109115    virtual void Submit(CModelDecal* decal) = 0;
  • source/renderer/VertexBuffer.cpp

    diff --git a/source/renderer/VertexBuffer.cpp b/source/renderer/VertexBuffer.cpp
    index bcbfebe..84fce45 100644
    a b CVertexBuffer::CVertexBuffer(size_t vertexSize, GLenum usage, GLenum target)  
    3333{
    3434    size_t size = MAX_VB_SIZE_BYTES;
    3535
    36     if (target == GL_ARRAY_BUFFER)
     36    if (target == GL_ARRAY_BUFFER) // vertex data buffer
    3737    {
    3838        // We want to store 16-bit indices to any vertex in a buffer, so the
    39         // buffer must never be bigger than vertexSize*64K bytes
     39        // buffer must never be bigger than vertexSize*64K bytes since we can
     40        // address at most 64K of them with 16-bit indices
    4041        size = std::min(size, vertexSize*65536);
    4142    }
    4243
    CVertexBuffer::CVertexBuffer(size_t vertexSize, GLenum usage, GLenum target)  
    5455    }
    5556
    5657    // store max/free vertex counts
    57     m_MaxVertices=m_FreeVertices=size/vertexSize;
     58    m_MaxVertices = m_FreeVertices = size/vertexSize;
    5859   
    5960    // create sole free chunk
    60     VBChunk* chunk=new VBChunk;
    61     chunk->m_Owner=this;
    62     chunk->m_Count=m_FreeVertices;
    63     chunk->m_Index=0;
     61    VBChunk* chunk = new VBChunk;
     62    chunk->m_Owner = this;
     63    chunk->m_Count = m_FreeVertices;
     64    chunk->m_Index = 0;
    6465    m_FreeList.push_front(chunk);
    6566}
    6667
    CVertexBuffer::VBChunk* CVertexBuffer::Allocate(size_t vertexSize, size_t numVer  
    100101        return 0;
    101102
    102103    // trawl free list looking for first free chunk with enough space
    103     VBChunk* chunk=0;
     104    VBChunk* chunk = 0;
    104105    typedef std::list<VBChunk*>::iterator Iter;
    105     for (Iter iter=m_FreeList.begin();iter!=m_FreeList.end();++iter) {
    106         if (numVertices<=(*iter)->m_Count) {
    107             chunk=*iter;
     106    for (Iter iter = m_FreeList.begin(); iter != m_FreeList.end(); ++iter) {
     107        if (numVertices <= (*iter)->m_Count) {
     108            chunk = *iter;
    108109            // remove this chunk from the free list
    109110            m_FreeList.erase(iter);
    110111            m_FreeVertices -= chunk->m_Count;
    void CVertexBuffer::Release(VBChunk* chunk)  
    173174
    174175///////////////////////////////////////////////////////////////////////////////
    175176// UpdateChunkVertices: update vertex data for given chunk
    176 void CVertexBuffer::UpdateChunkVertices(VBChunk* chunk,void* data)
     177void CVertexBuffer::UpdateChunkVertices(VBChunk* chunk, void* data, unsigned int cnt /* = 0*/)
    177178{
    178179    if (g_Renderer.m_Caps.m_VBO)
    179180    {
  • source/renderer/VertexBuffer.h

    diff --git a/source/renderer/VertexBuffer.h b/source/renderer/VertexBuffer.h
    index 809ac65..a4579e2 100644
    a b  
    3030// absolute maximum (bytewise) size of each GL vertex buffer object
    3131#define MAX_VB_SIZE_BYTES       (512*1024)
    3232
    33 ///////////////////////////////////////////////////////////////////////////////
    34 // CVertexBuffer: encapsulation of ARB_vertex_buffer_object, also supplying
    35 // some additional functionality for sharing buffers between multiple objects
     33/**
     34 * CVertexBuffer: encapsulation of ARB_vertex_buffer_object, also supplying
     35 * some additional functionality for sharing buffers between multiple objects
     36 */
    3637class CVertexBuffer
    3738{
    3839public:
    39     // VBChunk: describes a portion of this vertex buffer
     40
     41    /// VBChunk: describes a portion of this vertex buffer
    4042    struct VBChunk
    4143    {
    42         // owning buffer
     44        /// Owning (parent) vertex buffer
    4345        CVertexBuffer* m_Owner;
    44         // start index of this chunk in owner
     46        /// Start index of this chunk in owner
    4547        size_t m_Index;
    46         // number of vertices used by chunk
     48        /// Number of vertices used by chunk
    4749        size_t m_Count;
    4850
    4951    private:
    public:  
    5961    CVertexBuffer(size_t vertexSize, GLenum usage, GLenum target);
    6062    ~CVertexBuffer();
    6163
    62     // bind to this buffer; return pointer to address required as parameter
    63     // to glVertexPointer ( + etc) calls
     64    /// Bind to this buffer; return pointer to address required as parameter
     65    /// to glVertexPointer ( + etc) calls
    6466    u8* Bind();
    6567
    66     // get the address that Bind() will return, without actually binding
     68    /// Get the address that Bind() will return, without actually binding
    6769    u8* GetBindAddress();
    6870
    69     // unbind any currently-bound buffer, so glVertexPointer etc calls will not attempt to use it
     71    /// Unbind any currently-bound buffer, so glVertexPointer etc calls will not attempt to use it
    7072    static void Unbind();
    7173
    72     // update vertex data for given chunk
    73     void UpdateChunkVertices(VBChunk* chunk, void* data);
     74    /// Update vertex data for given chunk. Transfers the provided data to the actual OpenGL vertex buffer.
     75    void UpdateChunkVertices(VBChunk* chunk, void* data, unsigned int cnt = 0);
    7476
    7577    size_t GetVertexSize() const { return m_VertexSize; }
    76 
    7778    size_t GetBytesReserved() const;
    7879    size_t GetBytesAllocated() const;
    7980
     81    /// Returns true if this vertex buffer is compatible with the specified vertex type and intended usage.
    8082    bool CompatibleVertexType(size_t vertexSize, GLenum usage, GLenum target);
    8183
    8284    void DumpStatus();
    public:  
    8486protected:
    8587    friend class CVertexBufferManager;      // allow allocate only via CVertexBufferManager
    8688   
    87     // try to allocate a buffer of given number of vertices (each of given size),
    88     // and with the given type - return null if no free chunks available
     89    /// Try to allocate a buffer of given number of vertices (each of given size),
     90    /// and with the given type - return null if no free chunks available
    8991    VBChunk* Allocate(size_t vertexSize, size_t numVertices, GLenum usage, GLenum target);
    90     // return given chunk to this buffer
     92    /// Return given chunk to this buffer
    9193    void Release(VBChunk* chunk);
    9294   
    9395   
    9496private:   
    95     // vertex size of this vertex buffer
     97    /// Vertex size of this vertex buffer
    9698    size_t m_VertexSize;
    97     // number of vertices of above size in this buffer
     99    /// Number of vertices of above size in this buffer
    98100    size_t m_MaxVertices;
    99     // list of free chunks in this buffer
     101    /// List of free chunks in this buffer
    100102    std::list<VBChunk*> m_FreeList;
    101     // available free vertices - total of all free vertices in the free list
     103    /// Available free vertices - total of all free vertices in the free list
    102104    size_t m_FreeVertices;
    103     // handle to the actual GL vertex buffer object
     105    /// Handle to the actual GL vertex buffer object
    104106    GLuint m_Handle;
    105     // raw system memory for systems not supporting VBOs
     107    /// Raw system memory for systems not supporting VBOs
    106108    u8* m_SysMem;
    107     // usage type of the buffer (GL_STATIC_DRAW etc)
     109    /// Usage type of the buffer (GL_STATIC_DRAW etc)
    108110    GLenum m_Usage;
    109     // buffer target (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER)
     111    /// Buffer target (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER)
    110112    GLenum m_Target;
    111113};
    112114
  • source/renderer/VertexBufferManager.h

    diff --git a/source/renderer/VertexBufferManager.h b/source/renderer/VertexBufferManager.h
    index b97c68d..568e96a 100644
    a b  
    3030class CVertexBufferManager
    3131{
    3232public:
    33     // Explicit shutdown of the vertex buffer subsystem
    34     void Shutdown();
    3533   
    3634    /**
    3735     * Try to allocate a vertex buffer of the given size and type.
    public:  
    4442     */
    4543    CVertexBuffer::VBChunk* Allocate(size_t vertexSize, size_t numVertices, GLenum usage, GLenum target);
    4644
    47     // return given chunk to its owner
     45    /// Returns the given @p chunk to its owning buffer
    4846    void Release(CVertexBuffer::VBChunk* chunk);
    4947
    50     // return list of all buffers
     48    /// Returns a list of all buffers
    5149    const std::list<CVertexBuffer*>& GetBufferList() const { return m_Buffers; }
    5250
    5351    size_t GetBytesReserved();
    5452    size_t GetBytesAllocated();
    5553
     54    /// Returns the maximum possible size of a single vertex buffer
     55    size_t GetMaxBufferSize() const { return MAX_VB_SIZE_BYTES; }
     56
     57    /// Explicit shutdown of the vertex buffer subsystem; releases all currently-allocated buffers.
     58    void Shutdown();
     59
    5660private:
    57     // list of all known vertex buffers
     61    /// List of all known vertex buffers
    5862    std::list<CVertexBuffer*> m_Buffers;
    5963};
    6064
  • source/simulation2/components/CCmpRallyPointRenderer.cpp

    diff --git a/source/simulation2/components/CCmpRallyPointRenderer.cpp b/source/simulation2/components/CCmpRallyPointRenderer.cpp
    index 78d9730..06d18e3 100644
    a b void CCmpRallyPointRenderer::ConstructOverlayLines()  
    600600            // construct solid textured overlay line along a subset of the full path points from startPointIdx to endPointIdx
    601601            SOverlayTexturedLine overlayLine;
    602602            overlayLine.m_Thickness = m_LineThickness;
    603             overlayLine.m_Terrain = cmpTerrain->GetCTerrain();
     603            overlayLine.m_SimContext = &GetSimContext();
    604604            overlayLine.m_TextureBase = m_Texture;
    605605            overlayLine.m_TextureMask = m_TextureMask;
    606606            overlayLine.m_Color = m_LineColor;
    void CCmpRallyPointRenderer::ConstructOverlayLines()  
    624624        }
    625625        else
    626626        {
    627             // construct dashed line from startPointIdx to endPointIdx, add textured overlay lines for it to the render list
     627            // construct dashed line from startPointIdx to endPointIdx; add textured overlay lines for it to the render list
    628628            std::vector<CVector2D> straightLine;
    629629            straightLine.push_back(m_Path[segment.m_StartIndex]);
    630630            straightLine.push_back(m_Path[segment.m_EndIndex]);
    631631
    632             // We always want to have the dashed line end at either point with a full dash (i.e. not a cleared space), so that the dashed
    633             // area is visually obvious. That implies that we want at least So, let's do some calculations to see what size we should make
    634             // the dashes and clears.
     632            // We always want to the dashed line to end at either point with a full dash (i.e. not a cleared space), so that the dashed
     633            // area is visually obvious. This requires some calculations to see what size we should make the dashes and clears for them
     634            // to fit exactly.
    635635
    636636            float maxDashSize = 3.f;
    637637            float maxClearSize = 3.f;
    void CCmpRallyPointRenderer::ConstructOverlayLines()  
    642642
    643643            float distance = (m_Path[segment.m_StartIndex] - m_Path[segment.m_EndIndex]).Length(); // straight-line distance between the points
    644644
    645             // see how many pairs (dash + clear) can fit into the distance unmodified. Then check the remaining distance; if it's not exactly
    646             // a dash size's worth (and it likely won't be), then adjust the dash/clear sizes slightly so that it is.
     645            // See how many pairs (dash + clear) of unmodified size can fit into the distance. Then check the remaining distance; if it's not exactly
     646            // a dash size's worth (which it probably won't be), then adjust the dash/clear sizes slightly so that it is.
    647647            int numFitUnmodified = floor(distance/(dashSize + clearSize));
    648648            float remainderDistance = distance - (numFitUnmodified * (dashSize + clearSize));
    649649
    void CCmpRallyPointRenderer::ConstructOverlayLines()  
    672672                SOverlayTexturedLine dashOverlay;
    673673
    674674                dashOverlay.m_Thickness = m_LineThickness;
    675                 dashOverlay.m_Terrain = cmpTerrain->GetCTerrain();
     675                dashOverlay.m_SimContext = &GetSimContext();
    676676                dashOverlay.m_TextureBase = m_Texture;
    677677                dashOverlay.m_TextureMask = m_TextureMask;
    678678                dashOverlay.m_Color = m_LineDashColor;
    void CCmpRallyPointRenderer::FixFootprintWaypoints(std::vector<CVector2D>& coord  
    757757    {
    758758    case ICmpFootprint::SQUARE:
    759759        {
    760             // in this case, footprintSize0 and 1 respectively indicate the (unrotated) size along the X and Z axes
     760            // in this case, footprintSize0 and 1 indicate the size along the X and Z axes, respectively.
    761761
    762762            // the building's footprint could be rotated any which way, so let's get the rotation around the Y axis
    763763            // and the rotated unit vectors in the X/Z plane of the shape's footprint
    void CCmpRallyPointRenderer::FixInvisibleWaypoints(std::vector<CVector2D>& coord  
    827827    player_id_t currentPlayer = GetSimContext().GetCurrentDisplayedPlayer();
    828828    CLosQuerier losQuerier(cmpRangeMgr->GetLosQuerier(currentPlayer));
    829829
    830     //for (std::vector<Waypoint>::iterator it = waypoints.begin(); it != waypoints.end();)
    831830    for(std::vector<CVector2D>::iterator it = coords.begin(); it != coords.end();)
    832831    {
    833832        int i = (fixed::FromFloat(it->X) / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNearest();
  • source/simulation2/components/CCmpSelectable.cpp

    diff --git a/source/simulation2/components/CCmpSelectable.cpp b/source/simulation2/components/CCmpSelectable.cpp
    index d23916f..c3593da 100644
    a b  
    2020#include "simulation2/system/Component.h"
    2121#include "ICmpSelectable.h"
    2222
    23 #include "ICmpPosition.h"
    24 #include "ICmpFootprint.h"
    25 #include "ICmpVisual.h"
    26 #include "simulation2/MessageTypes.h"
    27 #include "simulation2/helpers/Render.h"
    28 
    2923#include "graphics/Overlay.h"
     24#include "graphics/Terrain.h"
     25#include "graphics/TextureManager.h"
    3026#include "maths/MathUtil.h"
    3127#include "maths/Matrix3D.h"
    3228#include "maths/Vector3D.h"
     29#include "maths/Vector2D.h"
     30#include "ps/CLogger.h"
    3331#include "renderer/Scene.h"
     32#include "renderer/Renderer.h"
     33#include "simulation2/MessageTypes.h"
     34#include "simulation2/components/ICmpPosition.h"
     35#include "simulation2/components/ICmpFootprint.h"
     36#include "simulation2/components/ICmpVisual.h"
     37#include "simulation2/components/ICmpTerrain.h"
     38#include "simulation2/components/ICmpOwnership.h"
     39#include "simulation2/components/ICmpPlayer.h"
     40#include "simulation2/components/ICmpPlayerManager.h"
     41#include "simulation2/components/ICmpWaterManager.h"
     42#include "simulation2/helpers/Render.h"
    3443
    3544class CCmpSelectable : public ICmpSelectable
    3645{
    public:  
    3948    {
    4049        componentManager.SubscribeToMessageType(MT_Interpolate);
    4150        componentManager.SubscribeToMessageType(MT_RenderSubmit);
     51        // these next two are primarily for invalidating the static building outlines; not really applicable for unit
     52        // overlays since their positions and ownerships etc. are gathered every frame
     53        componentManager.SubscribeToMessageType(MT_OwnershipChanged);
     54        componentManager.SubscribeToMessageType(MT_PositionChanged);
    4255        // TODO: it'd be nice if we didn't get these messages except in the rare
    4356        // cases where we're actually drawing a selection highlight
    4457    }
    4558
    4659    DEFAULT_COMPONENT_ALLOCATOR(Selectable)
    4760
    48     SOverlayLine m_Overlay;
    49     SOverlayLine* m_DebugBoundingBoxOverlay;
    50     SOverlayLine* m_DebugSelectionBoxOverlay;
    51     bool m_EditorOnly;
    52 
    5361    CCmpSelectable()
    54         : m_DebugBoundingBoxOverlay(NULL), m_DebugSelectionBoxOverlay(NULL)
     62        : m_DebugBoundingBoxOverlay(NULL), m_DebugSelectionBoxOverlay(NULL),
     63          m_AlphaTarget(0.f), m_BuildingOverlay(NULL), m_UnitOverlay(NULL)
    5564    {
    56         m_Overlay.m_Thickness = 2;
    57         m_Overlay.m_Color = CColor(0, 0, 0, 0);
     65        m_Color = CColor(0, 0, 0, m_AlphaTarget);
    5866    }
    5967
    60     ~CCmpSelectable(){
     68    ~CCmpSelectable()
     69    {
    6170        delete m_DebugBoundingBoxOverlay;
    6271        delete m_DebugSelectionBoxOverlay;
     72        delete m_BuildingOverlay;
     73        delete m_UnitOverlay;
    6374    }
    6475
    6576    static std::string GetSchema()
    public:  
    7182                "<element name='EditorOnly' a:help='If this element is present, the entity is only selectable in Atlas'>"
    7283                    "<empty/>"
    7384                "</element>"
    74             "</optional>";
     85            "</optional>"
     86            "<element name='Overlay' a:help='Specifies the type of overlay to be displayed when this entity is selected'>"
     87                "<choice>"
     88                    "<element name='Texture' a:help='Displays a texture underneath the entity'>"
     89                        "<element name='MainTexture'><text/></element>"
     90                        "<element name='MainTextureMask'><text/></element>"
     91                    "</element>"
     92                    "<element name='Outline' a:help='Traces the outline of the entity with a line texture'>"
     93                        "<element name='LineTexture'><text/></element>"
     94                        "<element name='LineTextureMask'><text/></element>"
     95                        "<element name='LineThickness'><ref name='positiveDecimal'/></element>"
     96                    "</element>"
     97                "</choice>"
     98            "</element>";
    7599    }
    76100
    77101    virtual void Init(const CParamNode& paramNode)
    78102    {
    79103        m_EditorOnly = paramNode.GetChild("EditorOnly").IsOk();
    80     }
    81104
    82     virtual void Deinit()
    83     {
     105        const CParamNode& textureNode = paramNode.GetChild("Overlay").GetChild("Texture");
     106        const CParamNode& outlineNode = paramNode.GetChild("Overlay").GetChild("Outline");
     107
     108        // TODO: we might be able to save some memory by de-duplicating these descriptors (particularly if actors
     109        // also end up having Selectable components -- see CCmpTemplateManager::ConstructTemplateActor)
     110
     111        if (textureNode.IsOk())
     112        {
     113            // textured quad mode
     114            m_OverlayDescriptor.m_Type = ICmpSelectable::DYNAMIC_QUAD;
     115            m_OverlayDescriptor.m_QuadTexture = textureNode.GetChild("MainTexture").ToString();
     116            m_OverlayDescriptor.m_QuadTextureMask = textureNode.GetChild("MainTextureMask").ToString();
     117        }
     118        else if (outlineNode.IsOk())
     119        {
     120            // outline mode
     121            m_OverlayDescriptor.m_Type = ICmpSelectable::STATIC_OUTLINE;
     122            m_OverlayDescriptor.m_LineTexture = outlineNode.GetChild("LineTexture").ToString();
     123            m_OverlayDescriptor.m_LineTextureMask = outlineNode.GetChild("LineTextureMask").ToString();
     124            m_OverlayDescriptor.m_LineThickness = outlineNode.GetChild("LineThickness").ToFixed().ToFloat();
     125        }
    84126    }
    85127
     128    virtual void Deinit() { }
     129
    86130    virtual void Serialize(ISerializer& UNUSED(serialize))
    87131    {
    88132        // Nothing to do here (the overlay object is not worth saving, it'll get
    public:  
    95139        Init(paramNode);
    96140    }
    97141
    98     virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
     142    virtual void HandleMessage(const CMessage& msg, bool UNUSED(global));
     143
     144    virtual void SetSelectionHighlight(CColor color)
    99145    {
    100         switch (msg.GetType())
    101         {
    102         case MT_Interpolate:
     146        m_Color.r = color.r;
     147        m_Color.g = color.g;
     148        m_Color.b = color.b;
     149        m_AlphaTarget = color.a; // set target for alpha fading
     150    }
     151
     152    virtual bool IsEditorOnly()
     153    {
     154        return m_EditorOnly;
     155    }
     156
     157    void RenderSubmit(SceneCollector& collector);
     158
     159    void UpdateStaticOverlay();
     160    void UpdateDynamicOverlay(float frameOffset);
     161
     162    void InvalidateStaticOverlay();
     163
     164private:
     165    SOverlayDescriptor m_OverlayDescriptor;
     166    SOverlayTexturedLine* m_BuildingOverlay;
     167    SOverlayQuad* m_UnitOverlay;
     168
     169    SOverlayLine* m_DebugBoundingBoxOverlay;
     170    SOverlayLine* m_DebugSelectionBoxOverlay;
     171
     172    bool m_EditorOnly;
     173    CColor m_Color; ///< Current selection overlay color (including alpha component for fading)
     174    float m_AlphaTarget; // fade target
     175};
     176
     177void CCmpSelectable::HandleMessage(const CMessage& msg, bool UNUSED(global))
     178{
     179    switch (msg.GetType())
     180    {
     181    case MT_Interpolate:
    103182        {
    104             if (m_Overlay.m_Color.a > 0)
     183            // update dynamic overlay only when visible
     184            if (m_Color.a > 0)
    105185            {
    106                 float offset = static_cast<const CMessageInterpolate&> (msg).offset;
    107                 ConstructShape(offset);
     186                const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg);
     187                UpdateDynamicOverlay(msgData.offset);
    108188            }
    109189            break;
    110190        }
    111         case MT_RenderSubmit:
     191    case MT_OwnershipChanged:
    112192        {
    113             if (m_Overlay.m_Color.a > 0)
     193            const CMessageOwnershipChanged& msgData = static_cast<const CMessageOwnershipChanged&> (msg);
     194
     195            // don't update color if there's no new owner (e.g. the unit died)
     196            if (msgData.to == INVALID_PLAYER)
     197                break;
     198
     199            // update the selection highlight color
     200            // TODO: very similar to CCmpMinimap's MT_OwnershipChanged handler, consider factoring this out
     201            CmpPtr<ICmpPlayerManager> cmpPlayerManager(GetSimContext(), SYSTEM_ENTITY);
     202            if (!cmpPlayerManager)
     203                break;
     204
     205            CmpPtr<ICmpPlayer> cmpPlayer(GetSimContext(), cmpPlayerManager->GetPlayerByID(msgData.to));
     206            if (!cmpPlayer)
     207                break;
     208
     209            CColor color = cmpPlayer->GetColour();
     210            SetSelectionHighlight(CColor(color.r, color.g, color.b, m_AlphaTarget)); // keep current alpha target, change only RGB
     211        }
     212        // fall-through
     213    case MT_PositionChanged:
     214        {
     215            InvalidateStaticOverlay();
     216            break;
     217        }
     218    case MT_RenderSubmit:
     219        {
     220            // fade overlay alpha to target alpha
     221            const float fadeSpeed = 0.3f; // ]0,1[, higher = faster
     222
     223            if (m_Color.a != m_AlphaTarget) // should be cheap enough to perform every frame for every selectable while also quickly eliminating many hidden overlays
    114224            {
    115                 const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg);
    116                 RenderSubmit(msgData.collector);
     225                float targetDiff = m_AlphaTarget - m_Color.a;
     226                if (fabs(targetDiff) > 0.001f)
     227                {
     228                    m_Color.a += targetDiff * fadeSpeed; // apply a fraction of the remainder distance each frame
     229                }
     230                else
     231                {
     232                    // snap to the target value so we don't keep rendering because the alpha is like 0.000005 or sth when the
     233                    // target alpha is set to 0 to hide the overlay
     234                    m_Color.a = m_AlphaTarget;
     235                }
    117236            }
     237
     238            const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg);
     239            RenderSubmit(msgData.collector);
     240
    118241            break;
    119242        }
    120         }
    121243    }
     244}
    122245
    123     virtual bool IsEditorOnly()
     246void CCmpSelectable::InvalidateStaticOverlay()
     247{
     248    SAFE_DELETE(m_BuildingOverlay);
     249}
     250
     251void CCmpSelectable::UpdateStaticOverlay()
     252{
     253    if (m_BuildingOverlay || m_OverlayDescriptor.m_Type != STATIC_OUTLINE)
     254        return;
     255   
     256    entity_id_t entityId = GetEntityId();
     257    CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), entityId);
     258    CmpPtr<ICmpFootprint> cmpFootprint(GetSimContext(), entityId);
     259    CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), entityId);
     260    if (!cmpFootprint || !cmpPosition)
     261        return;
     262
     263    CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY);
     264    CmpPtr<ICmpPlayerManager> cmpPlayerManager(GetSimContext(), SYSTEM_ENTITY);
     265    if (!cmpTerrain)
     266        return; // should never happen
     267
     268    // grab position/footprint data
     269    CFixedVector2D position = cmpPosition->GetPosition2D();
     270    CFixedVector3D rotation = cmpPosition->GetRotation();
     271
     272    ICmpFootprint::EShape fpShape;
     273    entity_pos_t fpSize0_fixed, fpSize1_fixed, fpHeight_fixed;
     274    cmpFootprint->GetShape(fpShape, fpSize0_fixed, fpSize1_fixed, fpHeight_fixed);
     275
     276    CTextureProperties texturePropsBase(m_OverlayDescriptor.m_LineTexture);
     277    texturePropsBase.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE);
     278    texturePropsBase.SetMaxAnisotropy(4.f);
     279
     280    CTextureProperties texturePropsMask(m_OverlayDescriptor.m_LineTextureMask);
     281    texturePropsMask.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE);
     282    texturePropsMask.SetMaxAnisotropy(4.f);
     283
     284    // -------------------------------------------------------------------------------------
     285
     286    m_BuildingOverlay = new SOverlayTexturedLine;
     287    m_BuildingOverlay->m_AlwaysVisible = false;
     288    m_BuildingOverlay->m_Closed = true;
     289    m_BuildingOverlay->m_SimContext = &GetSimContext();
     290    m_BuildingOverlay->m_Thickness = m_OverlayDescriptor.m_LineThickness;
     291
     292    if (g_Renderer.IsInitialised())
    124293    {
    125         return m_EditorOnly;
     294        m_BuildingOverlay->m_TextureBase = g_Renderer.GetTextureManager().CreateTexture(texturePropsBase);
     295        m_BuildingOverlay->m_TextureMask = g_Renderer.GetTextureManager().CreateTexture(texturePropsMask);
    126296    }
    127 
    128     virtual void SetSelectionHighlight(CColor color)
     297    else
    129298    {
    130         m_Overlay.m_Color = color;
     299        LOGWARNING(L"[CCmpSelectable] Cannot update static selection overlay; renderer not initialized");
     300    }
     301
     302    CVector2D origin(position.X.ToFloat(), position.Y.ToFloat());
    131303
    132         if (color.a == 0 && !m_Overlay.m_Coords.empty())
     304    switch (fpShape)
     305    {
     306    case ICmpFootprint::SQUARE:
    133307        {
    134             // Delete the overlay data to save memory (we don't want hundreds of bytes
    135             // times thousands of units when the selections are not being rendered any more)
    136             std::vector<float> empty;
    137             m_Overlay.m_Coords.swap(empty);
    138             ENSURE(m_Overlay.m_Coords.capacity() == 0);
     308            float s = sinf(-rotation.Y.ToFloat());
     309            float c = cosf(-rotation.Y.ToFloat());
     310            CVector2D unitX(c, s);
     311            CVector2D unitZ(-s, c);
     312
     313            // add half the line thickness to the radius so that we get an 'outside' stroke of the footprint shape
     314            const float halfSizeX = fpSize0_fixed.ToFloat()/2.f + m_BuildingOverlay->m_Thickness/2.f;
     315            const float halfSizeZ = fpSize1_fixed.ToFloat()/2.f + m_BuildingOverlay->m_Thickness/2.f;
     316
     317            std::vector<CVector2D> points;
     318            points.push_back(CVector2D(origin + unitX *  halfSizeX    + unitZ *(-halfSizeZ)));
     319            points.push_back(CVector2D(origin + unitX *(-halfSizeX)   + unitZ *(-halfSizeZ)));
     320            points.push_back(CVector2D(origin + unitX *(-halfSizeX)   + unitZ *  halfSizeZ));
     321            points.push_back(CVector2D(origin + unitX *  halfSizeX    + unitZ *  halfSizeZ));
     322
     323            SimRender::SubdividePoints(points, TERRAIN_TILE_SIZE/3.f, m_BuildingOverlay->m_Closed);
     324            m_BuildingOverlay->PushCoords(points);
    139325        }
     326        break;
     327    case ICmpFootprint::CIRCLE:
     328        {
     329            const float radius = fpSize0_fixed.ToFloat() + m_BuildingOverlay->m_Thickness/3.f;
     330            if (radius > 0) // prevent catastrophic failure
     331            {
     332                float stepAngle;
     333                unsigned numSteps;
     334                SimRender::AngularStepFromChordLen(TERRAIN_TILE_SIZE/3.f, radius, stepAngle, numSteps);
    140335
    141         // TODO: it'd be nice to fade smoothly (but quickly) from transparent to solid
     336                for (unsigned i = 0; i < numSteps; i++) // '<' is sufficient because the line is closed automatically
     337                {
     338                    float angle = i * stepAngle;
     339                    float px = origin.X + radius * sinf(angle);
     340                    float pz = origin.Y + radius * cosf(angle);
     341
     342                    m_BuildingOverlay->PushCoords(px, pz);
     343                }
     344            }
     345        }
     346        break;
    142347    }
    143348
    144     void ConstructShape(float frameOffset)
     349    ENSURE(m_BuildingOverlay);
     350}
     351
     352void CCmpSelectable::UpdateDynamicOverlay(float frameOffset)
     353{
     354    if (m_OverlayDescriptor.m_Type != DYNAMIC_QUAD)
     355        return;
     356
     357    entity_id_t entityId = GetEntityId();
     358    CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), entityId);
     359    CmpPtr<ICmpFootprint> cmpFootprint(GetSimContext(), entityId);
     360    CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), entityId);
     361    if (!cmpFootprint || !cmpPosition || !cmpPosition->IsInWorld())
     362        return;
     363
     364    float rotY;
     365    CVector2D position;
     366    cmpPosition->GetInterpolatedPosition2D(frameOffset, position.X, position.Y, rotY);
     367
     368    CmpPtr<ICmpWaterManager> cmpWaterManager(GetSimContext(), SYSTEM_ENTITY);
     369    CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY);
     370    ENSURE(cmpWaterManager && cmpTerrain);
     371
     372    CTerrain* terrain = cmpTerrain->GetCTerrain();
     373    ENSURE(terrain);
     374
     375    // ---------------------------------------------------------------------------------
     376
     377    CTextureProperties texturePropsBase(m_OverlayDescriptor.m_QuadTexture);
     378    texturePropsBase.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE);
     379    texturePropsBase.SetMaxAnisotropy(4.f);
     380
     381    CTextureProperties texturePropsMask(m_OverlayDescriptor.m_QuadTextureMask);
     382    texturePropsMask.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE);
     383    texturePropsMask.SetMaxAnisotropy(4.f);
     384
     385    ICmpFootprint::EShape fpShape;
     386    entity_pos_t fpSize0_fixed, fpSize1_fixed, fpHeight_fixed;
     387    cmpFootprint->GetShape(fpShape, fpSize0_fixed, fpSize1_fixed, fpHeight_fixed);
     388
     389    // ---------------------------------------------------------------------------------
     390
     391    if (!m_UnitOverlay)
     392        m_UnitOverlay = new SOverlayQuad;
     393
     394    m_UnitOverlay->m_Texture = g_Renderer.GetTextureManager().CreateTexture(texturePropsBase);
     395    m_UnitOverlay->m_TextureMask = g_Renderer.GetTextureManager().CreateTexture(texturePropsMask);
     396    m_UnitOverlay->m_Color = m_Color;
     397
     398    // TODO: some code duplication here :< would be nice to factor out getting the corner points of an entity based on its footprint
     399    // sizes (and regardless of whether it's a circle or a square)
     400
     401    float s = sinf(-rotY);
     402    float c = cosf(-rotY);
     403    CVector2D unitX(c, s);
     404    CVector2D unitZ(-s, c);
     405
     406    float halfSizeX = fpSize0_fixed.ToFloat();
     407    float halfSizeZ = fpSize1_fixed.ToFloat();
     408    if (fpShape == ICmpFootprint::SQUARE)
    145409    {
    146         CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId());
    147         if (!cmpPosition)
    148             return;
     410        halfSizeX /= 2.0f;
     411        halfSizeZ /= 2.0f;
     412    }
    149413
    150         if (!cmpPosition->IsInWorld())
    151             return;
     414    std::vector<CVector2D> points;
     415    points.push_back(CVector2D(position + unitX *(-halfSizeX)   + unitZ *  halfSizeZ));  // top left
     416    points.push_back(CVector2D(position + unitX *(-halfSizeX)   + unitZ *(-halfSizeZ))); // bottom left
     417    points.push_back(CVector2D(position + unitX *  halfSizeX    + unitZ *(-halfSizeZ))); // bottom right
     418    points.push_back(CVector2D(position + unitX *  halfSizeX    + unitZ *  halfSizeZ));  // top right
    152419
    153         float x, z, rotY;
    154         cmpPosition->GetInterpolatedPosition2D(frameOffset, x, z, rotY);
     420    for (int i=0; i < 4; i++)
     421    {
     422        float quadY = std::max(
     423            terrain->GetExactGroundLevel(points[i].X, points[i].Y),
     424            cmpWaterManager->GetExactWaterLevel(points[i].X, points[i].Y)
     425        );
    155426
    156         CmpPtr<ICmpFootprint> cmpFootprint(GetSimContext(), GetEntityId());
    157         if (!cmpFootprint)
    158         {
    159             // Default (this probably shouldn't happen) - just render an arbitrary-sized circle
    160             SimRender::ConstructCircleOnGround(GetSimContext(), x, z, 2.f, m_Overlay, cmpPosition->IsFloating());
    161         }
    162         else
     427        m_UnitOverlay->m_Corners[i] = CVector3D(points[i].X, quadY, points[i].Y);
     428    }
     429}
     430
     431void CCmpSelectable::RenderSubmit(SceneCollector& collector)
     432{
     433    // don't render selection overlay if it's not gonna be visible
     434    if (m_Color.a > 0)
     435    {
     436        switch (m_OverlayDescriptor.m_Type)
    163437        {
    164             ICmpFootprint::EShape shape;
    165             entity_pos_t size0, size1, height;
    166             cmpFootprint->GetShape(shape, size0, size1, height);
    167 
    168             if (shape == ICmpFootprint::SQUARE)
    169                 SimRender::ConstructSquareOnGround(GetSimContext(), x, z, size0.ToFloat(), size1.ToFloat(), rotY, m_Overlay, cmpPosition->IsFloating());
    170             else
    171                 SimRender::ConstructCircleOnGround(GetSimContext(), x, z, size0.ToFloat(), m_Overlay, cmpPosition->IsFloating());
     438            case STATIC_OUTLINE:
     439                {
     440                    UpdateStaticOverlay();
     441                    m_BuildingOverlay->m_Color = m_Color; // done separately so alpha changes don't require a full update call
     442                    collector.Submit(m_BuildingOverlay);
     443                }
     444                break;
     445            case DYNAMIC_QUAD:
     446                {
     447                    if (m_UnitOverlay)
     448                        collector.Submit(m_UnitOverlay);
     449                }
     450                break;
     451            default:
     452                break;
    172453        }
    173454    }
    174455
    175     void RenderSubmit(SceneCollector& collector)
     456    // render bounding box debug overlays
     457    if (m_AlphaTarget > 0) // so the debug overlay responds immediately to deselection without delay from fading out
    176458    {
    177         // (This is only called if a > 0)
    178         collector.Submit(&m_Overlay);
    179 
    180459        if (ICmpSelectable::ms_EnableDebugOverlays)
    181460        {
    182461            // allocate debug overlays on-demand
    public:  
    205484            if (m_DebugSelectionBoxOverlay) SAFE_DELETE(m_DebugSelectionBoxOverlay);
    206485        }
    207486    }
    208 };
     487}
    209488
    210489REGISTER_COMPONENT_TYPE(Selectable)
  • source/simulation2/components/CCmpTemplateManager.cpp

    diff --git a/source/simulation2/components/CCmpTemplateManager.cpp b/source/simulation2/components/CCmpTemplateManager.cpp
    index d512934..73c560f 100644
    a b void CCmpTemplateManager::ConstructTemplateActor(const std::string& actorName, C  
    371371
    372372    // Initialise the actor's name and make it an Atlas selectable entity.
    373373    std::string name = utf8_from_wstring(CParamNode::EscapeXMLString(wstring_from_utf8(actorName)));
    374     std::string xml = "<Entity><VisualActor><Actor>" + name + "</Actor></VisualActor><Selectable><EditorOnly/></Selectable></Entity>";
     374    std::string xml = "<Entity>"
     375                          "<VisualActor><Actor>" + name + "</Actor></VisualActor>"
     376                          "<Selectable>"
     377                              "<EditorOnly/>"
     378                              "<Overlay><Texture><MainTexture>art/textures/selection/actor.png</MainTexture><MainTextureMask>art/textures/selection/actor_mask.png</MainTextureMask></Texture></Overlay>"
     379                          "</Selectable>"
     380                      "</Entity>";
     381
    375382    CParamNode::LoadXMLString(out, xml.c_str());
    376383}
    377384
  • source/simulation2/components/CCmpTerritoryManager.cpp

    diff --git a/source/simulation2/components/CCmpTerritoryManager.cpp b/source/simulation2/components/CCmpTerritoryManager.cpp
    index 0ffdd62..c907de1 100644
    a b void CCmpTerritoryManager::UpdateBoundaryLines()  
    618618    texturePropsMask.SetMaxAnisotropy(2.f);
    619619    CTexturePtr textureMask = g_Renderer.GetTextureManager().CreateTexture(texturePropsMask);
    620620
    621     CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY);
    622     if (!cmpTerrain)
    623         return;
    624     CTerrain* terrain = cmpTerrain->GetCTerrain();
    625 
    626621    CmpPtr<ICmpPlayerManager> cmpPlayerManager(GetSimContext(), SYSTEM_ENTITY);
    627622    if (!cmpPlayerManager)
    628623        return;
    void CCmpTerritoryManager::UpdateBoundaryLines()  
    640635        m_BoundaryLines.push_back(SBoundaryLine());
    641636        m_BoundaryLines.back().connected = boundaries[i].connected;
    642637        m_BoundaryLines.back().color = color;
    643         m_BoundaryLines.back().overlay.m_Terrain = terrain;
     638        m_BoundaryLines.back().overlay.m_SimContext = &GetSimContext();
    644639        m_BoundaryLines.back().overlay.m_TextureBase = textureBase;
    645640        m_BoundaryLines.back().overlay.m_TextureMask = textureMask;
    646641        m_BoundaryLines.back().overlay.m_Color = color;
    void CCmpTerritoryManager::UpdateBoundaryLines()  
    648643        m_BoundaryLines.back().overlay.m_Closed = true;
    649644
    650645        SimRender::SmoothPointsAverage(boundaries[i].points, m_BoundaryLines.back().overlay.m_Closed);
    651 
    652646        SimRender::InterpolatePointsRNS(boundaries[i].points, m_BoundaryLines.back().overlay.m_Closed, m_BorderSeparation);
    653647
    654648        std::vector<float>& points = m_BoundaryLines.back().overlay.m_Coords;
  • source/simulation2/components/ICmpSelectable.h

    diff --git a/source/simulation2/components/ICmpSelectable.h b/source/simulation2/components/ICmpSelectable.h
    index c7c8ec2..03d2819 100644
    a b struct CColor;  
    2525class ICmpSelectable : public IComponent
    2626{
    2727public:
     28    /// Batch rendering data ID; used by the selection highlight renderer to efficiently render selection highlights
     29    typedef std::size_t renderdata_id;
     30
     31    enum EOverlayType {
     32        /// A single textured quad overlay, intended for entities that move around much, like units (e.g. foot soldiers, etc).
     33        DYNAMIC_QUAD,
     34        /// A more complex textured line overlay, composed of several textured line segments. Intended for entities that do not
     35        /// move often, such as buildings (structures).
     36        STATIC_OUTLINE,
     37    };
     38
     39    struct SOverlayDescriptor
     40    {
     41        EOverlayType m_Type;
     42        std::wstring m_QuadTexture;
     43        std::wstring m_QuadTextureMask;
     44        std::wstring m_LineTexture;
     45        std::wstring m_LineTextureMask;
     46        float m_LineThickness;
     47        std::wstring m_CornerTexture;
     48        std::wstring m_CornerTextureMask;
     49
     50        SOverlayDescriptor() : m_LineThickness(0) {}
     51
     52        struct hash : std::unary_function<SOverlayDescriptor, renderdata_id>
     53        {
     54            renderdata_id operator()(SOverlayDescriptor const& d) const;
     55        };
     56
     57        bool operator==(const SOverlayDescriptor& other) const;
     58    };
     59
    2860    /**
    2961     * Returns true if the entity is only selectable in Atlas editor, e.g. a decorative visual actor.
    3062     */
  • source/simulation2/helpers/Geometry.cpp

    diff --git a/source/simulation2/helpers/Geometry.cpp b/source/simulation2/helpers/Geometry.cpp
    index b357561..7b36a24 100644
    a b CFixedVector2D Geometry::GetHalfBoundingBox(CFixedVector2D u, CFixedVector2D v,  
    4545    );
    4646}
    4747
     48float Geometry::ChordToCentralAngle(const float chordLength, const float radius)
     49{
     50    ENSURE(radius > 0);
     51    return acosf(1.f - SQR(chordLength)/(2.f*SQR(radius))); // cfr. law of cosines
     52}
     53
    4854fixed Geometry::DistanceToSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize)
    4955{
    5056    /*
  • source/simulation2/helpers/Geometry.h

    diff --git a/source/simulation2/helpers/Geometry.h b/source/simulation2/helpers/Geometry.h
    index cb925b7..b073099 100644
    a b  
    2424 */
    2525
    2626#include "maths/Fixed.h"
     27#include "maths/MathUtil.h"
    2728
    2829class CFixedVector2D;
    2930
    CFixedVector2D GetHalfBoundingBox(CFixedVector2D u, CFixedVector2D v, CFixedVect  
    5354fixed DistanceToSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize);
    5455
    5556/**
     57 * Given a circle of radius @p radius, and a chord of length @p chordLength on this circle, computes the central angle formed by
     58 * connecting the chord's endpoints to the center of the circle.
     59 *
     60 * @param radius Radius of the circle; must be strictly positive.
     61 */
     62float ChordToCentralAngle(const float chordLength, const float radius);
     63
     64/**
    5665 * Find point closest to the given point on the edge of the given square or rectangle.
    5766 *
    5867 * @note Currently assumes the @p u and @p v vectors are perpendicular.
  • source/simulation2/helpers/Render.cpp

    diff --git a/source/simulation2/helpers/Render.cpp b/source/simulation2/helpers/Render.cpp
    index 450a28d..712a6d4 100644
    a b  
    1919
    2020#include "Render.h"
    2121
    22 #include "simulation2/Simulation2.h"
    23 #include "simulation2/components/ICmpTerrain.h"
    24 #include "simulation2/components/ICmpWaterManager.h"
    2522#include "graphics/Overlay.h"
    2623#include "graphics/Terrain.h"
    2724#include "maths/BoundingBoxAligned.h"
    2825#include "maths/BoundingBoxOriented.h"
    2926#include "maths/MathUtil.h"
     27#include "maths/Quaternion.h"
    3028#include "maths/Vector2D.h"
    3129#include "ps/Profile.h"
    32 #include "maths/Quaternion.h"
     30#include "simulation2/Simulation2.h"
     31#include "simulation2/components/ICmpTerrain.h"
     32#include "simulation2/components/ICmpWaterManager.h"
     33#include "simulation2/helpers/Geometry.h"
    3334
    3435void SimRender::ConstructLineOnGround(const CSimContext& context, const std::vector<float>& xz,
    3536        SOverlayLine& overlay, bool floating, float heightOffset)
    static CVector2D EvaluateSpline(float t, CVector2D a0, CVector2D a1, CVector2D a  
    343344    return p + CVector2D(dp.Y*-offset, dp.X*offset);
    344345}
    345346
    346 void SimRender::InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed, float offset, int segmentSamples)
     347void SimRender::InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed, float offset, int segmentSamples /* = 4 */)
    347348{
    348349    PROFILE("InterpolatePointsRNS");
    349350    ENSURE(segmentSamples > 0);
    void SimRender::InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed  
    381382
    382383    for (size_t i = 0; i < imax; ++i)
    383384    {
    384 
    385385        // Get the relevant points for this spline segment; each step interpolates the segment between p1 and p2; p0 and p3 are the points
    386386        // before p1 and after p2, respectively; they're needed to compute tangents and whatnot.
    387387        CVector2D p0; // normally points[(i-1+n)%n], but it's a bit more complicated due to open/closed paths -- see below
    void SimRender::ConstructDashedLine(const std::vector<CVector2D>& keyPoints, SDa  
    521521    }
    522522
    523523}
     524
     525void SimRender::AngularStepFromChordLen(const float maxChordLength, const float radius, float& out_stepAngle, unsigned& out_numSteps)
     526{
     527    float maxAngle = Geometry::ChordToCentralAngle(maxChordLength, radius);
     528    out_numSteps = ceilf(float(2*M_PI)/maxAngle);
     529    out_stepAngle = float(2*M_PI)/out_numSteps;
     530}
     531
     532// TODO: this serves a similar purpose to SplitLine above, but is more general. Also, SplitLine seems to be implemented more
     533// efficiently, might be nice to take some cues from it
     534void SimRender::SubdividePoints(std::vector<CVector2D>& points, float maxSegmentLength, bool closed)
     535{
     536    size_t numControlPoints = points.size();
     537    if (numControlPoints < 2)
     538        return;
     539
     540    ENSURE(maxSegmentLength > 0);
     541
     542    size_t endIndex = numControlPoints;
     543    if (!closed && numControlPoints > 2)
     544        endIndex--;
     545
     546    std::vector<CVector2D> newPoints;
     547
     548    for (size_t i = 0; i < endIndex; i++)
     549    {
     550        const CVector2D& curPoint = points[i];
     551        const CVector2D& nextPoint = points[(i+1) % numControlPoints];
     552        const CVector2D line(nextPoint - curPoint);
     553        CVector2D lineDirection = line.Normalized();
     554
     555        // include control point i + a list of intermediate points between i and i + 1 (excluding i+1 itself)
     556        newPoints.push_back(curPoint);
     557
     558        // calculate how many intermediate points are needed so that each segment is of length <= maxSegmentLength
     559        float lineLength = line.Length();
     560        size_t numSegments = (size_t) ceilf(lineLength / maxSegmentLength);
     561        float segmentLength = lineLength / numSegments;
     562
     563        for (size_t s = 1; s < numSegments; ++s) // start at one, we already included curPoint
     564        {
     565            newPoints.push_back(curPoint + lineDirection * (s * segmentLength));
     566        }
     567    }
     568
     569    points.swap(newPoints);
     570}
     571 No newline at end of file
  • source/simulation2/helpers/Render.h

    diff --git a/source/simulation2/helpers/Render.h b/source/simulation2/helpers/Render.h
    index 93e812d..e95d8e4 100644
    a b void ConstructLineOnGround(const CSimContext& context, const std::vector<float>&  
    7676 * @param[in,out] overlay Updated overlay line representing this circle.
    7777 * @param[in] floating If true, the circle conforms to water as well.
    7878 * @param[in] heightOffset Height above terrain to offset the circle.
     79 * @param heightOffset The vertical offset to apply to points, to raise the line off the terrain a bit.
    7980 */
    8081void ConstructCircleOnGround(const CSimContext& context, float x, float z, float radius,
    8182        SOverlayLine& overlay,
    void ConstructSquareOnGround(const CSimContext& context, float x, float z, float  
    9697        bool floating, float heightOffset = 0.25f);
    9798
    9899/**
    99  * Constructs a solid outline of an arbitrarily-aligned box.
     100 * Constructs a solid outline of an arbitrarily-aligned bounding @p box.
    100101 *
    101102 * @param[in] box
    102103 * @param[in,out] overlayLine Updated overlay line representing the oriented box.
    void ConstructSquareOnGround(const CSimContext& context, float x, float z, float  
    104105void ConstructBoxOutline(const CBoundingBoxOriented& box, SOverlayLine& overlayLine);
    105106
    106107/**
    107  * Constructs a solid outline of an axis-aligned bounding box.
     108 * Constructs a solid outline of an axis-aligned bounding @p box.
    108109 *
    109110 * @param[in] bound
    110111 * @param[in,out] overlayLine Updated overlay line representing the AABB.
    111112 */
    112 void ConstructBoxOutline(const CBoundingBoxAligned& bound, SOverlayLine& overlayLine);
     113void ConstructBoxOutline(const CBoundingBoxAligned& box, SOverlayLine& overlayLine);
    113114
    114115/**
    115116 * Constructs a simple gimbal outline with the given radius and center.
    void ConstructGimbal(const CVector3D& center, float radius, SOverlayLine& out, s  
    124125
    125126/**
    126127 * Constructs 3D axis marker overlay lines for the given coordinate system.
    127  * The overlay lines are colored RGB for the XYZ axes, respectively.
     128 * The XYZ axes are colored RGB, respectively.
    128129 *
    129130 * @param[in] coordSystem Specifies the coordinate system.
    130131 * @param[out] outX,outY,outZ Constructed overlay lines for each axes.
    void InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed, float off  
    162163 * @param[in] dashLength Length of a single dash. Must be strictly positive.
    163164 * @param[in] blankLength Length of a single blank between dashes. Must be strictly positive.
    164165 */
    165 void ConstructDashedLine(const std::vector<CVector2D>& linePoints, SDashedLine& dashedLineOut, const float dashLength, const float blankLength);
     166void ConstructDashedLine(const std::vector<CVector2D>& linePoints, SDashedLine& dashedLineOut,
     167        const float dashLength, const float blankLength);
     168
     169/**
     170 * Computes angular step parameters @p out_stepAngle and @p out_numSteps, given a @p maxChordLength on a circle of radius @p radius.
     171 * The resulting values satisfy @p out_numSteps * @p out_stepAngle = 2*PI.
     172 *
     173 * This function is used to find the angular step parameters when drawing a circle outline approximated by several connected chords;
     174 * it returns the step angle and number of steps such that the length of each resulting chord is less than or equal to @p maxChordLength.
     175 * By stating that each chord cannot be longer than a particular length, a certain level of visual smoothness of the resulting circle
     176 * outline can be guaranteed independently of the radius of the outline.
     177 *
     178 * @param radius Radius of the circle. Must be strictly positive.
     179 * @param maxChordLength Desired maximum length of individual chords. Must be strictly positive.
     180 */
     181void AngularStepFromChordLen(const float maxChordLength, const float radius, float& out_stepAngle, unsigned& out_numSteps);
     182
     183/**
     184 * Subdivides a list of @p points into segments of maximum length @p maxSegmentLength that are of equal size between every two
     185 * control points. The resulting subdivided list of points is written back to @p points.
     186 *
     187 * @param points The list of intermediate points to subdivide.
     188 * @param maxSegmentLength The maximum length of a single segment after subdivision. Must be strictly positive.
     189 * @param closed Should the provided list of points be treated as a closed shape? If true, the resulting list of points will include
     190 *        extra subdivided points between the last and the first point.
     191 */
     192void SubdividePoints(std::vector<CVector2D>& points, float maxSegmentLength, bool closed);
    166193
    167194} // namespace
    168195
  • source/simulation2/tests/test_CmpTemplateManager.h

    diff --git a/source/simulation2/tests/test_CmpTemplateManager.h b/source/simulation2/tests/test_CmpTemplateManager.h
    index d087929..2168e64 100644
    a b public:  
    7878        const CParamNode* actor = tempMan->LoadTemplate(ent2, "actor|example1", -1);
    7979        TS_ASSERT(actor != NULL);
    8080        TS_ASSERT_WSTR_EQUALS(actor->ToXML(),
    81                 L"<Selectable><EditorOnly></EditorOnly></Selectable><VisualActor><Actor>example1</Actor><SilhouetteDisplay>false</SilhouetteDisplay><SilhouetteOccluder>false</SilhouetteOccluder></VisualActor>");
     81                L"<Selectable><EditorOnly></EditorOnly><Overlay><Texture><MainTexture>art/textures/selection/actor.png</MainTexture><MainTextureMask>art/textures/selection/actor_mask.png</MainTextureMask></Texture></Overlay></Selectable>"
     82                L"<VisualActor><Actor>example1</Actor><SilhouetteDisplay>false</SilhouetteDisplay><SilhouetteOccluder>false</SilhouetteOccluder></VisualActor>");
    8283
    8384        const CParamNode* preview = tempMan->LoadTemplate(ent2, "preview|unit", -1);
    8485        TS_ASSERT(preview != NULL);
  • new file source/tools/selectiontexgen/selectiontexgen.py

    diff --git a/source/tools/selectiontexgen/selectiontexgen.py b/source/tools/selectiontexgen/selectiontexgen.py
    new file mode 100644
    index 0000000..a275783
    - +  
     1"""Generates basic square and circle selection overlay textures by parsing all the entity XML files and reading
     2their Footprint components."""
     3
     4# This script uses PyCairo for plotting, since PIL (Python Imaging Library) is absolutely horrible. On Linux,
     5# this should be merely a matter of installing a package (e.g. 'python-cairo' for Debian/Ubuntu), but on Windows
     6# it's kind of tricky and requires some Google-fu. Fortunately, I have saved the working instructions below:
     7#
     8# Grab a Win32 binary from http://ftp.gnome.org/pub/GNOME/binaries/win32/pycairo/1.8/ and install PyCairo using
     9# the installer. The installer extracts the necessary files into Lib\site-packages\cairo within the folder where
     10# Python is installed. There are some extra DLLs which are required to make Cairo work, so we have to get these
     11# as well.
     12#
     13# Head to http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/ and get the binary versions of Cairo
     14# (cairo_1.8.10-3_win32.zip at the time of writing), Fontconfig (fontconfig_2.8.0-2_win32.zip), Freetype
     15# (freetype_2.4.4-1_win32.zip), Expat (expat_2.0.1-1_win32.zip), libpng (libpng_1.4.3-1_win32.zip) and zlib
     16# (zlib_1.2.5-2_win32.zip). Version numbers may vary, so be adaptive! Each ZIP file will contain a bin subfolder
     17# with a DLL file in it. Put the following DLLs in Lib\site-packages\cairo within your Python installation:
     18#
     19#    freetype6.dll (from freetype_2.4.4-1_win32.zip)
     20#    libcairo-2.dll (from cairo_1.8.10-3_win32.zip)
     21#    libexpat-1.dll (from expat_2.0.1-1_win32.zip)
     22#    libfontconfig-1.dll (from fontconfig_2.8.0-2_win32.zip)
     23#    libpng14-14.dll (from libpng_1.4.3-1_win32.zip)
     24#    zlib1.dll (from zlib_1.2.5-2_win32.zip).
     25#
     26# Should be all set now.
     27
     28import optparse
     29import sys, os
     30import math
     31import operator
     32import cairo                    # Requires PyCairo (see notes above)
     33from os.path import *
     34from xml.dom import minidom
     35
     36def geqPow2(x):
     37    """Returns the smallest power of two that's equal to or greater than x"""
     38    return int(2**math.ceil(math.log(x, 2)))
     39
     40def generateSelectionTexture(shape, textureW, textureH, outputDir):
     41   
     42    outputBasename = "%dx%d" % (textureW, textureH)
     43   
     44    # size of the image canvas containing the texture (may be larger to ensure power-of-two dimensions)
     45    canvasW = geqPow2(textureW)
     46    canvasH = geqPow2(textureH)
     47   
     48    # draw texture
     49    texture = cairo.ImageSurface(cairo.FORMAT_ARGB32, canvasW, canvasH)
     50    textureMask = cairo.ImageSurface(cairo.FORMAT_RGB24, canvasW, canvasH)
     51   
     52    ctxTexture = cairo.Context(texture)
     53    ctxTextureMask = cairo.Context(textureMask)
     54   
     55    # fill entire image with transparent pixels
     56    ctxTexture.set_source_rgba(1.0, 1.0, 1.0, 0.0)      # transparent
     57    ctxTexture.rectangle(0, 0, textureW, textureH)         
     58    ctxTexture.fill()                                   # fill current path
     59   
     60    ctxTextureMask.set_source_rgb(0.0, 0.0, 0.0)        # black
     61    ctxTextureMask.rectangle(0, 0, canvasW, canvasH)    # (!)
     62    ctxTextureMask.fill()
     63   
     64    pasteX = (canvasW - textureW)//2                    # integer division, floored result
     65    pasteY = (canvasH - textureH)//2                    # integer division, floored result
     66    ctxTexture.translate(pasteX, pasteY)                # translate all drawing so that the result is centered
     67    ctxTextureMask.translate(pasteX, pasteY)
     68   
     69    if shape == "square":
     70       
     71        rectW = textureW
     72        rectH = textureH
     73       
     74        # draw texture (4px white outline, then overlay a 2px black outline)
     75        ctxTexture.rectangle(2, 2, rectW-4, rectH-4)
     76        ctxTexture.set_line_width(4)
     77        ctxTexture.set_source_rgba(1.0, 1.0, 1.0, 1.0)  # white
     78        ctxTexture.stroke_preserve()                    # stroke and maintain path
     79        ctxTexture.set_line_width(2)
     80        ctxTexture.set_source_rgba(0.0, 0.0, 0.0, 1.0)  # black
     81        ctxTexture.stroke()                             # stroke and clear path
     82       
     83        # draw mask (2px white)
     84        ctxTextureMask.rectangle(2, 2, rectW-4, rectH-4)
     85        ctxTextureMask.set_line_width(2)
     86        ctxTextureMask.set_source_rgb(1.0, 1.0, 1.0)
     87        ctxTextureMask.stroke()
     88       
     89    elif shape == "circle":
     90       
     91        centerX = textureW//2
     92        centerY = textureH//2
     93        radius = textureW//2 - 2 # allow for 2px on either side so the 4px-wide white stroke can fit
     94       
     95        # draw texture
     96        ctxTexture.arc(centerX, centerY, radius, 0, 2*math.pi)
     97        ctxTexture.set_line_width(4)
     98        ctxTexture.set_source_rgba(1.0, 1.0, 1.0, 1.0)  # white
     99        ctxTexture.stroke_preserve()                    # stroke and maintain path
     100        ctxTexture.set_line_width(2)
     101        ctxTexture.set_source_rgba(0.0, 0.0, 0.0, 1.0)  # black
     102        ctxTexture.stroke()
     103       
     104        # draw mask
     105        ctxTextureMask.arc(centerX, centerY, radius, 0, 2*math.pi)
     106        ctxTextureMask.set_line_width(2)
     107        ctxTextureMask.set_source_rgb(1.0, 1.0, 1.0)
     108        ctxTextureMask.stroke()
     109   
     110    finalOutputDir = outputDir + "/" + shape
     111    if not isdir(finalOutputDir):
     112        os.makedirs(finalOutputDir)
     113   
     114    print "Generating " + os.path.normcase(finalOutputDir + "/" + outputBasename + ".png")
     115   
     116    texture.write_to_png(finalOutputDir + "/" + outputBasename + ".png")
     117    textureMask.write_to_png(finalOutputDir + "/" + outputBasename + "_mask.png")
     118
     119
     120def generateSelectionTextures(xmlTemplateDir, outputDir, snapSizes = False):
     121   
     122    # recursively list XML files
     123    xmlFiles = []
     124   
     125    for dir, subdirs, basenames in os.walk(xmlTemplateDir):
     126        for basename in basenames:
     127            filename = join(dir, basename)
     128            if filename[-4:] == ".xml":
     129                xmlFiles.append(filename)
     130   
     131    textureTypesRaw = set()     # set of (type, w, h) tuples (so we can eliminate duplicates)
     132   
     133    # parse the XML files, and look for <Footprint> nodes that are a child of <Entity> and
     134    # that do not have the disable attribute defined
     135    for xmlFile in xmlFiles:
     136        xmlDoc = minidom.parse(xmlFile)
     137        rootNode = xmlDoc.childNodes[0]
     138       
     139        # we're only interested in entity templates
     140        if not rootNode.nodeName == "Entity":
     141            continue
     142       
     143        # check if this entity has a footprint definition
     144        rootChildNodes = [n for n in rootNode.childNodes if n.localName is not None] # remove whitespace text nodes
     145        footprintNodes = filter(lambda x: x.localName == "Footprint", rootChildNodes)
     146        if not len(footprintNodes) == 1:
     147            continue
     148       
     149        footprintNode = footprintNodes[0]
     150        if footprintNode.hasAttribute("disable"):
     151            continue
     152       
     153        # parse the footprint declaration
     154        # Footprints can either have either one of these children:
     155        # <Circle radius="xx.x" />
     156        # <Square width="xx.x" depth="xx.x"/>
     157        # There's also a <Height> node, but we don't care about it here.
     158       
     159        squareNodes = footprintNode.getElementsByTagName("Square")
     160        circleNodes = footprintNode.getElementsByTagName("Circle")
     161       
     162        numSquareNodes = len(squareNodes)
     163        numCircleNodes = len(circleNodes)
     164       
     165        if not (numSquareNodes + numCircleNodes == 1):
     166            print "Invalid Footprint definition: insufficient or too many Square and/or Circle definitions in %s" % xmlFile
     167       
     168        texShape = None
     169        texW = None     # in world-space units
     170        texH = None     # in world-space units
     171       
     172        if numSquareNodes == 1:
     173            texShape = "square"
     174            texW = float(squareNodes[0].getAttribute("width"))
     175            texH = float(squareNodes[0].getAttribute("depth"))
     176       
     177        elif numCircleNodes == 1:
     178            texShape = "circle"
     179            texW = float(circleNodes[0].getAttribute("radius"))
     180            texH = texW
     181       
     182        textureTypesRaw.add((texShape, texW, texH))
     183   
     184    # endfor xmlFiles
     185   
     186    print "Found:     %d footprints (%d square, %d circle)" % (
     187        len(textureTypesRaw),
     188        len([x for x in textureTypesRaw if x[0] == "square"]),
     189        len([x for x in textureTypesRaw if x[0] == "circle"])
     190    )
     191   
     192    textureTypes = set()
     193   
     194    for type, w, h in textureTypesRaw:
     195        if snapSizes:
     196            # "snap" texture sizes to close-enough neighbours that will still look good enough so we can get away with fewer
     197            # actual textures than there are unique footprint outlines
     198            w = 1*math.ceil(w/1)    # round up to the nearest world-space unit
     199            h = 1*math.ceil(h/1)    # round up to the nearest world-space unit
     200           
     201        textureTypes.add((type, w, h))
     202   
     203    if snapSizes:
     204        print "Reduced: %d footprints (%d square, %d circle)" % (
     205            len(textureTypes),
     206            len([x for x in textureTypes if x[0] == "square"]),
     207            len([x for x in textureTypes if x[0] == "circle"])
     208        )
     209   
     210    # create list from texture types set (so we can sort and have prettier output)
     211    textureTypes = sorted(list(textureTypes), key=operator.itemgetter(0,1,2))       # sort by the first tuple element, then by the second, then the third
     212   
     213    # ------------------------------------------------------------------------------------
     214    # compute the size of the actual texture we want to generate (in px)
     215   
     216    scale = 8       # world-space-units-to-pixels scale
     217    for type, w, h in textureTypes:
     218       
     219        # if we have a circle, update the w and h so that they're the full width and height of the texture
     220        # and not just the radius
     221        if type == "circle":
     222            assert w == h
     223            w *= 2
     224            h *= 2
     225       
     226        w = int(math.ceil(w*scale))
     227        h = int(math.ceil(h*scale))
     228       
     229        # apply a minimum size for really small textures (otherwise you get really ugly textures ingame)
     230        w = max(24, w)
     231        h = max(24, h)
     232       
     233        generateSelectionTexture(type, w, h, outputDir)
     234
     235
     236if __name__ == "__main__":
     237   
     238    parser = optparse.OptionParser(usage="Usage: %prog [filenames]")
     239   
     240    parser.add_option("--template-dir",  type="str",  default=None,  help="Path to simulation template XML definition folder. Will be searched recursively for templates containing Footprint definitions. If not specified and this script is run from its directory, it will be automatically determined.")
     241    parser.add_option("--output-dir",    type="str",  default=".",   help="Output directory. Will be created if it does not already exist.")
     242   
     243    (options, args) = parser.parse_args()
     244   
     245    templateDir = options.template_dir
     246    if templateDir is None:
     247       
     248        scriptDir = dirname(abspath(__file__))
     249       
     250        # 'autodetect' location if run from its own dir
     251        if normcase(scriptDir).replace('\\', '/').endswith("source/tools/selectiontexgen"):
     252            templateDir = "../../../binaries/data/mods/public/simulation/templates"
     253        else:
     254            print "No template dir specified; use the --template-dir command line argument."
     255            sys.exit()
     256   
     257    # check if the template dir exists
     258    templateDir = abspath(templateDir)
     259    if not isdir(templateDir):
     260        print "No such template directory: %s" % templateDir
     261        sys.exit()
     262   
     263    # check if the output dir exists, create it if needed
     264    outputDir = abspath(options.output_dir)
     265    print outputDir
     266    if not isdir(outputDir):
     267        print "Creating output directory: %s" % outputDir
     268        os.makedirs(outputDir)
     269   
     270    print "Template directory:\t%s" % templateDir
     271    print "Output directory:  \t%s" % outputDir
     272    print "------------------------------------------------"
     273   
     274    generateSelectionTextures(templateDir, outputDir)
     275 No newline at end of file