Ticket #824: 824_selection_rings_21apr12.patch

File 824_selection_rings_21apr12.patch, 121.5 KB (added by vts, 12 years ago)

Updated version of the patch after incorporating Philip's comments

  • binaries/data/mods/public/shaders/arb/overlayline.fp

    diff --git a/binaries/data/mods/public/shaders/arb/overlayline.fp b/binaries/data/mods/public/shaders/arb/overlayline.fp
    index fd78472..bde918c 100644
    a b TEMP base;  
    44TEMP mask;
    55TEMP color;
    66
    7 
    87// Combine base texture and color, using mask texture
    98TEX base, fragment.texcoord[0], texture[0], 2D;
    109TEX mask, fragment.texcoord[0], texture[1], 2D;
    11 LRP color.rgb, mask, objectColor, base;
     10#if USE_OBJECTCOLOR
     11  LRP color.rgb, mask, objectColor, base;
     12#else
     13  LRP color.rgb, mask, fragment.color, base;
     14#endif
    1215
    13 #ifdef IGNORE_LOS
     16#if IGNORE_LOS
    1417  MOV result.color.rgb, color;
    1518#else
    1619  // Multiply RGB by LOS texture (alpha channel)
    LRP color.rgb, mask, objectColor, base;  
    1922  MUL result.color.rgb, color, los.a;
    2023#endif
    2124
    22 // Use alpha from base texture, combined with the object color alpha.
    23 // The latter is usually 1, so this basically comes down to base.a
    24 MUL result.color.a, objectColor.a, base.a;
    25 
     25// Use alpha from base texture, combined with the object color/fragment alpha.
     26#if USE_OBJECTCOLOR
     27  MUL result.color.a, objectColor.a, base.a;
     28#else
     29  MUL result.color.a, fragment.color.a, base.a;
     30#endif
    2631
    2732END
  • binaries/data/mods/public/shaders/arb/overlayline.xml

    diff --git a/binaries/data/mods/public/shaders/arb/overlayline.xml b/binaries/data/mods/public/shaders/arb/overlayline.xml
    index 07383bc..6dd0031 100644
    a b  
    11<?xml version="1.0" encoding="utf-8"?>
    22<program type="arb">
    33
     4    <!-- This shader is used for rendering overlay lines (e.g. territory boundaries), and also for
     5    rendering the unit selection ring quad overlays, since they are almost identical. The only
     6    difference is that in unit selection ring mode, the uniform objectColor is ignored and instead
     7    replaced by a color stream from the vertices. The USE_OBJECTCOLOR define is used to switch
     8    between either mode. -->
     9   
    410    <vertex file="arb/overlayline.vp">
    511        <stream name="pos"/>
    612        <stream name="uv0"/>
     13        <stream name="color" if="!USE_OBJECTCOLOR"/>
    714        <uniform name="losTransform" loc="0" type="vec2"/>
    815    </vertex>
    916
     
    1118        <uniform name="baseTex" loc="0" type="sampler2D"/>
    1219        <uniform name="maskTex" loc="1" type="sampler2D"/>
    1320        <uniform name="losTex" loc="2" type="sampler2D"/>
    14         <uniform name="objectColor" loc="0" type="vec3"/>
     21        <uniform name="objectColor" loc="0" type="vec3" if="USE_OBJECTCOLOR"/>
    1522    </fragment>
    1623
    1724</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 4be58ad..4d458dd 100644
    a b GuiInterface.prototype.IsStanceSelected = function(player, data)  
    495495GuiInterface.prototype.SetSelectionHighlight = function(player, cmd)
    496496{
    497497    var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
    498 
    499498    var playerColours = {}; // cache of owner -> colour map
    500499   
    501500    for each (var ent in cmd.entities)
    GuiInterface.prototype.SetSelectionHighlight = function(player, cmd)  
    504503        if (!cmpSelectable)
    505504            continue;
    506505
    507         if (cmd.alpha == 0)
    508         {
    509             cmpSelectable.SetSelectionHighlight({"r":0, "g":0, "b":0, "a":0});
    510             continue;
    511         }
    512 
    513506        // Find the entity's owner's colour:
    514 
    515507        var owner = -1;
    516508        var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
    517509        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 7774fb6..0ee7160 100644
    a b  
    2323  </Position>
    2424  <Selectable>
    2525    <EditorOnly/>
     26    <Overlay>
     27      <Texture>
     28        <MainTexture>circle/128x128.png</MainTexture>
     29        <MainTextureMask>circle/128x128_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..2c2be00 100644
    a b  
    2323  </Position>
    2424  <Selectable>
    2525    <EditorOnly disable=""/>
     26    <Overlay>
     27      <Texture>
     28        <MainTexture>circle/128x128.png</MainTexture>
     29        <MainTextureMask>circle/128x128_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..65fdcaf 100644
    a b  
    1818  </ResourceSupply>
    1919  <Selectable>
    2020    <EditorOnly disable=""/>
     21    <Overlay>
     22      <Texture>
     23        <MainTexture>circle/128x128.png</MainTexture>
     24        <MainTextureMask>circle/128x128_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..096c4e7 100644
    a b  
    1717  </ResourceSupply>
    1818  <Selectable>
    1919    <EditorOnly disable=""/>
     20    <Overlay>
     21      <Texture>
     22        <MainTexture>circle/128x128.png</MainTexture>
     23        <MainTextureMask>circle/128x128_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..0f17d2c 100644
    a b  
    1717  </ResourceSupply>
    1818  <Selectable>
    1919    <EditorOnly disable=""/>
     20    <Overlay>
     21      <Outline>
     22        <LineTexture>outline_border.png</LineTexture>
     23        <LineTextureMask>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..a247c15 100644
    a b  
    1717  </ResourceSupply>
    1818  <Selectable>
    1919    <EditorOnly disable=""/>
     20    <Overlay>
     21      <Texture>
     22        <MainTexture>circle/128x128.png</MainTexture>
     23        <MainTextureMask>circle/128x128_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 f9ee70a..408ccb6 100644
    a b  
    2424  </ResourceSupply>
    2525  <Selectable>
    2626    <EditorOnly disable=""/>
     27    <Overlay>
     28      <Texture>
     29        <MainTexture>circle/128x128.png</MainTexture>
     30        <MainTextureMask>circle/128x128_mask.png</MainTextureMask>
     31      </Texture>
     32    </Overlay>
    2733  </Selectable>
    2834  <Sound>
    2935    <SoundGroups>
  • 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 15aea8a..4f11e37 100644
    a b  
    2929  </Sound>
    3030  <Selectable>
    3131    <EditorOnly disable=""/>
     32    <Overlay>
     33      <Texture>
     34        <MainTexture>circle/128x128.png</MainTexture>
     35        <MainTextureMask>circle/128x128_mask.png</MainTextureMask>
     36      </Texture>
     37    </Overlay>
    3238  </Selectable>
    3339</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 d27bdd5..c7fdbd5 100644
    a b  
    6666    <LineCostClass>default</LineCostClass>
    6767    <LinePassabilityClass>default</LinePassabilityClass>
    6868  </RallyPointRenderer>
     69  <Selectable>
     70    <Overlay>
     71      <Outline>
     72        <LineTexture>outline_border.png</LineTexture>
     73        <LineTextureMask>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 d539d87..8afd0fc 100644
    a b  
    8080      <metal>20</metal>
    8181    </Capacities>
    8282  </ResourceGatherer>
     83  <Selectable>
     84    <Overlay>
     85      <Texture>
     86        <MainTexture>circle/128x128.png</MainTexture>
     87        <MainTextureMask>circle/128x128_mask.png</MainTextureMask>
     88      </Texture>
     89    </Overlay>
     90  </Selectable>
    8391  <StatusBars>
    8492    <BarWidth>2.0</BarWidth>
    8593    <BarHeight>0.333</BarHeight>
  • binaries/data/mods/public/simulation/templates/template_unit_infantry.xml

    diff --git a/binaries/data/mods/public/simulation/templates/template_unit_infantry.xml b/binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
    index 736abae..afadec1 100644
    a b  
    7070      <metal.ore>1</metal.ore>
    7171    </Rates>
    7272  </ResourceGatherer>
     73  <Selectable>
     74    <Overlay>
     75      <Texture>
     76        <MainTexture>circle/128x128.png</MainTexture>
     77        <MainTextureMask>circle/128x128_mask.png</MainTextureMask>
     78      </Texture>
     79    </Overlay>
     80  </Selectable>
    7381  <Sound>
    7482    <SoundGroups>
    7583      <select>voice/hellenes/civ/civ_male_select.xml</select>
  • 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..556c5bd 100644
    a b  
    1 /* Copyright (C) 2011 Wildfire Games.
     1/* Copyright (C) 2012 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    2020
    2121#include "graphics/RenderableObject.h"
    2222#include "graphics/Texture.h"
     23#include "maths/Vector2D.h"
    2324#include "maths/Vector3D.h"
    2425#include "ps/Overlay.h" // CColor  (TODO: that file has nothing to do with overlays, it should be renamed)
     26#include "simulation2/components/ICmpFootprint.h"
    2527
    2628class CTerrain;
    2729
    struct SOverlayLine  
    3537
    3638    CColor m_Color;
    3739    std::vector<float> m_Coords; // (x, y, z) vertex coordinate triples; shape is not automatically closed
    38     u8 m_Thickness; // pixels
     40    u8 m_Thickness; // in pixels
    3941
    40     /// Utility function; pushes three vertex coordinates at once onto the coordinates array
    4142    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
    4343    void PushCoords(const CVector3D& v) { PushCoords(v.X, v.Y, v.Z); }
    4444};
    4545
    struct SOverlayTexturedLine  
    6464    };
    6565
    6666    SOverlayTexturedLine()
    67         : m_Terrain(NULL), m_Thickness(1.0f), m_Closed(false), m_AlwaysVisible(false), m_StartCapType(LINECAP_FLAT), m_EndCapType(LINECAP_FLAT)
    68     {}
     67        : m_SimContext(NULL), m_Thickness(1.0f), m_Closed(false), m_AlwaysVisible(false),
     68          m_StartCapType(LINECAP_FLAT), m_EndCapType(LINECAP_FLAT)
     69    { }
    6970
    70     CTerrain* m_Terrain;
    7171    CTexturePtr m_TextureBase;
    7272    CTexturePtr m_TextureMask;
    7373    CColor m_Color; ///< Color to apply to the line texture
    struct SOverlayTexturedLine  
    7979    LineCapType m_StartCapType; ///< LineCapType to be used at the start of the line
    8080    LineCapType m_EndCapType; ///< LineCapType to be used at the end of the line
    8181
     82    const CSimContext* m_SimContext; /// Simulation context applicable for this overlay line; used to obtain terrain information
    8283    shared_ptr<CRenderData> m_RenderData; ///< Cached renderer data (shared_ptr so that copies/deletes are automatic)
    8384
    8485    /**
    struct SOverlayTexturedLine  
    8687     * If the input string is unrecognized, a warning is issued and a default value is returned.
    8788     */
    8889    static LineCapType StrToLineCapType(const std::wstring& str);
     90
     91    void PushCoords(const float x, const float z) { m_Coords.push_back(x); m_Coords.push_back(z); }
     92    void PushCoords(const CVector2D& v) { PushCoords(v.X, v.Y); }
     93    void PushCoords(const std::vector<CVector2D>& points)
     94    {
     95        for (size_t i = 0; i < points.size(); ++i)
     96            PushCoords(points[i]);
     97    }
    8998};
    9099
    91100/**
    struct SOverlaySprite  
    99108    float m_X0, m_Y0, m_X1, m_Y1; // billboard corner coordinates, relative to base position
    100109};
    101110
     111/**
     112 * Rectangular single-quad terrain overlay, with world space coordinates. The vertices of the quad
     113 * are not required to be coplanar; the quad is arbitrarily triangulated with no effort being made to
     114 * find a best fit to the underlying terrain.
     115 */
     116struct SOverlayQuad
     117{
     118    CTexturePtr m_Texture;
     119    CTexturePtr m_TextureMask;
     120    CVector3D m_Corners[4];
     121    CColor m_Color;
     122};
     123
    102124// TODO: OverlayText
    103125
    104126#endif // INCLUDED_GRAPHICS_OVERLAY
  • source/graphics/RenderableObject.h

    diff --git a/source/graphics/RenderableObject.h b/source/graphics/RenderableObject.h
    index c3784fd..cdc702a 100644
    a b  
    1 /* Copyright (C) 2009 Wildfire Games.
     1/* Copyright (C) 2012 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    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/graphics/ShaderProgram.h

    diff --git a/source/graphics/ShaderProgram.h b/source/graphics/ShaderProgram.h
    index 4f94da1..c024299 100644
    a b public:  
    136136
    137137    /**
    138138     * Returns bitset of STREAM_* value, indicating what vertex data streams the
    139      * vertex shader needs.
     139     * vertex shader needs (e.g. position, color, UV, ...).
    140140     */
    141141    int GetStreamFlags() const;
    142142
  • source/graphics/ShaderProgramFFP.cpp

    diff --git a/source/graphics/ShaderProgramFFP.cpp b/source/graphics/ShaderProgramFFP.cpp
    index 7eb565c..de4f8d8 100644
    a b class CShaderProgramFFP_OverlayLine : public CShaderProgramFFP  
    168168    };
    169169
    170170    bool m_IgnoreLos;
     171    bool m_UseObjectColor;
    171172
    172173public:
    173174    CShaderProgramFFP_OverlayLine(const CShaderDefines& defines) :
    174         CShaderProgramFFP(STREAM_POS | STREAM_UV0 | STREAM_UV1)
     175        CShaderProgramFFP(0) // will be set manually in initializer below
    175176    {
    176177        SetUniformIndex("losTransform", ID_losTransform);
    177178        SetUniformIndex("objectColor", ID_objectColor);
    public:  
    182183        SetUniformIndex("losTex", 2);
    183184
    184185        m_IgnoreLos = (defines.GetInt("IGNORE_LOS") != 0);
     186        m_UseObjectColor = (defines.GetInt("USE_OBJECTCOLOR") != 0);
     187
     188        m_StreamFlags = STREAM_POS | STREAM_UV0 | STREAM_UV1;
     189        if (!m_UseObjectColor)
     190            m_StreamFlags |= STREAM_COLOR;
    185191    }
    186192
    187193    bool IsIgnoreLos()
    public:  
    219225    virtual void Bind()
    220226    {
    221227        // RGB channels:
    222         //   Unit 0: Load base texture
    223         //   Unit 1: Load mask texture; interpolate with objectColor & base
     228        //   Unit 0: Sample base texture
     229        //   Unit 1: Sample mask texture; interpolate with [objectColor or vertexColor] and base, depending on USE_OBJECTCOLOR
    224230        //   Unit 2: (Load LOS texture; multiply) if not #IGNORE_LOS, pass through otherwise
    225231        // Alpha channel:
    226         //   Unit 0: Load base texture
     232        //   Unit 0: Sample base texture
    227233        //   Unit 1: Multiply by objectColor
    228234        //   Unit 2: Pass through
    229235
    public:  
    231237        glEnable(GL_TEXTURE_2D);
    232238        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
    233239
     240        // Sample base texture RGB
    234241        glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
    235242        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
    236243        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
    237244
     245        // Sample base texture Alpha
    238246        glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
    239247        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
    240248        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
    public:  
    244252        pglActiveTextureARB(GL_TEXTURE1);
    245253        glEnable(GL_TEXTURE_2D);
    246254        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
    247         // Uniform() sets GL_TEXTURE_ENV_COLOR
    248255
    249         // load mask texture; interpolate with objectColor and base; GL_INTERPOLATE takes 3 arguments:
    250         // a0 * a2 + a1 * (1 - a2)
     256        // RGB: interpolate component-wise between [objectColor or vertexColor] and base using mask:
     257        //   a0 * a2 + a1 * (1 - a2)
     258        // Overridden implementation of Uniform() sets GL_TEXTURE_ENV_COLOR to objectColor, which
     259        // is referenced as GL_CONSTANT (see spec)
    251260        glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE);
    252         glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_CONSTANT);
     261        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, m_UseObjectColor ? GL_CONSTANT : GL_PRIMARY_COLOR);
    253262        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
    254263        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS);
    255264        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
    256265        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_TEXTURE);
    257266        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_COLOR);
    258267
     268        // ALPHA: Multiply base alpha with [objectColor or vertexColor] alpha
    259269        glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
    260         glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_CONSTANT);
     270        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, m_UseObjectColor ? GL_CONSTANT : GL_PRIMARY_COLOR);
    261271        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
    262272        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_PREVIOUS);
    263273        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, GL_SRC_ALPHA);
    public:  
    278288        }
    279289        else
    280290        {
    281             // multiply RGB with LoS texture alpha channel
     291            // Multiply RGB result up till now with LoS texture alpha channel
    282292            glEnable(GL_TEXTURE_GEN_S);
    283293            glEnable(GL_TEXTURE_GEN_T);
    284294            glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
    285295            glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
    286             // Uniform() sets GL_OBJECT_PLANE values
     296            // Overridden implementation of Uniform() sets GL_OBJECT_PLANE values
    287297           
    288298            glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
    289299            glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
  • new file source/maths/Ease.h

    diff --git a/source/maths/Ease.h b/source/maths/Ease.h
    new file mode 100644
    index 0000000..66e7859
    - +  
     1/* Copyright (C) 2012 Wildfire Games.
     2 * This file is part of 0 A.D.
     3 *
     4 * 0 A.D. is free software: you can redistribute it and/or modify
     5 * it under the terms of the GNU General Public License as published by
     6 * the Free Software Foundation, either version 2 of the License, or
     7 * (at your option) any later version.
     8 *
     9 * 0 A.D. is distributed in the hope that it will be useful,
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 * GNU General Public License for more details.
     13 *
     14 * You should have received a copy of the GNU General Public License
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
     16 */
     17
     18#ifndef INCLUDED_EASE
     19#define INCLUDED_EASE
     20
     21/**
     22 * Straightforward C++ port of Robert Penner's easing equations
     23 * http://www.robertpenner.com/easing/
     24 *
     25 * Open source under the BSD License.
     26 * Copyright (c) 2001 Robert Penner
     27 * All rights reserved.
     28 * http://www.robertpenner.com/easing_terms_of_use.html
     29 */
     30
     31#include <math.h>
     32
     33/**
     34 * Generic easing functions. In each function, the parameters are:
     35 *
     36 * @param t Current time in seconds, as a float between 0 and d (inclusive).
     37 * @param d Total duration of the ease, in seconds. Must be strictly positive.
     38 * @param b Baseline value (at t = 0).
     39 * @param c Delta from baseline value to reach the target value (at t = 1). I.e., target = b + c.
     40 *
     41 * Each function outputs the eased value between 'b' and 'b+c' at time 't'.
     42 */
     43class Ease
     44{
     45public:
     46    static float QuadIn(float t, const float b, const float c, const float d)
     47    {
     48        t /= d;
     49        return c*t*t + b;
     50    }
     51
     52    static float QuadOut(float t, const float b, const float c, const float d)
     53    {
     54        t /= d;
     55        return -c * t*(t-2) + b;
     56    }
     57
     58    static float QuadInOut(float t, const float b, const float c, const float d)
     59    {
     60        t /= d/2;
     61        if (t < 1)
     62            return c/2*t*t + b;
     63        --t;
     64        return -c/2 * (t*(t-2) - 1) + b;
     65    }
     66
     67    static float CubicIn(float t, const float b, const float c, const float d)
     68    {
     69        t /= d;
     70        return c*t*t*t + b;
     71    }
     72
     73    static float CubicOut(float t, const float b, const float c, const float d)
     74    {
     75        t = t/d - 1;
     76        return c*(t*t*t + 1) + b;
     77    }
     78
     79    static float CubicInOut(float t, const float b, const float c, const float d)
     80    {
     81        t /= d/2;
     82        if (t < 1)
     83            return c/2*t*t*t + b;
     84        t -= 2;
     85        return c/2*(t*t*t + 2) + b;
     86    }
     87
     88    static float QuartIn(float t, const float b, const float c, const float d)
     89    {
     90        t /= d;
     91        return c*t*t*t*t + b;
     92    }
     93
     94    static float QuartOut(float t, const float b, const float c, const float d)
     95    {
     96        t = t/d - 1;
     97        return -c*(t*t*t*t - 1) + b;
     98    }
     99
     100    static float QuartInOut(float t, const float b, const float c, const float d)
     101    {
     102        t /= d/2;
     103        if (t < 1)
     104            return c/2*t*t*t*t + b;
     105        t -= 2;
     106        return -c/2 * (t*t*t*t - 2) + b;
     107    }
     108
     109    static float QuintIn(float t, const float b, const float c, const float d)
     110    {
     111        t /= d;
     112        return c*t*t*t*t*t + b;
     113    }
     114
     115    static float QuintOut(float t, const float b, const float c, const float d)
     116    {
     117        t = t/d - 1;
     118        return c*(t*t*t*t*t + 1) + b;
     119    }
     120
     121    static float QuintInOut(float t, const float b, const float c, const float d)
     122    {
     123        t /= d/2;
     124        if (t < 1)
     125            return c/2*t*t*t*t*t + b;
     126        t -= 2;
     127        return c/2*(t*t*t*t*t + 2) + b;
     128    }
     129};
     130
     131#endif // INCLUDED_EASE
  • 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 0a30e7a..c22e8e8 100644
    a b  
    1 /* Copyright (C) 2011 Wildfire Games.
     1/* Copyright (C) 2012 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    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"
     33#include "renderer/VertexArray.h"
    3334#include "renderer/VertexBuffer.h"
    3435#include "renderer/VertexBufferManager.h"
    3536#include "simulation2/Simulation2.h"
    3637#include "simulation2/components/ICmpWaterManager.h"
     38#include "simulation2/system/SimContext.h"
     39
     40/**
     41 * As a general TODO, some of the code here still uses g_VBMan manually.
     42 * For consistency with other parts of the engine, it'd be nice to switch
     43 * over to the cleaner and more readable VertexArray API.
     44 */
     45
     46const float OverlayRenderer::OVERLAY_VOFFSET = 0.2f;
     47
     48/**
     49 * Key used to group quads into batches for more efficient rendering. Currently groups by the combination
     50 * of the main texture and the texture mask, to minimize texture swapping during rendering.
     51 */
     52struct QuadBatchKey
     53{
     54    QuadBatchKey (const CTexturePtr& texture, const CTexturePtr& textureMask)
     55        : m_Texture(texture), m_TextureMask(textureMask)
     56    { }
     57
     58    bool operator==(const QuadBatchKey& other) const
     59    {
     60        return (m_Texture == other.m_Texture && m_TextureMask == other.m_TextureMask);
     61    }
     62
     63    CTexturePtr m_Texture;
     64    CTexturePtr m_TextureMask;
     65};
     66
     67/**
     68 * Holds information about a single quad rendering batch.
     69 */
     70class QuadBatchData : public CRenderData
     71{
     72public:
     73    QuadBatchData() : m_IndicesBase(0) { }
     74
     75    /// Holds the quad overlay structures to be rendered during this batch.
     76    /// Must be cleared after each frame.
     77    std::vector<SOverlayQuad*> m_Quads;
     78    /// Start index of this batch into the dedicated quad indices VertexArray (see OverlayInternals).
     79    size_t m_IndicesBase;
     80};
    3781
    3882struct OverlayRendererInternals
    3983{
     84    typedef boost::unordered_map<QuadBatchKey, QuadBatchData> QuadBatchMap;
     85
     86    OverlayRendererInternals();
     87    ~OverlayRendererInternals(){ }
     88
    4089    std::vector<SOverlayLine*> lines;
    4190    std::vector<SOverlayTexturedLine*> texlines;
    4291    std::vector<SOverlaySprite*> sprites;
     92    std::vector<SOverlayQuad*> quads;
     93
     94    QuadBatchMap quadBatchMap;
     95
     96    // Dedicated vertex/index buffers for rendering all quads (to within the limits set by
     97    // MAX_QUAD_OVERLAYS).
     98    VertexArray quadVertices;
     99    VertexArray::Attribute quadAttributePos;
     100    VertexArray::Attribute quadAttributeColor;
     101    VertexArray::Attribute quadAttributeUV;
     102    VertexIndexArray quadIndices;
     103
     104    /// Maximum amount of quad overlays we support for rendering. This limit is set to be able to
     105    /// render all quads from a single dedicated VB without having to reallocate it, which is much
     106    /// faster in the typical case of rendering only a handful of quads. When modifying this value,
     107    /// you must take care for the new amount of quads to fit in a single VBO (which is not likely
     108    /// to be a problem).
     109    static const size_t MAX_QUAD_OVERLAYS = 1024;
     110
     111    // Sets of commonly-(re)used shader defines.
     112    CShaderDefines defsOverlayLineNormal;
     113    CShaderDefines defsOverlayLineAlwaysVisible;
     114    CShaderDefines defsQuadOverlay;
    43115};
    44116
     117OverlayRendererInternals::OverlayRendererInternals()
     118    : quadVertices(GL_DYNAMIC_DRAW), quadIndices(GL_DYNAMIC_DRAW)
     119{
     120    quadAttributePos.elems = 3;
     121    quadAttributePos.type = GL_FLOAT;
     122    quadVertices.AddAttribute(&quadAttributePos);
     123
     124    quadAttributeColor.elems = 4;
     125    quadAttributeColor.type = GL_FLOAT;
     126    quadVertices.AddAttribute(&quadAttributeColor);
     127
     128    quadAttributeUV.elems = 2;
     129    quadAttributeUV.type = GL_SHORT; // don't use GL_UNSIGNED_SHORT here, TexCoordPointer won't accept it
     130    quadVertices.AddAttribute(&quadAttributeUV);
     131
     132    quadVertices.SetNumVertices(MAX_QUAD_OVERLAYS * 4);
     133    quadVertices.Layout(); // allocate backing store
     134
     135    quadIndices.SetNumVertices(MAX_QUAD_OVERLAYS * 6);
     136    quadIndices.Layout(); // allocate backing store
     137
     138    // Since the quads in the vertex array are independent and always consist of exactly 4 vertices per quad, the
     139    // indices are always the same; we can therefore fill in all the indices once and pretty much forget about
     140    // them. We then also no longer need its backing store, since we never change any indices afterwards.
     141    VertexArrayIterator<u16> index = quadIndices.GetIterator();
     142    for (size_t i = 0; i < MAX_QUAD_OVERLAYS; ++i)
     143    {
     144        *index++ = i*4 + 0;
     145        *index++ = i*4 + 1;
     146        *index++ = i*4 + 2;
     147        *index++ = i*4 + 2;
     148        *index++ = i*4 + 3;
     149        *index++ = i*4 + 0;
     150    }
     151    quadIndices.Upload();
     152    quadIndices.FreeBackingStore();
     153
     154    // Note that we're reusing the textured overlay line shader for the quad overlay rendering. This
     155    // is because their code is almost identical; the only difference is that for the quad overlays
     156    // we want to use a vertex color stream as opposed to an objectColor uniform. To this end, the
     157    // shader has been set up to switch between the two behaviours based on the USE_OBJECTCOLOR define.
     158    defsOverlayLineNormal.Add("USE_OBJECTCOLOR", "1");
     159    defsOverlayLineAlwaysVisible.Add("USE_OBJECTCOLOR", "1");
     160    defsOverlayLineAlwaysVisible.Add("IGNORE_LOS", "1");
     161}
     162
    45163class CTexturedLineRData : public CRenderData
    46164{
    47165public:
    48     CTexturedLineRData(SOverlayTexturedLine* line) :
    49         m_Line(line), m_VB(NULL), m_VBIndices(NULL), m_Raise(.2f)
     166    CTexturedLineRData(SOverlayTexturedLine* line) : m_Line(line), m_VB(NULL), m_VBIndices(NULL)
    50167    { }
    51168
    52169    ~CTexturedLineRData()
    public:  
    62179        SVertex(CVector3D pos, float u, float v) : m_Position(pos) { m_UVs[0] = u; m_UVs[1] = v; }
    63180        CVector3D m_Position;
    64181        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
     182        float _padding[3]; // get a pow2 struct size
    66183    };
    67184    cassert(sizeof(SVertex) == 32);
    68185
    public:  
    91208    SOverlayTexturedLine* m_Line;
    92209    CVertexBuffer::VBChunk* m_VB;
    93210    CVertexBuffer::VBChunk* m_VBIndices;
    94 
    95     float m_Raise; // small vertical offset of line from terrain to prevent visual glitches
    96211};
    97212
     213static size_t hash_value(const QuadBatchKey& d)
     214{
     215    size_t seed = 0;
     216    boost::hash_combine(seed, d.m_Texture);
     217    boost::hash_combine(seed, d.m_TextureMask);
     218    return seed;
     219}
     220
    98221OverlayRenderer::OverlayRenderer()
    99222{
    100223    m = new OverlayRendererInternals();
    void OverlayRenderer::Submit(SOverlaySprite* overlay)  
    128251    m->sprites.push_back(overlay);
    129252}
    130253
     254void OverlayRenderer::Submit(SOverlayQuad* overlay)
     255{
     256    m->quads.push_back(overlay);
     257}
     258
    131259void OverlayRenderer::EndFrame()
    132260{
    133261    m->lines.clear();
    134262    m->texlines.clear();
    135263    m->sprites.clear();
     264    m->quads.clear();
    136265    // this should leave the capacity unchanged, which is okay since it
    137266    // won't be very large or very variable
     267   
     268    // Empty the batch rendering data structures, but keep their key mappings around for the next frames
     269    for (OverlayRendererInternals::QuadBatchMap::iterator it = m->quadBatchMap.begin(); it != m->quadBatchMap.end(); it++)
     270    {
     271        it->second.m_Quads.clear();
     272    }
    138273}
    139274
    140275void OverlayRenderer::PrepareForRendering()
    void OverlayRenderer::PrepareForRendering()  
    157292            // any of the parameters after first submitting the line.
    158293        }
    159294    }
     295
     296    // Group quad overlays by their texture/mask combination for efficient rendering
     297    for (size_t i = 0; i < m->quads.size(); ++i)
     298    {
     299        SOverlayQuad* const quad = m->quads[i];
     300
     301        QuadBatchKey textures(quad->m_Texture, quad->m_TextureMask);
     302        QuadBatchData& batchRenderData = m->quadBatchMap[textures]; // will create entry if it doesn't already exist
     303
     304        // add overlay to list of quads
     305        batchRenderData.m_Quads.push_back(quad);
     306    }
     307
     308    const CVector3D vOffset(0, OVERLAY_VOFFSET, 0);
     309
     310    // Write quad overlay vertices/indices to VA backing store
     311    VertexArrayIterator<CVector3D> vertexPos = m->quadAttributePos.GetIterator<CVector3D>();
     312    VertexArrayIterator<CVector4D> vertexColor = m->quadAttributeColor.GetIterator<CVector4D>();
     313    VertexArrayIterator<short[2]> vertexUV = m->quadAttributeUV.GetIterator<short[2]>();
     314
     315    size_t indicesIdx = 0;
     316
     317    for (OverlayRendererInternals::QuadBatchMap::iterator it = m->quadBatchMap.begin(); it != m->quadBatchMap.end(); ++it)
     318    {
     319        QuadBatchData& batchRenderData = (it->second);
     320        if (batchRenderData.m_Quads.empty())
     321            continue;
     322
     323        // Remember our current index into the (entire) indices array as our base offset for this batch
     324        batchRenderData.m_IndicesBase = indicesIdx;
     325
     326        // points to the index where each iteration's vertices will be appended
     327        for (size_t i = 0; i < batchRenderData.m_Quads.size(); i++)
     328        {
     329            const SOverlayQuad* quad = batchRenderData.m_Quads[i];
     330
     331            // TODO: this is kind of ugly, the iterator should use a type that can have quad->m_Color assigned
     332            // to it directly
     333            const CVector4D quadColor(quad->m_Color.r, quad->m_Color.g, quad->m_Color.b, quad->m_Color.a);
     334
     335            *vertexPos++ = quad->m_Corners[0] + vOffset;
     336            *vertexPos++ = quad->m_Corners[1] + vOffset;
     337            *vertexPos++ = quad->m_Corners[2] + vOffset;
     338            *vertexPos++ = quad->m_Corners[3] + vOffset;
     339           
     340            (*vertexUV)[0] = 0;
     341            (*vertexUV)[1] = 0;
     342            ++vertexUV;
     343            (*vertexUV)[0] = 0;
     344            (*vertexUV)[1] = 1;
     345            ++vertexUV;
     346            (*vertexUV)[0] = 1;
     347            (*vertexUV)[1] = 1;
     348            ++vertexUV;
     349            (*vertexUV)[0] = 1;
     350            (*vertexUV)[1] = 0;
     351            ++vertexUV;
     352
     353            *vertexColor++ = quadColor;
     354            *vertexColor++ = quadColor;
     355            *vertexColor++ = quadColor;
     356            *vertexColor++ = quadColor;
     357
     358            indicesIdx += 6;
     359        }
     360    }
     361
     362    m->quadVertices.Upload();
     363    // don't free the backing store! we'll overwrite it on the next frame to save a reallocation.
    160364}
    161365
    162366void OverlayRenderer::RenderOverlaysBeforeWater()
    void OverlayRenderer::RenderOverlaysAfterWater()  
    196400{
    197401    PROFILE3_GPU("overlays (after)");
    198402
     403    RenderTexturedOverlayLines();
     404    RenderQuadOverlays();
     405}
     406
     407void OverlayRenderer::RenderTexturedOverlayLines()
     408{
    199409#if CONFIG2_GLES
    200 #warning TODO: implement OverlayRenderer::RenderOverlaysAfterWater for GLES
     410#warning TODO: implement OverlayRenderer::RenderTexturedOverlayLines for GLES
    201411    return;
    202412#endif
     413    if (m->texlines.empty())
     414        return;
    203415
    204416    ogl_WarnIfError();
    205417
    206     if (!m->texlines.empty())
    207     {
    208         glEnable(GL_TEXTURE_2D);
    209         glEnable(GL_BLEND);
    210         glDepthMask(0);
    211 
    212         const char* shaderName;
    213         if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER)
    214             shaderName = "arb/overlayline";
    215         else
    216             shaderName = "fixed:overlayline";
     418    glEnable(GL_TEXTURE_2D);
     419    glEnable(GL_BLEND);
     420    glDepthMask(0);
    217421
    218         CShaderDefines defAlwaysVisible;
    219         defAlwaysVisible.Add("IGNORE_LOS", "1");
     422    const char* shaderName;
     423    if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER)
     424        shaderName = "arb/overlayline";
     425    else
     426        shaderName = "fixed:overlayline";
    220427
    221         CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
     428    CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
    222429
    223         CShaderManager& shaderManager = g_Renderer.GetShaderManager();
    224         CShaderProgramPtr shaderTexLineNormal(shaderManager.LoadProgram(shaderName, CShaderDefines()));
    225         CShaderProgramPtr shaderTexLineAlwaysVisible(shaderManager.LoadProgram(shaderName, defAlwaysVisible));
     430    CShaderManager& shaderManager = g_Renderer.GetShaderManager();
     431    CShaderProgramPtr shaderTexLineNormal(shaderManager.LoadProgram(shaderName, m->defsOverlayLineNormal));
     432    CShaderProgramPtr shaderTexLineAlwaysVisible(shaderManager.LoadProgram(shaderName, m->defsOverlayLineAlwaysVisible));
    226433
    227         // ----------------------------------------------------------------------------------------
     434    // ----------------------------------------------------------------------------------------
    228435
     436    if (shaderTexLineNormal)
     437    {
    229438        shaderTexLineNormal->Bind();
    230439        shaderTexLineNormal->BindTexture("losTex", los.GetTexture());
    231440        shaderTexLineNormal->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
    void OverlayRenderer::RenderOverlaysAfterWater()  
    234443        RenderTexturedOverlayLines(shaderTexLineNormal, false);
    235444
    236445        shaderTexLineNormal->Unbind();
     446    }
    237447
    238         // ----------------------------------------------------------------------------------------
     448    // ----------------------------------------------------------------------------------------
    239449
     450    if (shaderTexLineAlwaysVisible)
     451    {
    240452        shaderTexLineAlwaysVisible->Bind();
    241         // TODO: losTex and losTransform are unused in the always visible shader, but I'm not sure if it's worthwhile messing
    242         // with it just to remove these calls
     453        // TODO: losTex and losTransform are unused in the always visible shader; see if these can be safely omitted
    243454        shaderTexLineAlwaysVisible->BindTexture("losTex", los.GetTexture());
    244455        shaderTexLineAlwaysVisible->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
    245456
    void OverlayRenderer::RenderOverlaysAfterWater()  
    247458        RenderTexturedOverlayLines(shaderTexLineAlwaysVisible, true);
    248459
    249460        shaderTexLineAlwaysVisible->Unbind();
     461    }
    250462
    251         // TODO: the shader should probably be responsible for unbinding its textures
    252         g_Renderer.BindTexture(1, 0);
    253         g_Renderer.BindTexture(0, 0);
     463    // ----------------------------------------------------------------------------------------
    254464
    255         CVertexBuffer::Unbind();
     465    // TODO: the shaders should probably be responsible for unbinding their textures
     466    g_Renderer.BindTexture(1, 0);
     467    g_Renderer.BindTexture(0, 0);
    256468
    257         glDepthMask(1);
    258         glDisable(GL_BLEND);
    259     }
     469    CVertexBuffer::Unbind();
     470
     471    glDepthMask(1);
     472    glDisable(GL_BLEND);
    260473}
    261474
    262475void OverlayRenderer::RenderTexturedOverlayLines(CShaderProgramPtr shaderTexLine, bool alwaysVisible)
    void OverlayRenderer::RenderTexturedOverlayLines(CShaderProgramPtr shaderTexLine  
    298511        shaderTexLine->AssertPointersBound();
    299512        glDrawElements(GL_TRIANGLES, rdata->m_VBIndices->m_Count, GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*rdata->m_VBIndices->m_Index);
    300513
     514        g_Renderer.GetStats().m_DrawCalls++;
    301515        g_Renderer.GetStats().m_OverlayTris += rdata->m_VBIndices->m_Count/3;
    302516    }
    303517}
    304518
     519void OverlayRenderer::RenderQuadOverlays()
     520{
     521    if (m->quadBatchMap.empty())
     522        return;
     523
     524    ogl_WarnIfError();
     525
     526    glEnable(GL_TEXTURE_2D);
     527    glEnable(GL_BLEND);
     528    glDepthMask(0);
     529
     530    const char* shaderName;
     531    if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER)
     532        shaderName = "arb/overlayline";
     533    else
     534        shaderName = "fixed:overlayline";
     535
     536    // TODO: create an FFP version of this shader?
     537
     538    CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
     539
     540    CShaderManager& shaderManager = g_Renderer.GetShaderManager();
     541    CShaderProgramPtr shader(shaderManager.LoadProgram(shaderName, m->defsQuadOverlay));
     542
     543    // ----------------------------------------------------------------------------------------
     544
     545    if (shader)
     546    {
     547        shader->Bind();
     548        shader->BindTexture("losTex", los.GetTexture());
     549        shader->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
     550
     551        // Base offsets (in bytes) of the two backing stores relative to their owner VBO
     552        // (the backing stores are chunks inside a larger, bound VBO)
     553        u8* indexBase = m->quadIndices.Bind();
     554        u8* vertexBase = m->quadVertices.Bind();
     555        GLsizei indexStride = m->quadIndices.GetStride();
     556        GLsizei vertexStride = m->quadVertices.GetStride();
     557
     558        for (OverlayRendererInternals::QuadBatchMap::iterator it = m->quadBatchMap.begin(); it != m->quadBatchMap.end(); it++)
     559        {
     560            QuadBatchData& batchRenderData = it->second;
     561            const size_t batchNumQuads = batchRenderData.m_Quads.size();
     562
     563            // Careful; some drivers don't like drawing calls with 0 stuff to draw.
     564            // Also needed to ensure that batchRenderData.m_IndicesBase is valid (see PrepareForRendering).
     565            if (batchNumQuads == 0)
     566                continue;
     567
     568            const QuadBatchKey& maskPair = it->first;
     569
     570            shader->BindTexture("baseTex", maskPair.m_Texture->GetHandle());
     571            shader->BindTexture("maskTex", maskPair.m_TextureMask->GetHandle());
     572
     573            int streamflags = shader->GetStreamFlags(); ogl_WarnIfError();
     574
     575            if (streamflags & STREAM_POS)
     576                shader->VertexPointer(m->quadAttributePos.elems, m->quadAttributePos.type, vertexStride, vertexBase + m->quadAttributePos.offset);
     577
     578            if (streamflags & STREAM_UV0)
     579                shader->TexCoordPointer(GL_TEXTURE0, m->quadAttributeUV.elems, m->quadAttributeUV.type, vertexStride, vertexBase + m->quadAttributeUV.offset);
     580
     581            if (streamflags & STREAM_UV1)
     582                shader->TexCoordPointer(GL_TEXTURE1, m->quadAttributeUV.elems, m->quadAttributeUV.type, vertexStride, vertexBase + m->quadAttributeUV.offset);
     583           
     584            if (streamflags & STREAM_COLOR)
     585                shader->ColorPointer(m->quadAttributeColor.elems, m->quadAttributeColor.type, vertexStride, vertexBase + m->quadAttributeColor.offset);
     586           
     587            shader->AssertPointersBound();
     588            glDrawElements(GL_TRIANGLES, (GLsizei)(batchNumQuads * 6), GL_UNSIGNED_SHORT, indexBase + indexStride * batchRenderData.m_IndicesBase);
     589
     590            g_Renderer.GetStats().m_DrawCalls++;
     591            g_Renderer.GetStats().m_OverlayTris += batchNumQuads*2;
     592        }
     593
     594        shader->Unbind();
     595    }
     596
     597    // ----------------------------------------------------------------------------------------
     598
     599    // TODO: the shader should probably be responsible for unbinding its textures
     600    g_Renderer.BindTexture(1, 0);
     601    g_Renderer.BindTexture(0, 0);
     602
     603    CVertexBuffer::Unbind();
     604
     605    glDepthMask(1);
     606    glDisable(GL_BLEND);
     607}
     608
    305609void OverlayRenderer::RenderForegroundOverlays(const CCamera& viewCamera)
    306610{
    307611    PROFILE3_GPU("overlays (fg)");
    void OverlayRenderer::RenderForegroundOverlays(const CCamera& viewCamera)  
    339643
    340644        glVertexPointer(3, GL_FLOAT, sizeof(float)*3, &pos[0].X);
    341645        glDrawArrays(GL_QUADS, 0, (GLsizei)4);
     646
     647        g_Renderer.GetStats().m_DrawCalls++;
     648        g_Renderer.GetStats().m_OverlayTris += 2;
    342649    }
    343650
    344651    glDisableClientState(GL_VERTEX_ARRAY);
    void CTexturedLineRData::Update()  
    363670        m_VBIndices = NULL;
    364671    }
    365672
    366     CTerrain* terrain = m_Line->m_Terrain;
    367     CmpPtr<ICmpWaterManager> cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
     673    if (!m_Line->m_SimContext)
     674    {
     675        debug_warn(L"[OverlayRenderer] No SimContext set for textured overlay line, cannot render (no terrain data)");
     676        return;
     677    }
     678
     679    const CTerrain& terrain = m_Line->m_SimContext->GetTerrain();
     680    CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Line->m_SimContext, SYSTEM_ENTITY);
    368681
    369682    float v = 0.f;
    370683    std::vector<SVertex> vertices;
    void CTexturedLineRData::Update()  
    400713    // TODO: if we ever support more than one water level per map, recompute this per point
    401714    float w = cmpWaterManager->GetExactWaterLevel(p0.X, p0.Z);
    402715
    403     p0.Y = terrain->GetExactGroundLevel(p0.X, p0.Z);
     716    p0.Y = terrain.GetExactGroundLevel(p0.X, p0.Z);
    404717    if (p0.Y < w)
    405718        p0.Y = w;
    406719
    407     p1.Y = terrain->GetExactGroundLevel(p1.X, p1.Z);
     720    p1.Y = terrain.GetExactGroundLevel(p1.X, p1.Z);
    408721    if (p1.Y < w)
    409722    {
    410723        p1.Y = w;
    411724        p1floating = true;
    412725    }
    413726
    414     p2.Y = terrain->GetExactGroundLevel(p2.X, p2.Z);
     727    p2.Y = terrain.GetExactGroundLevel(p2.X, p2.Z);
    415728    if (p2.Y < w)
    416729    {
    417730        p2.Y = w;
    void CTexturedLineRData::Update()  
    428741        if (p1floating)
    429742            norm = CVector3D(0, 1, 0);
    430743        else
    431             norm = m_Line->m_Terrain->CalcExactNormal(p1.X, p1.Z);
     744            norm = terrain.CalcExactNormal(p1.X, p1.Z);
    432745
    433746        CVector3D b = ((p1 - p0).Normalized() + (p2 - p1).Normalized()).Cross(norm);
    434747
    void CTexturedLineRData::Update()  
    447760        // What the code below does is push the indices for a quad composed of two triangles in each iteration. The two triangles
    448761        // of each quad are indexed using the winding orders (BR, BL, TR) and (TR, BL, TR) (where BR is bottom-right of this
    449762        // iteration's quad, TR top-right etc).
    450         SVertex vertex1(p1 + b + norm*m_Raise, 0.f, v);
    451         SVertex vertex2(p1 - b + norm*m_Raise, 1.f, v);
     763        SVertex vertex1(p1 + b + norm*OverlayRenderer::OVERLAY_VOFFSET, 0.f, v);
     764        SVertex vertex2(p1 - b + norm*OverlayRenderer::OVERLAY_VOFFSET, 1.f, v);
    452765        vertices.push_back(vertex1);
    453766        vertices.push_back(vertex2);
    454767
    void CTexturedLineRData::Update()  
    497810        else
    498811            p2 = CVector3D(m_Line->m_Coords[((i+2) % n)*2], 0, m_Line->m_Coords[((i+2) % n)*2+1]);
    499812
    500         p2.Y = terrain->GetExactGroundLevel(p2.X, p2.Z);
     813        p2.Y = terrain.GetExactGroundLevel(p2.X, p2.Z);
    501814        if (p2.Y < w)
    502815        {
    503816            p2.Y = w;
    void CTexturedLineRData::Update()  
    540853
    541854        for (unsigned i = 0; i < capIndices.size(); i++)
    542855            capIndices[i] += vertices.size();
     856
    543857        vertices.insert(vertices.end(), capVertices.begin(), capVertices.end());
    544858        indices.insert(indices.end(), capIndices.begin(), capIndices.end());
    545859
    void CTexturedLineRData::Update()  
    560874
    561875        for (unsigned i = 0; i < capIndices.size(); i++)
    562876            capIndices[i] += vertices.size();
     877
    563878        vertices.insert(vertices.end(), capVertices.begin(), capVertices.end());
    564879        indices.insert(indices.end(), capIndices.begin(), capIndices.end());
    565880    }
    void CTexturedLineRData::CreateLineCap(const CVector3D& corner1, const CVector3D  
    595910    // That is to say, when viewed from the top, we will have something like
    596911    //                                                 .
    597912    //  this:                     and not like this:  /|
    598     //         ____.                                 / |
     913    //         ----+                                 / |
    599914    //             |                                /  .
    600915    //             |                                  /
    601     //         ____.                                 /
     916    //         ----+                                 /
    602917    //
    603918
    604919    int roundCapPoints = 8; // amount of points to sample along the semicircle for rounded caps (including corner points)
  • source/renderer/OverlayRenderer.h

    diff --git a/source/renderer/OverlayRenderer.h b/source/renderer/OverlayRenderer.h
    index 6c1dfdc..a61656f 100644
    a b  
    1 /* Copyright (C) 2011 Wildfire Games.
     1/* Copyright (C) 2012 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    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. The pointed-to object must remain valid at least
     44     *                until the end of the frame.
    4245     */
    4346    void Submit(SOverlayLine* overlay);
    4447
    4548    /**
    4649     * Add a textured line overlay for rendering in this frame.
     50     * @param overlay Must be non-null. The pointed-to object must remain valid at least
     51     *                until the end of the frame.
    4752     */
    4853    void Submit(SOverlayTexturedLine* overlay);
    4954
    5055    /**
    5156     * Add a sprite overlay for rendering in this frame.
     57     * @param overlay Must be non-null. The pointed-to object must remain valid at least
     58     *                until the end of the frame.
    5259     */
    5360    void Submit(SOverlaySprite* overlay);
    5461
    5562    /**
     63     * Add a textured quad overlay for rendering in this frame.
     64     * @param overlay Must be non-null. The pointed-to object must remain valid at least
     65     *                until the end of the frame.
     66     */
     67    void Submit(SOverlayQuad* overlay);
     68
     69    /**
    5670     * Prepare internal data structures for rendering.
    5771     * Must be called after all Submit calls for a frame, and before
    5872     * any rendering calls.
    public:  
    8599     */
    86100    void RenderForegroundOverlays(const CCamera& viewCamera);
    87101
     102    /// Small vertical offset of overlays from terrain to prevent visual glitches
     103    static const float OVERLAY_VOFFSET;
     104
    88105private:
    89106   
    90107    /**
    91      * Helper method; renders those overlay lines currently registered in the internals (i.e. in m->texlines) for which the
    92      * always visible flag equals @alwaysVisible. Used for batch rendering the overlay lines by their alwaysVisible status,
    93      * because this requires a separate shader to be used.
     108     * Helper method; renders all overlay lines currently registered in the internals. Batch-
     109     * renders textured overlay lines batched according to their visibility status by delegating
     110     * to RenderTexturedOverlayLines(CShaderProgramPtr, bool).
     111     */
     112    void RenderTexturedOverlayLines();
     113
     114    /**
     115     * Helper method; renders those overlay lines currently registered in the internals (i.e.
     116     * in m->texlines) for which the 'always visible' flag equals @p alwaysVisible. Used for
     117     * batch rendering the overlay lines according to their alwaysVisible status, as this
     118     * requires a separate shader to be used.
    94119     */
    95120    void RenderTexturedOverlayLines(CShaderProgramPtr shader, bool alwaysVisible);
    96121
     122    /**
     123     * Helper method; batch-renders all registered quad overlays, batched by their texture for effiency.
     124     */
     125    void RenderQuadOverlays();
     126
    97127private:
    98128    OverlayRendererInternals* m;
    99129};
  • source/renderer/Renderer.cpp

    diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp
    index d743ef2..e66c1fa 100644
    a b void CRenderer::Submit(SOverlaySprite* overlay)  
    15691569    m->overlayRenderer.Submit(overlay);
    15701570}
    15711571
     1572void CRenderer::Submit(SOverlayQuad* overlay)
     1573{
     1574    m->overlayRenderer.Submit(overlay);
     1575}
     1576
    15721577void CRenderer::Submit(CModelDecal* decal)
    15731578{
    15741579    m->terrainRenderer.Submit(decal);
  • source/renderer/Renderer.h

    diff --git a/source/renderer/Renderer.h b/source/renderer/Renderer.h
    index a6d1b2b..df82579 100644
    a b protected:  
    316316    void Submit(SOverlayLine* overlay);
    317317    void Submit(SOverlayTexturedLine* overlay);
    318318    void Submit(SOverlaySprite* overlay);
     319    void Submit(SOverlayQuad* overlay);
    319320    void Submit(CModelDecal* decal);
    320321    void Submit(CParticleEmitter* emitter);
    321322    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/VertexArray.cpp

    diff --git a/source/renderer/VertexArray.cpp b/source/renderer/VertexArray.cpp
    index 6bf1fed..42d769b 100644
    a b  
    2222#include "lib/sysdep/rtl.h"
    2323#include "maths/Vector3D.h"
    2424#include "maths/Vector4D.h"
     25#include "graphics/Color.h"
    2526#include "graphics/SColor.h"
    2627#include "renderer/VertexArray.h"
    2728#include "renderer/VertexBuffer.h"
    void VertexArray::SetNumVertices(size_t num)  
    7374// Add vertex attributes like Position, Normal, UV
    7475void VertexArray::AddAttribute(Attribute* attr)
    7576{
    76     ENSURE((attr->type == GL_FLOAT || attr->type == GL_UNSIGNED_SHORT || attr->type == GL_UNSIGNED_BYTE) && "Unsupported attribute type");
     77    ENSURE(
     78        (attr->type == GL_FLOAT || attr->type == GL_SHORT || attr->type == GL_UNSIGNED_SHORT || attr->type == GL_UNSIGNED_BYTE)
     79        && "Unsupported attribute type"
     80    );
    7781    ENSURE(attr->elems >= 1 && attr->elems <= 4);
    7882
    7983    attr->vertexArray = this;
    VertexArrayIterator<u16> VertexArray::Attribute::GetIterator<u16>() const  
    147151}
    148152
    149153template<>
     154VertexArrayIterator<u16[2]> VertexArray::Attribute::GetIterator<u16[2]>() const
     155{
     156    ENSURE(vertexArray);
     157    ENSURE(type == GL_UNSIGNED_SHORT);
     158    ENSURE(elems >= 2);
     159
     160    return vertexArray->MakeIterator<u16[2]>(this);
     161}
     162
     163template<>
    150164VertexArrayIterator<u8> VertexArray::Attribute::GetIterator<u8>() const
    151165{
    152166    ENSURE(vertexArray);
    VertexArrayIterator<u8[4]> VertexArray::Attribute::GetIterator<u8[4]>() const  
    166180    return vertexArray->MakeIterator<u8[4]>(this);
    167181}
    168182
     183template<>
     184VertexArrayIterator<short> VertexArray::Attribute::GetIterator<short>() const
     185{
     186    ENSURE(vertexArray);
     187    ENSURE(type == GL_SHORT);
     188    ENSURE(elems >= 1);
     189
     190    return vertexArray->MakeIterator<short>(this);
     191}
     192
     193template<>
     194VertexArrayIterator<short[2]> VertexArray::Attribute::GetIterator<short[2]>() const
     195{
     196    ENSURE(vertexArray);
     197    ENSURE(type == GL_SHORT);
     198    ENSURE(elems >= 2);
    169199
     200    return vertexArray->MakeIterator<short[2]>(this);
     201}
    170202
    171203static size_t RoundStride(size_t stride)
    172204{
    void VertexArray::Layout()  
    206238        case GL_UNSIGNED_BYTE:
    207239            attrSize = sizeof(GLubyte);
    208240            break;
     241        case GL_SHORT:
     242            attrSize = sizeof(GLshort);
     243            break;
    209244        case GL_UNSIGNED_SHORT:
    210245            attrSize = sizeof(GLushort);
    211246            break;
  • source/renderer/VertexArray.h

    diff --git a/source/renderer/VertexArray.h b/source/renderer/VertexArray.h
    index 7cf6cfa..4960052 100644
    a b  
    1 /* Copyright (C) 2011 Wildfire Games.
     1/* Copyright (C) 2012 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    class VertexArray  
    134134public:
    135135    struct Attribute
    136136    {
    137         // Data type. Currently supported: GL_FLOAT, GL_UNSIGNED_BYTE
     137        // Data type. Currently supported: GL_FLOAT, GL_SHORT, GL_UNSIGNED_SHORT, GL_UNSIGNED_BYTE.
    138138        GLenum type;
    139139        // How many elements per vertex (e.g. 3 for RGB, 2 for UV)
    140140        GLuint elems;
    public:  
    146146
    147147        Attribute() : type(0), elems(0), offset(0), vertexArray(0) { }
    148148   
    149         // Get an iterator for the given attribute that initially points at the first vertex.
    150         // Supported types T: CVector3D, CVector4D, float[2], SColor3ub, SColor4ub
     149        // Get an iterator over the backing store for the given attribute that
     150        // initially points at the first vertex.
     151        // Supported types T: CVector3D, CVector4D, float[2], SColor3ub, SColor4ub,
     152        // u16, u16[2], u8, u8[4], short, short[2].
    151153        // This function verifies at runtime that the requested type T matches
    152154        // the attribute definition passed to AddAttribute().
    153155        template<typename T>
    public:  
    171173    // attributes.
    172174    // All vertex data is lost when a vertex array is re-layouted.
    173175    void Layout();
    174     // (Re-)Upload the attributes of the vertex array.
     176    // (Re-)Upload the attributes of the vertex array from the backing store to
     177    // the underlying VBO object.
    175178    void Upload();
    176179    // Bind this array, returns the base address for calls to glVertexPointer etc.
    177180    u8* Bind();
    private:  
    204207 * A VertexArray that is specialised to handle 16-bit array indices.
    205208 * Call Bind() and pass the return value to the indices parameter of
    206209 * glDrawElements/glDrawRangeElements/glMultiDrawElements.
    207  * Use CVertexBuffer::Unbind() to unbind the array.
     210 * Use CVertexBuffer::Unbind() to unbind the array when done.
    208211 */
    209212class VertexIndexArray : public VertexArray
    210213{
    211214public:
    212215    VertexIndexArray(GLenum usage);
    213216
     217    /// Gets the iterator over the (only) attribute in this array, i.e. a u16.
    214218    VertexArrayIterator<u16> GetIterator() const;
    215219
    216220private:
  • source/renderer/VertexBuffer.cpp

    diff --git a/source/renderer/VertexBuffer.cpp b/source/renderer/VertexBuffer.cpp
    index bcbfebe..514d6eb 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)
    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 816d83f..c426868 100644
    a b  
    1 /* Copyright (C) 2011 Wildfire Games.
     1/* Copyright (C) 2012 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    3333// TODO: measure what influence this has on performance
    3434#define MAX_VB_SIZE_BYTES       (2*1024*1024)
    3535
    36 ///////////////////////////////////////////////////////////////////////////////
    37 // CVertexBuffer: encapsulation of ARB_vertex_buffer_object, also supplying
    38 // some additional functionality for sharing buffers between multiple objects
     36/**
     37 * CVertexBuffer: encapsulation of ARB_vertex_buffer_object, also supplying
     38 * some additional functionality for sharing buffers between multiple objects
     39 */
    3940class CVertexBuffer
    4041{
    4142public:
    42     // VBChunk: describes a portion of this vertex buffer
     43
     44    /// VBChunk: describes a portion of this vertex buffer
    4345    struct VBChunk
    4446    {
    45         // owning buffer
     47        /// Owning (parent) vertex buffer
    4648        CVertexBuffer* m_Owner;
    47         // start index of this chunk in owner
     49        /// Start index of this chunk in owner
    4850        size_t m_Index;
    49         // number of vertices used by chunk
     51        /// Number of vertices used by chunk
    5052        size_t m_Count;
    5153
    5254    private:
    public:  
    6264    CVertexBuffer(size_t vertexSize, GLenum usage, GLenum target);
    6365    ~CVertexBuffer();
    6466
    65     // bind to this buffer; return pointer to address required as parameter
    66     // to glVertexPointer ( + etc) calls
     67    /// Bind to this buffer; return pointer to address required as parameter
     68    /// to glVertexPointer ( + etc) calls
    6769    u8* Bind();
    6870
    69     // get the address that Bind() will return, without actually binding
     71    /// Get the address that Bind() will return, without actually binding
    7072    u8* GetBindAddress();
    7173
    72     // unbind any currently-bound buffer, so glVertexPointer etc calls will not attempt to use it
     74    /// Unbind any currently-bound buffer, so glVertexPointer etc calls will not attempt to use it
    7375    static void Unbind();
    7476
    75     // update vertex data for given chunk
     77    /// Update vertex data for given chunk. Transfers the provided data to the actual OpenGL vertex buffer.
    7678    void UpdateChunkVertices(VBChunk* chunk, void* data);
    7779
    7880    size_t GetVertexSize() const { return m_VertexSize; }
    79 
    8081    size_t GetBytesReserved() const;
    8182    size_t GetBytesAllocated() const;
    8283
     84    /// Returns true if this vertex buffer is compatible with the specified vertex type and intended usage.
    8385    bool CompatibleVertexType(size_t vertexSize, GLenum usage, GLenum target);
    8486
    8587    void DumpStatus();
    public:  
    8789protected:
    8890    friend class CVertexBufferManager;      // allow allocate only via CVertexBufferManager
    8991   
    90     // try to allocate a buffer of given number of vertices (each of given size),
    91     // and with the given type - return null if no free chunks available
     92    /// Try to allocate a buffer of given number of vertices (each of given size),
     93    /// and with the given type - return null if no free chunks available
    9294    VBChunk* Allocate(size_t vertexSize, size_t numVertices, GLenum usage, GLenum target);
    93     // return given chunk to this buffer
     95    /// Return given chunk to this buffer
    9496    void Release(VBChunk* chunk);
    9597   
    9698   
    9799private:   
    98     // vertex size of this vertex buffer
     100    /// Vertex size of this vertex buffer
    99101    size_t m_VertexSize;
    100     // number of vertices of above size in this buffer
     102    /// Number of vertices of above size in this buffer
    101103    size_t m_MaxVertices;
    102     // list of free chunks in this buffer
     104    /// List of free chunks in this buffer
    103105    std::list<VBChunk*> m_FreeList;
    104     // available free vertices - total of all free vertices in the free list
     106    /// Available free vertices - total of all free vertices in the free list
    105107    size_t m_FreeVertices;
    106     // handle to the actual GL vertex buffer object
     108    /// Handle to the actual GL vertex buffer object
    107109    GLuint m_Handle;
    108     // raw system memory for systems not supporting VBOs
     110    /// Raw system memory for systems not supporting VBOs
    109111    u8* m_SysMem;
    110     // usage type of the buffer (GL_STATIC_DRAW etc)
     112    /// Usage type of the buffer (GL_STATIC_DRAW etc)
    111113    GLenum m_Usage;
    112     // buffer target (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER)
     114    /// Buffer target (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER)
    113115    GLenum m_Target;
    114116};
    115117
  • source/renderer/VertexBufferManager.h

    diff --git a/source/renderer/VertexBufferManager.h b/source/renderer/VertexBufferManager.h
    index b97c68d..4ccb02f 100644
    a b  
    1 /* Copyright (C) 2011 Wildfire Games.
     1/* Copyright (C) 2012 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    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 dc16628..0a52b6f 100644
    a b void CCmpRallyPointRenderer::ConstructOverlayLines()  
    610610            // construct solid textured overlay line along a subset of the full path points from startPointIdx to endPointIdx
    611611            SOverlayTexturedLine overlayLine;
    612612            overlayLine.m_Thickness = m_LineThickness;
    613             overlayLine.m_Terrain = cmpTerrain->GetCTerrain();
     613            overlayLine.m_SimContext = &GetSimContext();
    614614            overlayLine.m_TextureBase = m_Texture;
    615615            overlayLine.m_TextureMask = m_TextureMask;
    616616            overlayLine.m_Color = m_LineColor;
    void CCmpRallyPointRenderer::ConstructOverlayLines()  
    634634        }
    635635        else
    636636        {
    637             // construct dashed line from startPointIdx to endPointIdx, add textured overlay lines for it to the render list
     637            // construct dashed line from startPointIdx to endPointIdx; add textured overlay lines for it to the render list
    638638            std::vector<CVector2D> straightLine;
    639639            straightLine.push_back(m_Path[segment.m_StartIndex]);
    640640            straightLine.push_back(m_Path[segment.m_EndIndex]);
    641641
    642             // 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
    643             // area is visually obvious. That implies that we want at least So, let's do some calculations to see what size we should make
    644             // the dashes and clears.
     642            // 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
     643            // area is visually obvious. This requires some calculations to see what size we should make the dashes and clears for them
     644            // to fit exactly.
    645645
    646646            float maxDashSize = 3.f;
    647647            float maxClearSize = 3.f;
    void CCmpRallyPointRenderer::ConstructOverlayLines()  
    652652
    653653            float distance = (m_Path[segment.m_StartIndex] - m_Path[segment.m_EndIndex]).Length(); // straight-line distance between the points
    654654
    655             // see how many pairs (dash + clear) can fit into the distance unmodified. Then check the remaining distance; if it's not exactly
    656             // a dash size's worth (and it likely won't be), then adjust the dash/clear sizes slightly so that it is.
     655            // See how many pairs (dash + clear) of unmodified size can fit into the distance. Then check the remaining distance; if it's not exactly
     656            // a dash size's worth (which it probably won't be), then adjust the dash/clear sizes slightly so that it is.
    657657            int numFitUnmodified = floor(distance/(dashSize + clearSize));
    658658            float remainderDistance = distance - (numFitUnmodified * (dashSize + clearSize));
    659659
    void CCmpRallyPointRenderer::ConstructOverlayLines()  
    682682                SOverlayTexturedLine dashOverlay;
    683683
    684684                dashOverlay.m_Thickness = m_LineThickness;
    685                 dashOverlay.m_Terrain = cmpTerrain->GetCTerrain();
     685                dashOverlay.m_SimContext = &GetSimContext();
    686686                dashOverlay.m_TextureBase = m_Texture;
    687687                dashOverlay.m_TextureMask = m_TextureMask;
    688688                dashOverlay.m_Color = m_LineDashColor;
    void CCmpRallyPointRenderer::FixFootprintWaypoints(std::vector<CVector2D>& coord  
    767767    {
    768768    case ICmpFootprint::SQUARE:
    769769        {
    770             // in this case, footprintSize0 and 1 respectively indicate the (unrotated) size along the X and Z axes
     770            // in this case, footprintSize0 and 1 indicate the size along the X and Z axes, respectively.
    771771
    772772            // the building's footprint could be rotated any which way, so let's get the rotation around the Y axis
    773773            // and the rotated unit vectors in the X/Z plane of the shape's footprint
    void CCmpRallyPointRenderer::FixInvisibleWaypoints(std::vector<CVector2D>& coord  
    837837    player_id_t currentPlayer = GetSimContext().GetCurrentDisplayedPlayer();
    838838    CLosQuerier losQuerier(cmpRangeMgr->GetLosQuerier(currentPlayer));
    839839
    840     //for (std::vector<Waypoint>::iterator it = waypoints.begin(); it != waypoints.end();)
    841840    for(std::vector<CVector2D>::iterator it = coords.begin(); it != coords.end();)
    842841    {
    843842        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..b4b6460 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"
     26#include "maths/Ease.h"
    3027#include "maths/MathUtil.h"
    3128#include "maths/Matrix3D.h"
    3229#include "maths/Vector3D.h"
     30#include "maths/Vector2D.h"
     31#include "ps/CLogger.h"
    3332#include "renderer/Scene.h"
     33#include "renderer/Renderer.h"
     34#include "simulation2/MessageTypes.h"
     35#include "simulation2/components/ICmpPosition.h"
     36#include "simulation2/components/ICmpFootprint.h"
     37#include "simulation2/components/ICmpVisual.h"
     38#include "simulation2/components/ICmpTerrain.h"
     39#include "simulation2/components/ICmpOwnership.h"
     40#include "simulation2/components/ICmpPlayer.h"
     41#include "simulation2/components/ICmpPlayerManager.h"
     42#include "simulation2/components/ICmpWaterManager.h"
     43#include "simulation2/helpers/Render.h"
    3444
    3545class CCmpSelectable : public ICmpSelectable
    3646{
    public:  
    3949    {
    4050        componentManager.SubscribeToMessageType(MT_Interpolate);
    4151        componentManager.SubscribeToMessageType(MT_RenderSubmit);
     52        componentManager.SubscribeToMessageType(MT_OwnershipChanged);
     53        componentManager.SubscribeToMessageType(MT_PositionChanged);
    4254        // TODO: it'd be nice if we didn't get these messages except in the rare
    4355        // cases where we're actually drawing a selection highlight
    4456    }
    4557
    4658    DEFAULT_COMPONENT_ALLOCATOR(Selectable)
    4759
    48     SOverlayLine m_Overlay;
    49     SOverlayLine* m_DebugBoundingBoxOverlay;
    50     SOverlayLine* m_DebugSelectionBoxOverlay;
    51     bool m_EditorOnly;
    52 
    5360    CCmpSelectable()
    54         : m_DebugBoundingBoxOverlay(NULL), m_DebugSelectionBoxOverlay(NULL)
     61        : m_DebugBoundingBoxOverlay(NULL), m_DebugSelectionBoxOverlay(NULL),
     62          m_BuildingOverlay(NULL), m_UnitOverlay(NULL),
     63          m_FadeBaselineAlpha(0.f), m_FadeDeltaAlpha(0.f), m_FadeProgress(0.f)
    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_FadeBaselineAlpha);
    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' a:help='Texture to display underneath the entity. Filepath relative to art/textures/selection/.'><text/></element>"
     90                        "<element name='MainTextureMask' a:help='Mask texture that controls where to apply player color. Filepath relative to art/textures/selection/.'><text/></element>"
     91                    "</element>"
     92                    "<element name='Outline' a:help='Traces the outline of the entity with a line texture.'>"
     93                        "<element name='LineTexture' a:help='Texture to apply to the line. Filepath relative to art/textures/selection/.'><text/></element>"
     94                        "<element name='LineTextureMask' a:help='Texture that controls where to apply player color. Filepath relative to art/textures/selection/.'><text/></element>"
     95                        "<element name='LineThickness' a:help='Thickness of the line, in world units.'><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        const char* textureBasePath = "art/textures/selection/";
     109
     110        // Save some memory by using interned file paths in these descriptors (almost all actors and
     111        // entities have this component, and many use the same textures).
     112        if (textureNode.IsOk())
     113        {
     114            // textured quad mode (dynamic, for units)
     115            m_OverlayDescriptor.m_Type = ICmpSelectable::DYNAMIC_QUAD;
     116            m_OverlayDescriptor.m_QuadTexture = CStrIntern(textureBasePath + textureNode.GetChild("MainTexture").ToUTF8());
     117            m_OverlayDescriptor.m_QuadTextureMask = CStrIntern(textureBasePath + textureNode.GetChild("MainTextureMask").ToUTF8());
     118        }
     119        else if (outlineNode.IsOk())
     120        {
     121            // textured outline mode (static, for buildings)
     122            m_OverlayDescriptor.m_Type = ICmpSelectable::STATIC_OUTLINE;
     123            m_OverlayDescriptor.m_LineTexture = CStrIntern(textureBasePath + outlineNode.GetChild("LineTexture").ToUTF8());
     124            m_OverlayDescriptor.m_LineTextureMask = CStrIntern(textureBasePath + outlineNode.GetChild("LineTextureMask").ToUTF8());
     125            m_OverlayDescriptor.m_LineThickness = outlineNode.GetChild("LineThickness").ToFloat();
     126        }
    84127    }
    85128
     129    virtual void Deinit() { }
     130
    86131    virtual void Serialize(ISerializer& UNUSED(serialize))
    87132    {
    88133        // Nothing to do here (the overlay object is not worth saving, it'll get
    public:  
    95140        Init(paramNode);
    96141    }
    97142
    98     virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
     143    virtual void HandleMessage(const CMessage& msg, bool UNUSED(global));
     144
     145    virtual void SetSelectionHighlight(CColor color)
    99146    {
    100         switch (msg.GetType())
    101         {
    102         case MT_Interpolate:
     147        m_Color.r = color.r;
     148        m_Color.g = color.g;
     149        m_Color.b = color.b;
     150
     151        // set up fading from the current value (as the baseline) to the target value
     152        m_FadeBaselineAlpha = m_Color.a;
     153        m_FadeDeltaAlpha = color.a - m_FadeBaselineAlpha;
     154        m_FadeProgress = 0.f;
     155    }
     156
     157    virtual bool IsEditorOnly()
     158    {
     159        return m_EditorOnly;
     160    }
     161
     162    void RenderSubmit(SceneCollector& collector);
     163
     164    /**
     165     * Called from RenderSubmit if using a static outline; responsible for ensuring that the static overlay
     166     * is up-to-date before it is rendered. Has no effect unless the static overlay is explicitly marked as
     167     * invalid first (see InvalidateStaticOverlay).
     168     */
     169    void UpdateStaticOverlay();
     170
     171    /**
     172     * Called from the interpolation handler; responsible for ensuring the dynamic overlay (provided we're
     173     * using one) is up-to-date and ready to be submitted to the next rendering run.
     174     */
     175    void UpdateDynamicOverlay(float frameOffset);
     176
     177    /// Explicitly invalidates the static overlay.
     178    void InvalidateStaticOverlay();
     179
     180private:
     181    SOverlayDescriptor m_OverlayDescriptor;
     182    SOverlayTexturedLine* m_BuildingOverlay;
     183    SOverlayQuad* m_UnitOverlay;
     184
     185    SOverlayLine* m_DebugBoundingBoxOverlay;
     186    SOverlayLine* m_DebugSelectionBoxOverlay;
     187
     188    bool m_EditorOnly;
     189   
     190    /// Current selection overlay color. Alpha component is subject to fading.
     191    CColor m_Color;
     192    /// Baseline alpha value to start fading from. Constant during a single fade.
     193    float m_FadeBaselineAlpha;
     194    /// Delta between target and baseline alpha. Constant during a single fade. Can be positive or negative.
     195    float m_FadeDeltaAlpha;
     196    /// Linear time progress of the fade, between 0 and m_FadeDuration.
     197    float m_FadeProgress;
     198    /// Total duration of a single fade, in seconds. Assumed constant for now; feel free to change this into
     199    /// a member variable if you need to adjust it per component.
     200    static const float FADE_DURATION;
     201};
     202
     203const float CCmpSelectable::FADE_DURATION = 0.3f;
     204
     205void CCmpSelectable::HandleMessage(const CMessage& msg, bool UNUSED(global))
     206{
     207    switch (msg.GetType())
     208    {
     209    case MT_Interpolate:
    103210        {
    104             if (m_Overlay.m_Color.a > 0)
     211            const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg);
     212
     213            if (m_FadeDeltaAlpha != 0.f)
     214            {
     215                m_FadeProgress += msgData.frameTime;
     216                if (m_FadeProgress >= FADE_DURATION)
     217                {
     218                    const float targetAlpha = m_FadeBaselineAlpha + m_FadeDeltaAlpha;
     219
     220                    // stop the fade
     221                    m_Color.a = targetAlpha;
     222                    m_FadeBaselineAlpha = targetAlpha;
     223                    m_FadeDeltaAlpha = 0.f;
     224                    m_FadeProgress = FADE_DURATION; // will need to be reset to start the next fade again
     225                }
     226                else
     227                {
     228                    m_Color.a = Ease::QuartOut(m_FadeProgress, m_FadeBaselineAlpha, m_FadeDeltaAlpha, FADE_DURATION);
     229                }
     230            }
     231
     232            // update dynamic overlay only when visible
     233            if (m_Color.a > 0)
    105234            {
    106                 float offset = static_cast<const CMessageInterpolate&> (msg).offset;
    107                 ConstructShape(offset);
     235                UpdateDynamicOverlay(msgData.offset);
    108236            }
     237
    109238            break;
    110239        }
    111         case MT_RenderSubmit:
     240    case MT_OwnershipChanged:
    112241        {
    113             if (m_Overlay.m_Color.a > 0)
    114             {
    115                 const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg);
    116                 RenderSubmit(msgData.collector);
    117             }
     242            const CMessageOwnershipChanged& msgData = static_cast<const CMessageOwnershipChanged&> (msg);
     243
     244            // don't update color if there's no new owner (e.g. the unit died)
     245            if (msgData.to == INVALID_PLAYER)
     246                break;
     247
     248            // update the selection highlight color
     249            CmpPtr<ICmpPlayerManager> cmpPlayerManager(GetSimContext(), SYSTEM_ENTITY);
     250            if (!cmpPlayerManager)
     251                break;
     252
     253            CmpPtr<ICmpPlayer> cmpPlayer(GetSimContext(), cmpPlayerManager->GetPlayerByID(msgData.to));
     254            if (!cmpPlayer)
     255                break;
     256
     257            // Update the highlight color, while keeping the current alpha target value intact
     258            // (i.e. baseline + delta), so that any ongoing fades simply continue with the new color.
     259            CColor color = cmpPlayer->GetColour();
     260            SetSelectionHighlight(CColor(color.r, color.g, color.b, m_FadeBaselineAlpha + m_FadeDeltaAlpha));
     261        }
     262        // fall-through
     263    case MT_PositionChanged:
     264        {
     265            InvalidateStaticOverlay();
    118266            break;
    119267        }
     268    case MT_RenderSubmit:
     269        {
     270            const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg);
     271            RenderSubmit(msgData.collector);
     272
     273            break;
    120274        }
    121275    }
     276}
    122277
    123     virtual bool IsEditorOnly()
    124     {
    125         return m_EditorOnly;
    126     }
     278void CCmpSelectable::InvalidateStaticOverlay()
     279{
     280    SAFE_DELETE(m_BuildingOverlay);
     281}
    127282
    128     virtual void SetSelectionHighlight(CColor color)
     283void CCmpSelectable::UpdateStaticOverlay()
     284{
     285    // Static overlays are allocated once and not updated until they are explicitly deleted again
     286    // (see InvalidateStaticOverlay). Since they are expected to change rarely (if ever) during
     287    // normal gameplay, this saves us doing all the work below on each frame.
     288   
     289    if (m_BuildingOverlay || m_OverlayDescriptor.m_Type != STATIC_OUTLINE)
     290        return;
     291   
     292    if (!CRenderer::IsInitialised())
     293        return;
     294
     295    entity_id_t entityId = GetEntityId();
     296    CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), entityId);
     297    CmpPtr<ICmpFootprint> cmpFootprint(GetSimContext(), entityId);
     298    if (!cmpFootprint || !cmpPosition || !cmpPosition->IsInWorld())
     299        return;
     300
     301    CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY);
     302    if (!cmpTerrain)
     303        return; // should never happen
     304
     305    // grab position/footprint data
     306    CFixedVector2D position = cmpPosition->GetPosition2D();
     307    CFixedVector3D rotation = cmpPosition->GetRotation();
     308
     309    ICmpFootprint::EShape fpShape;
     310    entity_pos_t fpSize0_fixed, fpSize1_fixed, fpHeight_fixed;
     311    cmpFootprint->GetShape(fpShape, fpSize0_fixed, fpSize1_fixed, fpHeight_fixed);
     312
     313    CTextureProperties texturePropsBase(m_OverlayDescriptor.m_LineTexture.c_str());
     314    texturePropsBase.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE);
     315    texturePropsBase.SetMaxAnisotropy(4.f);
     316
     317    CTextureProperties texturePropsMask(m_OverlayDescriptor.m_LineTextureMask.c_str());
     318    texturePropsMask.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE);
     319    texturePropsMask.SetMaxAnisotropy(4.f);
     320
     321    // -------------------------------------------------------------------------------------
     322
     323    m_BuildingOverlay = new SOverlayTexturedLine;
     324    m_BuildingOverlay->m_AlwaysVisible = false;
     325    m_BuildingOverlay->m_Closed = true;
     326    m_BuildingOverlay->m_SimContext = &GetSimContext();
     327    m_BuildingOverlay->m_Thickness = m_OverlayDescriptor.m_LineThickness;
     328    m_BuildingOverlay->m_TextureBase = g_Renderer.GetTextureManager().CreateTexture(texturePropsBase);
     329    m_BuildingOverlay->m_TextureMask = g_Renderer.GetTextureManager().CreateTexture(texturePropsMask);
     330
     331    CVector2D origin(position.X.ToFloat(), position.Y.ToFloat());
     332
     333    switch (fpShape)
    129334    {
    130         m_Overlay.m_Color = color;
     335    case ICmpFootprint::SQUARE:
     336        {
     337            float s = sinf(-rotation.Y.ToFloat());
     338            float c = cosf(-rotation.Y.ToFloat());
     339            CVector2D unitX(c, s);
     340            CVector2D unitZ(-s, c);
     341
     342            // add half the line thickness to the radius so that we get an 'outside' stroke of the footprint shape
     343            const float halfSizeX = fpSize0_fixed.ToFloat()/2.f + m_BuildingOverlay->m_Thickness/2.f;
     344            const float halfSizeZ = fpSize1_fixed.ToFloat()/2.f + m_BuildingOverlay->m_Thickness/2.f;
    131345
    132         if (color.a == 0 && !m_Overlay.m_Coords.empty())
     346            std::vector<CVector2D> points;
     347            points.push_back(CVector2D(origin + unitX *  halfSizeX    + unitZ *(-halfSizeZ)));
     348            points.push_back(CVector2D(origin + unitX *(-halfSizeX)   + unitZ *(-halfSizeZ)));
     349            points.push_back(CVector2D(origin + unitX *(-halfSizeX)   + unitZ *  halfSizeZ));
     350            points.push_back(CVector2D(origin + unitX *  halfSizeX    + unitZ *  halfSizeZ));
     351
     352            SimRender::SubdividePoints(points, TERRAIN_TILE_SIZE/3.f, m_BuildingOverlay->m_Closed);
     353            m_BuildingOverlay->PushCoords(points);
     354        }
     355        break;
     356    case ICmpFootprint::CIRCLE:
    133357        {
    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);
     358            const float radius = fpSize0_fixed.ToFloat() + m_BuildingOverlay->m_Thickness/3.f;
     359            if (radius > 0) // prevent catastrophic failure
     360            {
     361                float stepAngle;
     362                unsigned numSteps;
     363                SimRender::AngularStepFromChordLen(TERRAIN_TILE_SIZE/3.f, radius, stepAngle, numSteps);
     364
     365                for (unsigned i = 0; i < numSteps; i++) // '<' is sufficient because the line is closed automatically
     366                {
     367                    float angle = i * stepAngle;
     368                    float px = origin.X + radius * sinf(angle);
     369                    float pz = origin.Y + radius * cosf(angle);
     370
     371                    m_BuildingOverlay->PushCoords(px, pz);
     372                }
     373            }
    139374        }
     375        break;
     376    }
    140377
    141         // TODO: it'd be nice to fade smoothly (but quickly) from transparent to solid
     378    ENSURE(m_BuildingOverlay);
     379}
     380
     381void CCmpSelectable::UpdateDynamicOverlay(float frameOffset)
     382{
     383    // Dynamic overlay lines are allocated once and never deleted. Since they are expected to change frequently,
     384    // they are assumed dirty on every call to this function, and we should therefore use this function more
     385    // thoughtfully than calling it right before every frame render.
     386   
     387    if (m_OverlayDescriptor.m_Type != DYNAMIC_QUAD)
     388        return;
     389
     390    if (!CRenderer::IsInitialised())
     391        return;
     392
     393    entity_id_t entityId = GetEntityId();
     394    CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), entityId);
     395    CmpPtr<ICmpFootprint> cmpFootprint(GetSimContext(), entityId);
     396    if (!cmpFootprint || !cmpPosition || !cmpPosition->IsInWorld())
     397        return;
     398
     399    float rotY;
     400    CVector2D position;
     401    cmpPosition->GetInterpolatedPosition2D(frameOffset, position.X, position.Y, rotY);
     402
     403    CmpPtr<ICmpWaterManager> cmpWaterManager(GetSimContext(), SYSTEM_ENTITY);
     404    CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY);
     405    ENSURE(cmpWaterManager && cmpTerrain);
     406
     407    CTerrain* terrain = cmpTerrain->GetCTerrain();
     408    ENSURE(terrain);
     409
     410    ICmpFootprint::EShape fpShape;
     411    entity_pos_t fpSize0_fixed, fpSize1_fixed, fpHeight_fixed;
     412    cmpFootprint->GetShape(fpShape, fpSize0_fixed, fpSize1_fixed, fpHeight_fixed);
     413
     414    // ---------------------------------------------------------------------------------
     415
     416    if (!m_UnitOverlay)
     417    {
     418        m_UnitOverlay = new SOverlayQuad;
     419
     420        // Assuming we don't need the capability of swapping textures on-demand.
     421        CTextureProperties texturePropsBase(m_OverlayDescriptor.m_QuadTexture.c_str());
     422        texturePropsBase.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE);
     423        texturePropsBase.SetMaxAnisotropy(4.f);
     424
     425        CTextureProperties texturePropsMask(m_OverlayDescriptor.m_QuadTextureMask.c_str());
     426        texturePropsMask.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE);
     427        texturePropsMask.SetMaxAnisotropy(4.f);
     428
     429        m_UnitOverlay->m_Texture = g_Renderer.GetTextureManager().CreateTexture(texturePropsBase);
     430        m_UnitOverlay->m_TextureMask = g_Renderer.GetTextureManager().CreateTexture(texturePropsMask);
    142431    }
    143432
    144     void ConstructShape(float frameOffset)
     433    m_UnitOverlay->m_Color = m_Color;
     434
     435    // TODO: some code duplication here :< would be nice to factor out getting the corner points of an
     436    // entity based on its footprint sizes (and regardless of whether it's a circle or a square)
     437
     438    float s = sinf(-rotY);
     439    float c = cosf(-rotY);
     440    CVector2D unitX(c, s);
     441    CVector2D unitZ(-s, c);
     442
     443    float halfSizeX = fpSize0_fixed.ToFloat();
     444    float halfSizeZ = fpSize1_fixed.ToFloat();
     445    if (fpShape == ICmpFootprint::SQUARE)
    145446    {
    146         CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId());
    147         if (!cmpPosition)
    148             return;
     447        halfSizeX /= 2.0f;
     448        halfSizeZ /= 2.0f;
     449    }
    149450
    150         if (!cmpPosition->IsInWorld())
    151             return;
     451    std::vector<CVector2D> points;
     452    points.push_back(CVector2D(position + unitX *(-halfSizeX)   + unitZ *  halfSizeZ));  // top left
     453    points.push_back(CVector2D(position + unitX *(-halfSizeX)   + unitZ *(-halfSizeZ))); // bottom left
     454    points.push_back(CVector2D(position + unitX *  halfSizeX    + unitZ *(-halfSizeZ))); // bottom right
     455    points.push_back(CVector2D(position + unitX *  halfSizeX    + unitZ *  halfSizeZ));  // top right
    152456
    153         float x, z, rotY;
    154         cmpPosition->GetInterpolatedPosition2D(frameOffset, x, z, rotY);
     457    for (int i=0; i < 4; i++)
     458    {
     459        float quadY = std::max(
     460            terrain->GetExactGroundLevel(points[i].X, points[i].Y),
     461            cmpWaterManager->GetExactWaterLevel(points[i].X, points[i].Y)
     462        );
    155463
    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
     464        m_UnitOverlay->m_Corners[i] = CVector3D(points[i].X, quadY, points[i].Y);
     465    }
     466}
     467
     468void CCmpSelectable::RenderSubmit(SceneCollector& collector)
     469{
     470    // don't render selection overlay if it's not gonna be visible
     471    if (m_Color.a > 0)
     472    {
     473        switch (m_OverlayDescriptor.m_Type)
    163474        {
    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());
     475            case STATIC_OUTLINE:
     476                {
     477                    UpdateStaticOverlay();
     478                    m_BuildingOverlay->m_Color = m_Color; // done separately so alpha changes don't require a full update call
     479                    collector.Submit(m_BuildingOverlay);
     480                }
     481                break;
     482            case DYNAMIC_QUAD:
     483                {
     484                    if (m_UnitOverlay)
     485                        collector.Submit(m_UnitOverlay);
     486                }
     487                break;
     488            default:
     489                break;
    172490        }
    173491    }
    174492
    175     void RenderSubmit(SceneCollector& collector)
     493    // Render bounding box debug overlays if we have a positive target alpha value. This ensures
     494    // that the debug overlays respond immediately to deselection without delay from fading out.
     495    if (m_FadeBaselineAlpha + m_FadeDeltaAlpha > 0)
    176496    {
    177         // (This is only called if a > 0)
    178         collector.Submit(&m_Overlay);
    179 
    180497        if (ICmpSelectable::ms_EnableDebugOverlays)
    181498        {
    182499            // allocate debug overlays on-demand
    public:  
    205522            if (m_DebugSelectionBoxOverlay) SAFE_DELETE(m_DebugSelectionBoxOverlay);
    206523        }
    207524    }
    208 };
     525}
    209526
    210527REGISTER_COMPONENT_TYPE(Selectable)
  • source/simulation2/components/CCmpTemplateManager.cpp

    diff --git a/source/simulation2/components/CCmpTemplateManager.cpp b/source/simulation2/components/CCmpTemplateManager.cpp
    index 9061c5f..7b24af7 100644
    a b void CCmpTemplateManager::ConstructTemplateActor(const std::string& actorName, C  
    377377
    378378    // Initialise the actor's name and make it an Atlas selectable entity.
    379379    std::string name = utf8_from_wstring(CParamNode::EscapeXMLString(wstring_from_utf8(actorName)));
    380     std::string xml = "<Entity><VisualActor><Actor>" + name + "</Actor></VisualActor><Selectable><EditorOnly/></Selectable></Entity>";
     380    std::string xml = "<Entity>"
     381                          "<VisualActor><Actor>" + name + "</Actor></VisualActor>"
     382                          "<Selectable>"
     383                              "<EditorOnly/>"
     384                              "<Overlay><Texture><MainTexture>actor.png</MainTexture><MainTextureMask>actor_mask.png</MainTextureMask></Texture></Overlay>"
     385                          "</Selectable>"
     386                      "</Entity>";
     387
    381388    CParamNode::LoadXMLString(out, xml.c_str());
    382389}
    383390
  • source/simulation2/components/CCmpTerritoryManager.cpp

    diff --git a/source/simulation2/components/CCmpTerritoryManager.cpp b/source/simulation2/components/CCmpTerritoryManager.cpp
    index 4aa01bf..03c4fff 100644
    a b void CCmpTerritoryManager::UpdateBoundaryLines()  
    620620    texturePropsMask.SetMaxAnisotropy(2.f);
    621621    CTexturePtr textureMask = g_Renderer.GetTextureManager().CreateTexture(texturePropsMask);
    622622
    623     CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY);
    624     if (!cmpTerrain)
    625         return;
    626     CTerrain* terrain = cmpTerrain->GetCTerrain();
    627 
    628623    CmpPtr<ICmpPlayerManager> cmpPlayerManager(GetSimContext(), SYSTEM_ENTITY);
    629624    if (!cmpPlayerManager)
    630625        return;
    void CCmpTerritoryManager::UpdateBoundaryLines()  
    642637        m_BoundaryLines.push_back(SBoundaryLine());
    643638        m_BoundaryLines.back().connected = boundaries[i].connected;
    644639        m_BoundaryLines.back().color = color;
    645         m_BoundaryLines.back().overlay.m_Terrain = terrain;
     640        m_BoundaryLines.back().overlay.m_SimContext = &GetSimContext();
    646641        m_BoundaryLines.back().overlay.m_TextureBase = textureBase;
    647642        m_BoundaryLines.back().overlay.m_TextureMask = textureMask;
    648643        m_BoundaryLines.back().overlay.m_Color = color;
    void CCmpTerritoryManager::UpdateBoundaryLines()  
    650645        m_BoundaryLines.back().overlay.m_Closed = true;
    651646
    652647        SimRender::SmoothPointsAverage(boundaries[i].points, m_BoundaryLines.back().overlay.m_Closed);
    653 
    654648        SimRender::InterpolatePointsRNS(boundaries[i].points, m_BoundaryLines.back().overlay.m_Closed, m_BorderSeparation);
    655649
    656650        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..42f0026 100644
    a b  
    1818#ifndef INCLUDED_ICMPSELECTABLE
    1919#define INCLUDED_ICMPSELECTABLE
    2020
     21#include "ps/CStrIntern.h"
    2122#include "simulation2/system/Interface.h"
    2223
    2324struct CColor;
    struct CColor;  
    2526class ICmpSelectable : public IComponent
    2627{
    2728public:
     29
     30    enum EOverlayType {
     31        /// A single textured quad overlay, intended for entities that move around much, like units (e.g. foot soldiers, etc).
     32        DYNAMIC_QUAD,
     33        /// A more complex textured line overlay, composed of several textured line segments. Intended for entities that do not
     34        /// move often, such as buildings (structures).
     35        STATIC_OUTLINE,
     36    };
     37
     38    struct SOverlayDescriptor
     39    {
     40        EOverlayType m_Type;
     41        CStrIntern m_QuadTexture;
     42        CStrIntern m_QuadTextureMask;
     43        CStrIntern m_LineTexture;
     44        CStrIntern m_LineTextureMask;
     45        float m_LineThickness;
     46       
     47        SOverlayDescriptor() : m_LineThickness(0) { }
     48    };
     49
    2850    /**
    2951     * Returns true if the entity is only selectable in Atlas editor, e.g. a decorative visual actor.
    3052     */
  • source/simulation2/helpers/Geometry.cpp

    diff --git a/source/simulation2/helpers/Geometry.cpp b/source/simulation2/helpers/Geometry.cpp
    index b357561..5b713f0 100644
    a b  
    1 /* Copyright (C) 2010 Wildfire Games.
     1/* Copyright (C) 2012 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    CFixedVector2D Geometry::GetHalfBoundingBox(CFixedVector2D u, CFixedVector2D v,  
    4545    );
    4646}
    4747
     48float Geometry::ChordToCentralAngle(const float chordLength, const float radius)
     49{
     50    return acosf(1.f - SQR(chordLength)/(2.f*SQR(radius))); // cfr. law of cosines
     51}
     52
    4853fixed Geometry::DistanceToSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize)
    4954{
    5055    /*
  • source/simulation2/helpers/Geometry.h

    diff --git a/source/simulation2/helpers/Geometry.h b/source/simulation2/helpers/Geometry.h
    index cb925b7..e8af00e 100644
    a b  
    1 /* Copyright (C) 2011 Wildfire Games.
     1/* Copyright (C) 2012 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    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..6ed8be2 100644
    a b  
    1 /* Copyright (C) 2010 Wildfire Games.
     1/* Copyright (C) 2012 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    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/system/ParamNode.cpp

    diff --git a/source/simulation2/system/ParamNode.cpp b/source/simulation2/system/ParamNode.cpp
    index 4226a61..7cbf1bd 100644
    a b const std::string CParamNode::ToUTF8() const  
    205205    return utf8_from_wstring(m_Value);
    206206}
    207207
     208const CStrIntern CParamNode::ToUTF8Intern() const
     209{
     210    return CStrIntern(utf8_from_wstring(m_Value));
     211}
     212
    208213int CParamNode::ToInt() const
    209214{
    210215    int ret = 0;
    fixed CParamNode::ToFixed() const  
    219224    return fixed::FromString(CStrW(m_Value));
    220225}
    221226
     227float CParamNode::ToFloat() const
     228{
     229    float ret = 0;
     230    std::wstringstream strm;
     231    strm << m_Value;
     232    strm >> ret;
     233    return ret;
     234}
     235
    222236bool CParamNode::ToBool() const
    223237{
    224238    if (m_Value == L"true")
  • source/simulation2/system/ParamNode.h

    diff --git a/source/simulation2/system/ParamNode.h b/source/simulation2/system/ParamNode.h
    index 16e9b40..7caffbd 100644
    a b  
    2020
    2121#include "lib/file/vfs/vfs_path.h"
    2222#include "maths/Fixed.h"
     23#include "ps/CStrIntern.h"
    2324#include "ps/Errors.h"
    2425#include "scriptinterface/ScriptVal.h"
    2526
    public:  
    169170    const std::string ToUTF8() const;
    170171
    171172    /**
     173     * Returns the content of this node as an internalized 8-bit string. Should only be used for
     174     * predictably small and frequently-used strings.
     175     */
     176    const CStrIntern ToUTF8Intern() const;
     177
     178    /**
    172179     * Parses the content of this node as an integer
    173180     */
    174181    int ToInt() const;
    public:  
    179186    fixed ToFixed() const;
    180187
    181188    /**
     189     * Parses the content of this node as a floating-point number
     190     */
     191    float ToFloat() const;
     192
     193    /**
    182194     * Parses the content of this node as a boolean ("true" == true, anything else == false)
    183195     */
    184196    bool ToBool() const;
  • source/simulation2/tests/test_CmpTemplateManager.h

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