This Trac instance is not used for development anymore!

We migrated our development workflow to git and Gitea.
To test the future redirection, replace trac by ariadne in the page URL.

Changeset 9929 for ps


Ignore:
Timestamp:
07/30/11 02:56:45 (13 years ago)
Author:
philip
Message:

# New territory border rendering.
Add textured line overlay rendering.
Change terrain height calculations to be triangulation-dependent for improved accuracy.
Add triangulation-dependent terrain normal function.
Support separate S/T wrap modes for textures.
Rename CVector2D_Maths since it no longer conflicts with simulation CVector2D.
Coalesce freed chunks in vertex buffers, to avoid excessive fragmentation.
Add some things to help debug vertex buffer allocation a little.

Location:
ps/trunk
Files:
9 added
28 edited

Legend:

Unmodified
Added
Removed
  • ps/trunk/binaries/data/mods/public/shaders/terrain_base.xml

    r9889 r9929  
    66        <uniform name="losTransform" loc="1" type="vec2"/>
    77        <uniform name="shadowTransform" loc="2" type="mat4"/>
    8         <uniform name="territoryTransform" loc="6" type="vec2"/>
    98    </vertex>
    109
     
    1312        <uniform name="shadowTex" loc="2" type="sampler2DShadow"/>
    1413        <uniform name="losTex" loc="3" type="sampler2D"/>
    15         <uniform name="territoryTex" loc="4" type="sampler2D"/>
    1614
    1715        <uniform name="ambient" loc="0" type="vec3"/>
  • ps/trunk/binaries/data/mods/public/shaders/terrain_blend.xml

    r9889 r9929  
    88        <uniform name="losTransform" loc="1" type="vec2"/>
    99        <uniform name="shadowTransform" loc="2" type="mat4"/>
    10         <uniform name="territoryTransform" loc="6" type="vec2"/>
    1110    </vertex>
    1211
     
    1615        <uniform name="shadowTex" loc="2" type="sampler2DShadow"/>
    1716        <uniform name="losTex" loc="3" type="sampler2D"/>
    18         <uniform name="territoryTex" loc="4" type="sampler2D"/>
    1917
    2018        <uniform name="ambient" loc="0" type="vec3"/>
  • ps/trunk/binaries/data/mods/public/shaders/terrain_common.fp

    r9889 r9929  
    8282#endif
    8383
    84 // Blend with the territory boundary texture
    85 TEX tex, fragment.texcoord[4], texture[4], 2D;
    86 LRP color.rgb, tex.a, tex, color;
    87 
    8884// Multiply everything by the LOS texture
    8985TEX tex.a, fragment.texcoord[3], texture[3], 2D;
  • ps/trunk/binaries/data/mods/public/shaders/terrain_common.vp

    r9889 r9929  
    33PARAM losTransform = program.local[1];
    44PARAM shadowTransform[4] = { program.local[2..5] };
    5 PARAM territoryTransform = program.local[6];
    65
    76TEMP lighting;
     
    4039
    4140MAD result.texcoord[3], position.xzzz, losTransform.x, losTransform.y;
    42 MAD result.texcoord[4], position.xzzz, territoryTransform.x, territoryTransform.y;
    4341
    4442END
  • ps/trunk/binaries/data/mods/public/shaders/terrain_decal.xml

    r9889 r9929  
    88        <uniform name="losTransform" loc="1" type="vec2"/>
    99        <uniform name="shadowTransform" loc="2" type="mat4"/>
    10         <uniform name="territoryTransform" loc="6" type="vec2"/>
    1110    </vertex>
    1211
     
    1514        <uniform name="shadowTex" loc="2" type="sampler2DShadow"/>
    1615        <uniform name="losTex" loc="3" type="sampler2D"/>
    17         <uniform name="territoryTex" loc="4" type="sampler2D"/>
    1816
    1917        <uniform name="ambient" loc="0" type="vec3"/>
  • ps/trunk/binaries/data/mods/public/simulation/data/pathfinder.xml

    r9666 r9929  
    88
    99    <!-- Unit pathfinding classes: -->
     10    <unrestricted/>
    1011    <default>
    1112      <MaxWaterDepth>2</MaxWaterDepth>
  • ps/trunk/source/graphics/Overlay.h

    r8241 r9929  
    1 /* Copyright (C) 2010 Wildfire Games.
     1/* Copyright (C) 2011 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
     
    1919#define INCLUDED_GRAPHICS_OVERLAY
    2020
     21#include "graphics/RenderableObject.h"
    2122#include "graphics/Texture.h"
    2223#include "maths/Vector3D.h"
    2324#include "ps/Overlay.h" // CColor  (TODO: that file has nothing to do with overlays, it should be renamed)
     25
     26class CTerrain;
    2427
    2528/**
     
    3437    std::vector<float> m_Coords; // (x, y, z) vertex coordinate triples; shape is not automatically closed
    3538    u8 m_Thickness; // pixels
     39};
     40
     41/**
     42 * Textured line overlay, with world-space coordinates, rendered in the world
     43 * onto the terrain. Designed for territory borders.
     44 */
     45struct SOverlayTexturedLine
     46{
     47    SOverlayTexturedLine() : m_Terrain(NULL), m_Thickness(1.0f) { }
     48
     49    CTerrain* m_Terrain;
     50    CTexturePtr m_TextureBase;
     51    CTexturePtr m_TextureMask;
     52    CColor m_Color;
     53    std::vector<float> m_Coords; // (x, z) vertex coordinate pairs; y is computed automatically; shape is automatically closed
     54    float m_Thickness; // world-space units
     55
     56    shared_ptr<CRenderData> m_RenderData; // cached renderer data (shared_ptr so that copies/deletes are automatic)
    3657};
    3758
  • ps/trunk/source/graphics/Terrain.cpp

    r9055 r9929  
    224224}
    225225
     226CVector3D CTerrain::CalcExactNormal(float x, float z) const
     227{
     228    // Clamp to size-2 so we can use the tiles (xi,zi)-(xi+1,zi+1)
     229    const ssize_t xi = clamp((ssize_t)floor(x/CELL_SIZE), (ssize_t)0, m_MapSize-2);
     230    const ssize_t zi = clamp((ssize_t)floor(z/CELL_SIZE), (ssize_t)0, m_MapSize-2);
     231
     232    const float xf = clamp(x/CELL_SIZE-xi, 0.0f, 1.0f);
     233    const float zf = clamp(z/CELL_SIZE-zi, 0.0f, 1.0f);
     234
     235    float h00 = m_Heightmap[zi*m_MapSize + xi];
     236    float h01 = m_Heightmap[(zi+1)*m_MapSize + xi];
     237    float h10 = m_Heightmap[zi*m_MapSize + (xi+1)];
     238    float h11 = m_Heightmap[(zi+1)*m_MapSize + (xi+1)];
     239
     240    // Determine which terrain triangle this point is on,
     241    // then compute the normal of that triangle's plane
     242
     243    if (GetTriangulationDir(xi, zi))
     244    {
     245        if (xf + zf <= 1.f)
     246        {
     247            // Lower-left triangle (don't use h11)
     248            return -CVector3D(CELL_SIZE, (h10-h00)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h01-h00)*HEIGHT_SCALE, CELL_SIZE)).Normalized();
     249        }
     250        else
     251        {
     252            // Upper-right triangle (don't use h00)
     253            return -CVector3D(CELL_SIZE, (h11-h01)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h11-h10)*HEIGHT_SCALE, CELL_SIZE)).Normalized();
     254        }
     255    }
     256    else
     257    {
     258        if (xf <= zf)
     259        {
     260            // Upper-left triangle (don't use h10)
     261            return -CVector3D(CELL_SIZE, (h11-h01)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h01-h00)*HEIGHT_SCALE, CELL_SIZE)).Normalized();
     262        }
     263        else
     264        {
     265            // Lower-right triangle (don't use h01)
     266            return -CVector3D(CELL_SIZE, (h10-h00)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h11-h10)*HEIGHT_SCALE, CELL_SIZE)).Normalized();
     267        }
     268    }
     269}
     270
    226271///////////////////////////////////////////////////////////////////////////////
    227272// GetPatch: return the patch at (i,j) in patch space, or null if the patch is
     
    299344    float h10 = m_Heightmap[zi*m_MapSize + (xi+1)];
    300345    float h11 = m_Heightmap[(zi+1)*m_MapSize + (xi+1)];
    301     // Linearly interpolate
    302     return (HEIGHT_SCALE * (
    303         (1 - zf) * ((1 - xf) * h00 + xf * h10)
    304            + zf  * ((1 - xf) * h01 + xf * h11)));
     346
     347    // Determine which terrain triangle this point is on,
     348    // then compute the linearly-interpolated height on that triangle's plane
     349
     350    if (GetTriangulationDir(xi, zi))
     351    {
     352        if (xf + zf <= 1.f)
     353        {
     354            // Lower-left triangle (don't use h11)
     355            return HEIGHT_SCALE * (h00 + (h10-h00)*xf + (h01-h00)*zf);
     356        }
     357        else
     358        {
     359            // Upper-right triangle (don't use h00)
     360            return HEIGHT_SCALE * (h11 + (h01-h11)*(1-xf) + (h10-h11)*(1-zf));
     361        }
     362    }
     363    else
     364    {
     365        if (xf <= zf)
     366        {
     367            // Upper-left triangle (don't use h10)
     368            return HEIGHT_SCALE * (h00 + (h11-h01)*xf + (h01-h00)*zf);
     369        }
     370        else
     371        {
     372            // Lower-right triangle (don't use h01)
     373            return HEIGHT_SCALE * (h00 + (h10-h00)*xf + (h11-h10)*zf);
     374        }
     375    }
    305376}
    306377
     
    329400    return ((one - zf).Multiply(xf1 * h00 + xf0 * h10)
    330401                  + zf.Multiply(xf1 * h01 + xf0 * h11)) / (int)(HEIGHT_UNITS_PER_METRE / 2);
     402
     403    // TODO: This should probably be more like GetExactGroundLevel()
     404    // in handling triangulation properly
    331405}
    332406
  • ps/trunk/source/graphics/Terrain.h

    r9055 r9929  
    127127    void CalcNormalFixed(ssize_t i, ssize_t j, CFixedVector3D& normal) const;
    128128
     129    CVector3D CalcExactNormal(float x, float z) const;
     130
    129131    // flatten out an area of terrain (specified in world space coords); return
    130132    // the average height of the flattened area
  • ps/trunk/source/graphics/TextureManager.cpp

    r9410 r9929  
    4545        boost::hash_combine(seed, a.m_Path);
    4646        boost::hash_combine(seed, a.m_Filter);
    47         boost::hash_combine(seed, a.m_Wrap);
     47        boost::hash_combine(seed, a.m_WrapS);
     48        boost::hash_combine(seed, a.m_WrapT);
    4849        boost::hash_combine(seed, a.m_Aniso);
    4950        return seed;
     
    6263    {
    6364        return a.m_Path == b.m_Path && a.m_Filter == b.m_Filter
    64             && a.m_Wrap == b.m_Wrap && a.m_Aniso == b.m_Aniso;
     65            && a.m_WrapS == b.m_WrapS && a.m_WrapT == b.m_WrapT
     66            && a.m_Aniso == b.m_Aniso;
    6567    }
    6668    bool operator()(CTexturePtr const& a, CTexturePtr const& b) const
     
    187189
    188190        // Set GL upload properties
    189         (void)ogl_tex_set_wrap(h, texture->m_Properties.m_Wrap);
     191        (void)ogl_tex_set_wrap(h, texture->m_Properties.m_WrapS, texture->m_Properties.m_WrapT);
    190192        (void)ogl_tex_set_anisotropy(h, texture->m_Properties.m_Aniso);
    191193
  • ps/trunk/source/graphics/TextureManager.h

    r9123 r9929  
    130130     */
    131131    explicit CTextureProperties(const VfsPath& path) :
    132         m_Path(path), m_Filter(GL_LINEAR_MIPMAP_LINEAR), m_Wrap(GL_REPEAT), m_Aniso(1.0f)
     132        m_Path(path), m_Filter(GL_LINEAR_MIPMAP_LINEAR),
     133        m_WrapS(GL_REPEAT), m_WrapT(GL_REPEAT), m_Aniso(1.0f)
    133134    {
    134135    }
     
    142143     * Set wrapping mode (typically GL_REPEAT, GL_CLAMP_TO_EDGE, etc).
    143144     */
    144     void SetWrap(GLint wrap) { m_Wrap = wrap; }
     145    void SetWrap(GLint wrap) { m_WrapS = wrap; m_WrapT = wrap; }
     146
     147    /**
     148     * Set wrapping mode (typically GL_REPEAT, GL_CLAMP_TO_EDGE, etc),
     149     * separately for S and T.
     150     */
     151    void SetWrap(GLint wrap_s, GLint wrap_t) { m_WrapS = wrap_s; m_WrapT = wrap_t; }
    145152
    146153    /**
     
    169176    VfsPath m_Path;
    170177    GLint m_Filter;
    171     GLint m_Wrap;
     178    GLint m_WrapS;
     179    GLint m_WrapT;
    172180    float m_Aniso;
    173181};
  • ps/trunk/source/lib/res/graphics/ogl_tex.cpp

    r9546 r9929  
    276276    GLint filter;
    277277    // .. wrap mode
    278     //    note: to simplify things, we assume that apps will never want to
    279     //          set S/T modes independently. it that becomes necessary,
    280     //          it's easy to add.
    281     GLint wrap;
     278    GLint wrap_s;
     279    GLint wrap_t;
    282280    // .. anisotropy
    283281    //    note: ignored unless EXT_texture_filter_anisotropic is supported.
     
    290288{
    291289    ots->filter = default_filter;
    292     ots->wrap = GL_REPEAT;
     290    ots->wrap_s = GL_REPEAT;
     291    ots->wrap_t = GL_REPEAT;
    293292    ots->anisotropy = 1.0f;
    294293}
     
    306305
    307306    // wrap
    308     const GLint wrap = ots->wrap;
    309     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
    310     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
     307    const GLint wrap_s = ots->wrap_s;
     308    const GLint wrap_t = ots->wrap_t;
     309    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s);
     310    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t);
    311311    // .. only CLAMP and REPEAT are guaranteed to be available.
    312312    //    if we're using one of the others, we squelch the error that
    313313    //    may have resulted if this GL implementation is old.
    314     if(wrap != GL_CLAMP && wrap != GL_REPEAT)
     314    if((wrap_s != GL_CLAMP && wrap_s != GL_REPEAT) || (wrap_t != GL_CLAMP && wrap_t != GL_REPEAT))
    315315        ogl_SquelchError(GL_INVALID_ENUM);
    316316
     
    482482    if(!filter_valid(ot->state.filter))
    483483        WARN_RETURN(ERR::_14);
    484     if(!wrap_valid(ot->state.wrap))
     484    if(!wrap_valid(ot->state.wrap_s))
    485485        WARN_RETURN(ERR::_15);
     486    if(!wrap_valid(ot->state.wrap_t))
     487        WARN_RETURN(ERR::_16);
    486488
    487489    // misc
    488490    if(!q_flags_valid(ot->q_flags))
    489         WARN_RETURN(ERR::_16);
     491        WARN_RETURN(ERR::_17);
    490492    if(ot->tmu >= 128)  // unexpected that there will ever be this many
    491         WARN_RETURN(ERR::_17);
     493        WARN_RETURN(ERR::_18);
    492494    if(ot->flags > OT_ALL_FLAGS)
    493         WARN_RETURN(ERR::_18);
     495        WARN_RETURN(ERR::_19);
    494496    // .. note: don't check ot->fmt and ot->int_fmt - they aren't set
    495497    //    until during ogl_tex_upload.
     
    621623// override default wrap mode (GL_REPEAT) for this texture.
    622624// must be called before uploading (raises a warning if called afterwards).
    623 // wrap is as defined by OpenGL and applies to both S and T coordinates
    624 // (rationale: see OglTexState).
    625 Status ogl_tex_set_wrap(Handle ht, GLint wrap)
     625// wrap is as defined by OpenGL.
     626Status ogl_tex_set_wrap(Handle ht, GLint wrap_s, GLint wrap_t)
    626627{
    627628    H_DEREF(ht, OglTex, ot);
    628629
    629     if(!wrap_valid(wrap))
     630    if(!wrap_valid(wrap_s))
    630631        WARN_RETURN(ERR::INVALID_PARAM);
    631632
    632     if(ot->state.wrap != wrap)
     633    if(!wrap_valid(wrap_t))
     634        WARN_RETURN(ERR::INVALID_PARAM);
     635
     636    if(ot->state.wrap_s != wrap_s || ot->state.wrap_t != wrap_t)
    633637    {
    634638        warn_if_uploaded(ht, ot);
    635         ot->state.wrap = wrap;
     639        ot->state.wrap_s = wrap_s;
     640        ot->state.wrap_t = wrap_t;
    636641    }
    637642    return INFO::OK;
  • ps/trunk/source/lib/res/graphics/ogl_tex.h

    r9410 r9929  
    290290*
    291291* @param ht Texture handle
    292 * @param wrap OpenGL wrap mode (for both S and T coordinates)
    293 *        (rationale: see {@link OglTexState})
     292* @param wrap_s OpenGL wrap mode for S coordinates
     293* @param wrap_t OpenGL wrap mode for T coordinates
    294294* @return Status
    295295*
    296296* Must be called before uploading (raises a warning if called afterwards).
    297297*/
    298 extern Status ogl_tex_set_wrap(Handle ht, GLint wrap);
     298extern Status ogl_tex_set_wrap(Handle ht, GLint wrap_s, GLint wrap_t);
    299299
    300300/**
  • ps/trunk/source/maths/Noise.cpp

    r9905 r9929  
    4949{
    5050    freq = f;
    51     grads = new CVector2D_Maths*[freq];
     51    grads = new CVector2D*[freq];
    5252    for(int i=0; i<freq; i++)
    5353    {
    54         grads[i] = new CVector2D_Maths[freq];
     54        grads[i] = new CVector2D[freq];
    5555        for(int j=0; j<freq; j++)
    5656        {
    5757            float a = randFloat() * 2 * (float)M_PI;
    58             grads[i][j] = CVector2D_Maths(cos(a), sin(a));
     58            grads[i][j] = CVector2D(cos(a), sin(a));
    5959        }
    6060    }
     
    8787    int iy1 = (iy+1) % freq;
    8888   
    89     float s = grads[ix][iy].Dot(CVector2D_Maths(fx, fy));
    90     float t = grads[ix1][iy].Dot(CVector2D_Maths(fx-1, fy));
    91     float u = grads[ix][iy1].Dot(CVector2D_Maths(fx, fy-1));
    92     float v = grads[ix1][iy1].Dot(CVector2D_Maths(fx-1, fy-1));
     89    float s = grads[ix][iy].Dot(CVector2D(fx, fy));
     90    float t = grads[ix1][iy].Dot(CVector2D(fx-1, fy));
     91    float u = grads[ix][iy1].Dot(CVector2D(fx, fy-1));
     92    float v = grads[ix1][iy1].Dot(CVector2D(fx-1, fy-1));
    9393   
    9494    float ex = easeCurve(fx);
  • ps/trunk/source/maths/Noise.h

    r6832 r9929  
    3737
    3838    /// freq*freq random gradient vectors in the unit cube
    39     CVector2D_Maths** grads;
     39    CVector2D** grads;
    4040public:
    4141    Noise2D(int freq);
  • ps/trunk/source/maths/Vector2D.h

    r7326 r9929  
    1 /* Copyright (C) 2009 Wildfire Games.
     1/* Copyright (C) 2011 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
     
    2828
    2929///////////////////////////////////////////////////////////////////////////////
    30 // CVector2D_Maths:
    31 class CVector2D_Maths
     30// CVector2D:
     31class CVector2D
    3232{
    3333public:
    34     CVector2D_Maths() {}
    35     CVector2D_Maths(float x,float y) { X=x; Y=y; }
    36     CVector2D_Maths(const CVector2D_Maths& p) { X=p.X; Y=p.Y; }
     34    CVector2D() {}
     35    CVector2D(float x, float y) : X(x), Y(y) {}
    3736
    38     operator float*() {
     37    operator float*()
     38    {
    3939        return &X;
    4040    }
    4141
    42     operator const float*() const {
     42    operator const float*() const
     43    {
    4344        return &X;
    4445    }
    4546
    46     CVector2D_Maths operator-() const {
    47         return CVector2D_Maths(-X, -Y);
     47    CVector2D operator-() const
     48    {
     49        return CVector2D(-X, -Y);
    4850    }
    4951
    50     CVector2D_Maths operator+(const CVector2D_Maths& t) const {
    51         return CVector2D_Maths(X+t.X, Y+t.Y);
     52    CVector2D operator+(const CVector2D& t) const
     53    {
     54        return CVector2D(X + t.X, Y + t.Y);
    5255    }
    5356
    54     CVector2D_Maths operator-(const CVector2D_Maths& t) const {
    55         return CVector2D_Maths(X-t.X, Y-t.Y);
     57    CVector2D operator-(const CVector2D& t) const
     58    {
     59        return CVector2D(X - t.X, Y - t.Y);
    5660    }
    5761
    58     CVector2D_Maths operator*(float f) const {
    59         return CVector2D_Maths(X*f, Y*f);
     62    CVector2D operator*(float f) const
     63    {
     64        return CVector2D(X * f, Y * f);
    6065    }
    6166
    62     CVector2D_Maths operator/(float f) const {
    63         float inv=1.0f/f;
    64         return CVector2D_Maths(X*inv, Y*inv);
     67    CVector2D operator/(float f) const
     68    {
     69        float inv = 1.0f / f;
     70        return CVector2D(X * inv, Y * inv);
    6571    }
    6672
    67     CVector2D_Maths& operator+=(const CVector2D_Maths& t) {
    68         X+=t.X; Y+=t.Y;
    69         return *this;
     73    CVector2D& operator+=(const CVector2D& t)
     74    {
     75        X += t.X;
     76        Y += t.Y;
     77        return *this;
    7078    }
    7179
    72     CVector2D_Maths& operator-=(const CVector2D_Maths& t) {
    73         X-=t.X; Y-=t.Y;
    74         return *this;
     80    CVector2D& operator-=(const CVector2D& t)
     81    {
     82        X -= t.X;
     83        Y -= t.Y;
     84        return *this;
    7585    }
    7686
    77     CVector2D_Maths& operator*=(float f) {
    78         X*=f; Y*=f;
    79         return *this;
     87    CVector2D& operator*=(float f)
     88    {
     89        X *= f;
     90        Y *= f;
     91        return *this;
    8092    }
    8193
    82     CVector2D_Maths& operator/=(float f) {
    83         float invf=1.0f/f;
    84         X*=invf; Y*=invf;
    85         return *this;
     94    CVector2D& operator/=(float f)
     95    {
     96        float invf = 1.0f / f;
     97        X *= invf;
     98        Y *= invf;
     99        return *this;
    86100    }
    87101
    88     float Dot(const CVector2D_Maths& a) const {
    89         return X*a.X + Y*a.Y;
     102    float Dot(const CVector2D& a) const
     103    {
     104        return X * a.X + Y * a.Y;
    90105    }
    91106
    92     float LengthSquared() const {
    93         return Dot(*this);
     107    float LengthSquared() const
     108    {
     109        return Dot(*this);
    94110    }
    95111
    96     float Length() const {
    97         return (float) sqrt(LengthSquared());
     112    float Length() const
     113    {
     114        return (float)sqrt(LengthSquared());
    98115    }
    99116
    100     void Normalize() {
    101         float mag=Length();
    102         X/=mag; Y/=mag;
     117    void Normalize()
     118    {
     119        float mag = Length();
     120        X /= mag;
     121        Y /= mag;
     122    }
     123
     124    CVector2D Normalized()
     125    {
     126        float mag = Length();
     127        return CVector2D(X / mag, Y / mag);
    103128    }
    104129
  • ps/trunk/source/renderer/OverlayRenderer.cpp

    r9362 r9929  
    2020#include "OverlayRenderer.h"
    2121
     22#include "graphics/LOSTexture.h"
    2223#include "graphics/Overlay.h"
     24#include "graphics/ShaderManager.h"
     25#include "graphics/Terrain.h"
    2326#include "graphics/TextureManager.h"
    2427#include "lib/ogl.h"
     28#include "ps/Game.h"
     29#include "ps/Profile.h"
    2530#include "renderer/Renderer.h"
     31#include "renderer/VertexBuffer.h"
     32#include "renderer/VertexBufferManager.h"
     33#include "simulation2/Simulation2.h"
     34#include "simulation2/components/ICmpWaterManager.h"
    2635
    2736struct OverlayRendererInternals
    2837{
    2938    std::vector<SOverlayLine*> lines;
     39    std::vector<SOverlayTexturedLine*> texlines;
    3040    std::vector<SOverlaySprite*> sprites;
    3141};
    3242
     43class CTexturedLineRData : public CRenderData
     44{
     45public:
     46    CTexturedLineRData(SOverlayTexturedLine* line) :
     47        m_Line(line), m_VB(NULL), m_VBIndices(NULL)
     48    {
     49    }
     50
     51    ~CTexturedLineRData()
     52    {
     53        if (m_VB)
     54            g_VBMan.Release(m_VB);
     55        if (m_VBIndices)
     56            g_VBMan.Release(m_VBIndices);
     57    }
     58
     59    struct SVertex
     60    {
     61        SVertex(CVector3D pos, short u, short v) : m_Position(pos) { m_UVs[0] = u; m_UVs[1] = v; }
     62        CVector3D m_Position;
     63        GLshort m_UVs[2];
     64    };
     65    cassert(sizeof(SVertex) == 16);
     66
     67    void Update();
     68
     69    SOverlayTexturedLine* m_Line;
     70
     71    CVertexBuffer::VBChunk* m_VB;
     72    CVertexBuffer::VBChunk* m_VBIndices;
     73};
     74
    3375OverlayRenderer::OverlayRenderer()
    3476{
     
    4183}
    4284
    43 void OverlayRenderer::Submit(SOverlayLine* overlay)
    44 {
    45     m->lines.push_back(overlay);
     85void OverlayRenderer::Submit(SOverlayLine* line)
     86{
     87    ENSURE(line->m_Coords.size() % 3 == 0);
     88
     89    m->lines.push_back(line);
     90}
     91
     92void OverlayRenderer::Submit(SOverlayTexturedLine* line)
     93{
     94    // Simplify the rest of the code by guaranteeing non-empty lines
     95    if (line->m_Coords.empty())
     96        return;
     97
     98    ENSURE(line->m_Coords.size() % 2 == 0);
     99
     100    m->texlines.push_back(line);
    46101}
    47102
     
    54109{
    55110    m->lines.clear();
     111    m->texlines.clear();
    56112    m->sprites.clear();
    57113    // this should leave the capacity unchanged, which is okay since it
     
    61117void OverlayRenderer::PrepareForRendering()
    62118{
     119    PROFILE("prepare overlays");
     120
    63121    // This is where we should do something like sort the overlays by
    64122    // colour/sprite/etc for more efficient rendering
    65 }
    66 
    67 void OverlayRenderer::RenderOverlays()
    68 {
     123
     124    for (size_t i = 0; i < m->texlines.size(); ++i)
     125    {
     126        SOverlayTexturedLine* line = m->texlines[i];
     127        if (!line->m_RenderData)
     128        {
     129            line->m_RenderData = shared_ptr<CRenderData>(new CTexturedLineRData(line));
     130            static_cast<CTexturedLineRData*>(line->m_RenderData.get())->Update();
     131            // We assume the overlay line will get replaced by the caller
     132            // if terrain changes, so we don't need to detect that here and
     133            // call Update again. Also we assume the caller won't change
     134            // any of the parameters after first submitting the line.
     135        }
     136    }
     137}
     138
     139void OverlayRenderer::RenderOverlaysBeforeWater()
     140{
     141    PROFILE("render overlays (before water)");
     142
    69143    glDisable(GL_TEXTURE_2D);
    70144    glEnable(GL_BLEND);
     
    91165}
    92166
     167void OverlayRenderer::RenderOverlaysAfterWater()
     168{
     169    PROFILE("render overlays (after water)");
     170
     171    // Only supported in shader modes
     172    // (TODO: should support in non-shader too)
     173    if (g_Renderer.GetRenderPath() != CRenderer::RP_SHADER)
     174        return;
     175
     176    if (!m->texlines.empty())
     177    {
     178        glEnable(GL_TEXTURE_2D);
     179        glEnable(GL_BLEND);
     180        glDepthMask(0);
     181
     182        glEnableClientState(GL_VERTEX_ARRAY);
     183        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
     184
     185        CShaderManager& shaderManager = g_Renderer.GetShaderManager();
     186        CShaderProgramPtr shaderTexLine(shaderManager.LoadProgram("overlayline", std::map<CStr, CStr>()));
     187
     188        shaderTexLine->Bind();
     189
     190        CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
     191        shaderTexLine->BindTexture("losTex", los.GetTexture());
     192        shaderTexLine->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
     193
     194        for (size_t i = 0; i < m->texlines.size(); ++i)
     195        {
     196            SOverlayTexturedLine* line = m->texlines[i];
     197            if (!line->m_RenderData)
     198                continue;
     199
     200            shaderTexLine->BindTexture("baseTex", line->m_TextureBase->GetHandle());
     201            shaderTexLine->BindTexture("maskTex", line->m_TextureMask->GetHandle());
     202            shaderTexLine->Uniform("objectColor", line->m_Color);
     203
     204            CTexturedLineRData* rdata = static_cast<CTexturedLineRData*>(line->m_RenderData.get());
     205
     206            GLsizei stride = sizeof(CTexturedLineRData::SVertex);
     207            CTexturedLineRData::SVertex* base = reinterpret_cast<CTexturedLineRData::SVertex*>(rdata->m_VB->m_Owner->Bind());
     208
     209            glVertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]);
     210            glTexCoordPointer(2, GL_SHORT, stride, &base->m_UVs[0]);
     211
     212            u8* indexBase = rdata->m_VBIndices->m_Owner->Bind();
     213            glDrawElements(GL_QUAD_STRIP, rdata->m_VBIndices->m_Count, GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*rdata->m_VBIndices->m_Index);
     214
     215            g_Renderer.GetStats().m_OverlayTris += rdata->m_VBIndices->m_Count - 2;
     216        }
     217
     218        shaderTexLine->Unbind();
     219
     220        // TODO: the shader should probably be responsible for unbinding its textures
     221        g_Renderer.BindTexture(1, 0);
     222        g_Renderer.BindTexture(0, 0);
     223
     224        CVertexBuffer::Unbind();
     225        glDisableClientState(GL_VERTEX_ARRAY);
     226        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
     227
     228        glDepthMask(1);
     229        glDisable(GL_BLEND);
     230    }
     231}
     232
    93233void OverlayRenderer::RenderForegroundOverlays(const CCamera& viewCamera)
    94234{
     235    PROFILE("render overlays (fg)");
     236
    95237    glEnable(GL_TEXTURE_2D);
    96238    glEnable(GL_BLEND);
     
    133275    glDisable(GL_TEXTURE_2D);
    134276}
     277
     278void CTexturedLineRData::Update()
     279{
     280    if (m_VB)
     281    {
     282        g_VBMan.Release(m_VB);
     283        m_VB = NULL;
     284    }
     285
     286    if (m_VBIndices)
     287    {
     288        g_VBMan.Release(m_VBIndices);
     289        m_VBIndices = NULL;
     290    }
     291
     292    CmpPtr<ICmpWaterManager> cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
     293
     294    std::vector<SVertex> vertices;
     295    std::vector<u16> indices;
     296
     297    short v = 0;
     298
     299    size_t n = m_Line->m_Coords.size() / 2;
     300    ENSURE(n >= 1);
     301
     302    CTerrain* terrain = m_Line->m_Terrain;
     303
     304    // TODO: this assumes paths are closed loops; probably should extend this to
     305    // handle non-closed paths too
     306
     307    // In each iteration, p1 is the position of vertex i, p0 is i-1, p2 is i+1.
     308    // To avoid slightly expensive terrain computations we cycle these around and
     309    // recompute p2 at the end of each iteration.
     310    CVector3D p0 = CVector3D(m_Line->m_Coords[(n-1)*2], 0, m_Line->m_Coords[(n-1)*2+1]);
     311    CVector3D p1 = CVector3D(m_Line->m_Coords[0], 0, m_Line->m_Coords[1]);
     312    CVector3D p2 = CVector3D(m_Line->m_Coords[(1 % n)*2], 0, m_Line->m_Coords[(1 % n)*2+1]);
     313    bool p1floating = false;
     314    bool p2floating = false;
     315
     316    // Compute terrain heights, clamped to the water height (and remember whether
     317    // each point was floating on water, for normal computation later)
     318
     319    // TODO: if we ever support more than one water level per map, recompute this per point
     320    float w = cmpWaterManager->GetExactWaterLevel(p0.X, p0.Z);
     321
     322    p0.Y = terrain->GetExactGroundLevel(p0.X, p0.Z);
     323    if (p0.Y < w)
     324        p0.Y = w;
     325
     326    p1.Y = terrain->GetExactGroundLevel(p1.X, p1.Z);
     327    if (p1.Y < w)
     328    {
     329        p1.Y = w;
     330        p1floating = true;
     331    }
     332
     333    p2.Y = terrain->GetExactGroundLevel(p2.X, p2.Z);
     334    if (p2.Y < w)
     335    {
     336        p2.Y = w;
     337        p2floating = true;
     338    }
     339
     340    for (size_t i = 0; i < n; ++i)
     341    {
     342        // For vertex i, compute bisector of lines (i-1)..(i) and (i)..(i+1)
     343        // perpendicular to terrain normal
     344
     345        // Normal is vertical if on water, else computed from terrain
     346        CVector3D norm;
     347        if (p1floating)
     348            norm = CVector3D(0, 1, 0);
     349        else
     350            norm = m_Line->m_Terrain->CalcExactNormal(p1.X, p1.Z);
     351
     352        CVector3D b = ((p1 - p0).Normalized() + (p2 - p1).Normalized()).Cross(norm);
     353
     354        // Adjust bisector length to match the line thickness, along the line's width
     355        float l = b.Dot((p2 - p1).Normalized().Cross(norm));
     356        if (fabs(l) > 0.000001f) // avoid unlikely divide-by-zero
     357            b *= m_Line->m_Thickness / l;
     358
     359        // Raise off the terrain a little bit
     360        const float raised = 0.2f;
     361
     362        vertices.push_back(SVertex(p1 + b + norm*raised, 0, v));
     363        indices.push_back(vertices.size() - 1);
     364
     365        vertices.push_back(SVertex(p1 - b + norm*raised, 1, v));
     366        indices.push_back(vertices.size() - 1);
     367
     368        // Alternate V coordinate for debugging
     369        v = 1 - v;
     370
     371        // Cycle the p's and compute the new p2
     372        p0 = p1;
     373        p1 = p2;
     374        p1floating = p2floating;
     375        p2 = CVector3D(m_Line->m_Coords[((i+2) % n)*2], 0, m_Line->m_Coords[((i+2) % n)*2+1]);
     376        p2.Y = terrain->GetExactGroundLevel(p2.X, p2.Z);
     377        if (p2.Y < w)
     378        {
     379            p2.Y = w;
     380            p2floating = true;
     381        }
     382        else
     383            p2floating = false;
     384    }
     385
     386    // Close the path
     387    indices.push_back(0);
     388    indices.push_back(1);
     389
     390    m_VB = g_VBMan.Allocate(sizeof(SVertex), vertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
     391    m_VB->m_Owner->UpdateChunkVertices(m_VB, &vertices[0]);
     392
     393    // Update the indices to include the base offset of the vertex data
     394    for (size_t k = 0; k < indices.size(); ++k)
     395        indices[k] += m_VB->m_Index;
     396
     397    m_VBIndices = g_VBMan.Allocate(sizeof(u16), indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
     398    m_VBIndices->m_Owner->UpdateChunkVertices(m_VBIndices, &indices[0]);
     399}
  • ps/trunk/source/renderer/OverlayRenderer.h

    r8241 r9929  
    2020
    2121struct SOverlayLine;
     22struct SOverlayTexturedLine;
    2223struct SOverlaySprite;
    2324class CCamera;
     
    4142
    4243    /**
     44     * Add a textured line overlay for rendering in this frame.
     45     */
     46    void Submit(SOverlayTexturedLine* overlay);
     47
     48    /**
    4349     * Add a sprite overlay for rendering in this frame.
    4450     */
     
    5965    /**
    6066     * Render all the submitted overlays that are embedded in the world
    61      * (i.e. rendered behind other objects, underwater, etc).
     67     * (i.e. rendered behind other objects in the normal 3D way)
     68     * and should be drawn before water (i.e. may be visible under the water)
    6269     */
    63     void RenderOverlays();
     70    void RenderOverlaysBeforeWater();
     71
     72    /**
     73     * Render all the submitted overlays that are embedded in the world
     74     * (i.e. rendered behind other objects in the normal 3D way)
     75     * and should be drawn after water (i.e. may be visible on top of the water)
     76     */
     77    void RenderOverlaysAfterWater();
    6478
    6579    /**
  • ps/trunk/source/renderer/Renderer.cpp

    r9847 r9929  
    107107        Row_WaterTris,
    108108        Row_ModelTris,
     109        Row_OverlayTris,
    109110        Row_BlendSplats,
    110111        Row_Particles,
     
    174175            return "# model tris";
    175176        sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_ModelTris);
     177        return buf;
     178
     179    case Row_OverlayTris:
     180        if (col == 0)
     181            return "# overlay tris";
     182        sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_OverlayTris);
    176183        return buf;
    177184
     
    15741581    ogl_WarnIfError();
    15751582
    1576     // render other debug-related overlays before water (so they can be displayed when underwater)
    1577     PROFILE_START("render overlays");
    1578     m->overlayRenderer.RenderOverlays();
    1579     PROFILE_END("render overlays");
     1583    // render other debug-related overlays before water (so they can be seen when underwater)
     1584    m->overlayRenderer.RenderOverlaysBeforeWater();
    15801585    ogl_WarnIfError();
    15811586
     
    16041609    }
    16051610
     1611    // render some other overlays after water (so they can be displayed on top of water)
     1612    m->overlayRenderer.RenderOverlaysAfterWater();
     1613    ogl_WarnIfError();
     1614
    16061615    // particles are transparent so render after water
    16071616    RenderParticles();
     
    16231632
    16241633    // render overlays that should appear on top of all other objects
    1625     PROFILE_START("render fg overlays");
    16261634    m->overlayRenderer.RenderForegroundOverlays(m_ViewCamera);
    1627     PROFILE_END("render fg overlays");
    16281635    ogl_WarnIfError();
    16291636}
     
    17191726
    17201727void CRenderer::Submit(SOverlayLine* overlay)
     1728{
     1729    m->overlayRenderer.Submit(overlay);
     1730}
     1731
     1732void CRenderer::Submit(SOverlayTexturedLine* overlay)
    17211733{
    17221734    m->overlayRenderer.Submit(overlay);
     
    19331945    m_hCompositeAlphaMap = ogl_tex_wrap(&t, g_VFS, key);
    19341946    (void)ogl_tex_set_filter(m_hCompositeAlphaMap, GL_LINEAR);
    1935     (void)ogl_tex_set_wrap  (m_hCompositeAlphaMap, GL_CLAMP_TO_EDGE);
     1947    (void)ogl_tex_set_wrap  (m_hCompositeAlphaMap, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
    19361948    int ret = ogl_tex_upload(m_hCompositeAlphaMap, 0, 0, GL_INTENSITY);
    19371949
  • ps/trunk/source/renderer/Renderer.h

    r9827 r9929  
    117117        // number of (non-transparent) model triangles drawn
    118118        size_t m_ModelTris;
     119        // number of overlay triangles drawn
     120        size_t m_OverlayTris;
    119121        // number of splat passes for alphamapping
    120122        size_t m_BlendSplats;
     
    335337    void Submit(CPatch* patch);
    336338    void Submit(SOverlayLine* overlay);
     339    void Submit(SOverlayTexturedLine* overlay);
    337340    void Submit(SOverlaySprite* overlay);
    338341    void Submit(CModelDecal* decal);
  • ps/trunk/source/renderer/Scene.h

    r9889 r9929  
    3838class CTerritoryTexture;
    3939struct SOverlayLine;
     40struct SOverlayTexturedLine;
    4041struct SOverlaySprite;
    4142
     
    9495
    9596    /**
     97     * Submit a textured line overlay.
     98     */
     99    virtual void Submit(SOverlayTexturedLine* overlay) = 0;
     100
     101    /**
    96102     * Submit a sprite overlay.
    97103     */
  • ps/trunk/source/renderer/VertexBuffer.cpp

    r9362 r9929  
    2929#include "ps/CLogger.h"
    3030
    31 ///////////////////////////////////////////////////////////////////////////////
    32 // CVertexBuffer constructor
    3331CVertexBuffer::CVertexBuffer(size_t vertexSize, GLenum usage, GLenum target)
    3432    : m_VertexSize(vertexSize), m_Handle(0), m_SysMem(0), m_Usage(usage), m_Target(target)
     
    6765}
    6866
    69 ///////////////////////////////////////////////////////////////////////////////
    70 // CVertexBuffer destructor
    7167CVertexBuffer::~CVertexBuffer()
    7268{
     
    7470        pglDeleteBuffersARB(1, &m_Handle);
    7571
    76     if (m_SysMem)
    77         delete[] m_SysMem;
    78 
    79     // janwas 2004-06-14: release freelist
     72    delete[] m_SysMem;
     73
    8074    typedef std::list<VBChunk*>::iterator Iter;
    8175    for (Iter iter = m_FreeList.begin(); iter != m_FreeList.end(); ++iter)
    8276        delete *iter;
     77}
     78
     79
     80bool CVertexBuffer::CompatibleVertexType(size_t vertexSize, GLenum usage, GLenum target)
     81{
     82    if (usage != m_Usage || target != m_Target || vertexSize != m_VertexSize)
     83        return false;
     84
     85    return true;
    8386}
    8487
     
    9093{
    9194    // check this is the right kind of buffer
    92     if (usage != m_Usage || target != m_Target || vertexSize != m_VertexSize)
     95    if (!CompatibleVertexType(vertexSize, usage, target))
    9396        return 0;
    9497
     
    139142void CVertexBuffer::Release(VBChunk* chunk)
    140143{
    141     // add to free list
    142     // TODO, RC - need to merge available chunks where possible to avoid
    143     // excessive fragmentation of vertex buffer space
     144    // Update total free count before potentially modifying this chunk's count
     145    m_FreeVertices += chunk->m_Count;
     146
     147    typedef std::list<VBChunk*>::iterator Iter;
     148
     149    // Coalesce with any free-list items that are adjacent to this chunk;
     150    // merge the found chunk with the new one, and remove the old one
     151    // from the list, and repeat until no more are found
     152    bool coalesced;
     153    do
     154    {
     155        coalesced = false;
     156        for (Iter iter = m_FreeList.begin(); iter != m_FreeList.end(); ++iter)
     157        {
     158            if ((*iter)->m_Index == chunk->m_Index + chunk->m_Count
     159             || (*iter)->m_Index + (*iter)->m_Count == chunk->m_Index)
     160            {
     161                chunk->m_Index = std::min(chunk->m_Index, (*iter)->m_Index);
     162                chunk->m_Count += (*iter)->m_Count;
     163                m_FreeList.erase(iter);
     164                coalesced = true;
     165                break;
     166            }
     167        }
     168    }
     169    while (coalesced);
     170
    144171    m_FreeList.push_front(chunk);
    145     m_FreeVertices += chunk->m_Count;
    146172}
    147173
     
    206232    return (m_MaxVertices - m_FreeVertices) * m_VertexSize;
    207233}
     234
     235void CVertexBuffer::DumpStatus()
     236{
     237    debug_printf(L"freeverts = %d\n", m_FreeVertices);
     238
     239    size_t maxSize = 0;
     240    typedef std::list<VBChunk*>::iterator Iter;
     241    for (Iter iter = m_FreeList.begin(); iter != m_FreeList.end(); ++iter)
     242    {
     243        debug_printf(L"free chunk %p: size=%d\n", *iter, (*iter)->m_Count);
     244        maxSize = std::max((*iter)->m_Count, maxSize);
     245    }
     246    debug_printf(L"max size = %d\n", maxSize);
     247}
  • ps/trunk/source/renderer/VertexBuffer.h

    r9053 r9929  
    4646        // number of vertices used by chunk
    4747        size_t m_Count;
     48
     49    private:
     50        // Only CVertexBuffer can construct/delete these
     51        // (Other people should use g_VBMan.Allocate, g_VBMan.Release)
     52        friend class CVertexBuffer;
     53        VBChunk() {}
     54        ~VBChunk() {}
    4855    };
    4956
     
    7077    size_t GetBytesReserved() const;
    7178    size_t GetBytesAllocated() const;
     79
     80    bool CompatibleVertexType(size_t vertexSize, GLenum usage, GLenum target);
     81
     82    void DumpStatus();
    7283
    7384protected:
  • ps/trunk/source/renderer/VertexBufferManager.cpp

    r9362 r9929  
    2727#include "ps/CLogger.h"
    2828
     29#define DUMP_VB_STATS 0 // for debugging
     30
    2931CVertexBufferManager g_VBMan;
    30 
    31 // janwas 2004-06-14: added dtor
    32 
    33 CVertexBufferManager::~CVertexBufferManager()
    34 {
    35 }
    3632
    3733///////////////////////////////////////////////////////////////////////////////
     
    6258    // TODO, RC - run some sanity checks on allocation request
    6359
     60    typedef std::list<CVertexBuffer*>::iterator Iter;
     61
     62#if DUMP_VB_STATS
     63    debug_printf(L"\n============================\n# allocate vsize=%d nverts=%d\n\n", vertexSize, numVertices);
     64    for (Iter iter = m_Buffers.begin(); iter != m_Buffers.end(); ++iter) {
     65        CVertexBuffer* buffer = *iter;
     66        if (buffer->CompatibleVertexType(vertexSize, usage, target))
     67        {
     68            debug_printf(L"%p\n", buffer);
     69            buffer->DumpStatus();
     70        }
     71    }
     72#endif
     73
    6474    // iterate through all existing buffers testing for one that'll
    6575    // satisfy the allocation
    66     typedef std::list<CVertexBuffer*>::iterator Iter;
    6776    for (Iter iter = m_Buffers.begin(); iter != m_Buffers.end(); ++iter) {
    6877        CVertexBuffer* buffer = *iter;
     
    9099{
    91100    ENSURE(chunk);
     101#if DUMP_VB_STATS
     102    debug_printf(L"\n============================\n# release %p nverts=%d\n\n", chunk, chunk->m_Count);
     103#endif
    92104    chunk->m_Owner->Release(chunk);
    93105}
  • ps/trunk/source/renderer/VertexBufferManager.h

    r9052 r9929  
    3131{
    3232public:
    33     CVertexBufferManager() {}
    34     ~CVertexBufferManager();
    35 
    3633    // Explicit shutdown of the vertex buffer subsystem
    3734    void Shutdown();
  • ps/trunk/source/simulation2/components/CCmpTerritoryManager.cpp

    r9906 r9929  
    2121#include "ICmpTerritoryManager.h"
    2222
     23#include "graphics/Overlay.h"
    2324#include "graphics/Terrain.h"
     25#include "graphics/TextureManager.h"
    2426#include "maths/MathUtil.h"
     27#include "maths/Vector2D.h"
    2528#include "ps/Overlay.h"
     29#include "renderer/Renderer.h"
     30#include "renderer/Scene.h"
    2631#include "renderer/TerrainOverlay.h"
    2732#include "simulation2/MessageTypes.h"
     
    3035#include "simulation2/components/ICmpOwnership.h"
    3136#include "simulation2/components/ICmpPathfinder.h"
     37#include "simulation2/components/ICmpPlayer.h"
     38#include "simulation2/components/ICmpPlayerManager.h"
    3239#include "simulation2/components/ICmpPosition.h"
    3340#include "simulation2/components/ICmpSettlement.h"
     
    3744#include "simulation2/helpers/Grid.h"
    3845#include "simulation2/helpers/PriorityQueue.h"
     46#include "simulation2/helpers/Render.h"
    3947
    4048class CCmpTerritoryManager;
     
    5967        componentManager.SubscribeGloballyToMessageType(MT_PositionChanged);
    6068        componentManager.SubscribeToMessageType(MT_TerrainChanged);
     69        componentManager.SubscribeToMessageType(MT_RenderSubmit);
    6170    }
    6271
     
    6776        return "<a:component type='system'/><empty/>";
    6877    }
     78
     79    u8 m_ImpassableCost;
     80    float m_BorderThickness;
     81    float m_BorderSeparation;
    6982
    7083    Grid<u8>* m_Territories;
    7184    TerritoryOverlay* m_DebugOverlay;
     85    std::vector<SOverlayTexturedLine> m_BoundaryLines;
     86    bool m_BoundaryLinesDirty;
    7287
    7388    virtual void Init(const CParamNode& UNUSED(paramNode))
     
    7691        m_DebugOverlay = NULL;
    7792//      m_DebugOverlay = new TerritoryOverlay(*this);
     93        m_BoundaryLinesDirty = true;
    7894
    7995        m_DirtyID = 1;
     96
     97        CParamNode externalParamNode;
     98        CParamNode::LoadXML(externalParamNode, L"simulation/data/territorymanager.xml");
     99
     100        m_ImpassableCost = externalParamNode.GetChild("TerritoryManager").GetChild("ImpassableCost").ToInt();
     101        m_BorderThickness = externalParamNode.GetChild("TerritoryManager").GetChild("BorderThickness").ToFixed().ToFloat();
     102        m_BorderSeparation = externalParamNode.GetChild("TerritoryManager").GetChild("BorderSeparation").ToFixed().ToFloat();
    80103    }
    81104
     
    117140            break;
    118141        }
     142        case MT_RenderSubmit:
     143        {
     144            const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg);
     145            RenderSubmit(msgData.collector);
     146            break;
     147        }
    119148        }
    120149    }
     
    149178        SAFE_DELETE(m_Territories);
    150179        ++m_DirtyID;
     180        m_BoundaryLinesDirty = true;
    151181    }
    152182
     
    170200     */
    171201    void RasteriseInfluences(CComponentManager::InterfaceList& infls, Grid<u8>& grid);
     202
     203    struct TerritoryBoundary
     204    {
     205        player_id_t owner;
     206        std::vector<CVector2D> points;
     207    };
     208
     209    std::vector<TerritoryBoundary> ComputeBoundaries();
     210
     211    void UpdateBoundaryLines();
     212
     213    void RenderSubmit(SceneCollector& collector);
    172214};
    173215
     
    253295
    254296    CmpPtr<ICmpPathfinder> cmpPathfinder(GetSimContext(), SYSTEM_ENTITY);
    255     ICmpPathfinder::pass_class_t passClass = cmpPathfinder->GetPassabilityClass("default");
     297    ICmpPathfinder::pass_class_t passClassUnrestricted = cmpPathfinder->GetPassabilityClass("unrestricted");
     298    ICmpPathfinder::pass_class_t passClassDefault = cmpPathfinder->GetPassabilityClass("default");
    256299    const Grid<u16>& passGrid = cmpPathfinder->GetPassabilityGrid();
    257300    for (u32 j = 0; j < tilesH; ++j)
     
    259302        for (u32 i = 0; i < tilesW; ++i)
    260303        {
     304            u8 g = passGrid.get(i, j);
    261305            u8 cost;
    262             if (passGrid.get(i, j) & passClass)
    263                 cost = 4; // TODO: should come from some XML file
     306            if (g & passClassUnrestricted)
     307                cost = 255; // off the world; use maximum cost
     308            else if (g & passClassDefault)
     309                cost = m_ImpassableCost;
    264310            else
    265311                cost = 1;
     
    426472}
    427473
     474std::vector<CCmpTerritoryManager::TerritoryBoundary> CCmpTerritoryManager::ComputeBoundaries()
     475{
     476    PROFILE("ComputeBoundaries");
     477
     478    std::vector<CCmpTerritoryManager::TerritoryBoundary> boundaries;
     479
     480    CalculateTerritories();
     481
     482    // Copy the territories grid so we can mess with it
     483    Grid<u8> grid (*m_Territories);
     484
     485    // Some constants for the border walk
     486    CVector2D edgeOffsets[] = {
     487        CVector2D(0.5f, 0.0f),
     488        CVector2D(1.0f, 0.5f),
     489        CVector2D(0.5f, 1.0f),
     490        CVector2D(0.0f, 0.5f)
     491    };
     492
     493    // Try to find an assigned tile
     494    for (int j = 0; j < grid.m_H; ++j)
     495    {
     496        for (int i = 0; i < grid.m_W; ++i)
     497        {
     498            u8 owner = grid.get(i, j);
     499            if (owner)
     500            {
     501                // Found the first tile (which must be the lowest j value of any non-zero tile);
     502                // start at the bottom edge of it and chase anticlockwise around the border until
     503                // we reach the starting point again
     504
     505                boundaries.push_back(TerritoryBoundary());
     506                boundaries.back().owner = owner;
     507                std::vector<CVector2D>& points = boundaries.back().points;
     508
     509                int dir = 0; // 0 == bottom edge of tile, 1 == right, 2 == top, 3 == left
     510
     511                int cdir = dir;
     512                int ci = i, cj = j;
     513
     514                while (true)
     515                {
     516                    points.push_back((CVector2D(ci, cj) + edgeOffsets[cdir]) * CELL_SIZE);
     517
     518                    // Given that we're on an edge on a continuous boundary and aiming anticlockwise,
     519                    // we can either carry on straight or turn left or turn right, so examine each
     520                    // of the three possible cases (depending on initial direction):
     521                    switch (cdir)
     522                    {
     523                    case 0:
     524                        if (ci < grid.m_W-1 && cj > 0 && grid.get(ci+1, cj-1) == owner)
     525                        {
     526                            ++ci;
     527                            --cj;
     528                            cdir = 3;
     529                        }
     530                        else if (ci < grid.m_W-1 && grid.get(ci+1, cj) == owner)
     531                            ++ci;
     532                        else
     533                            cdir = 1;
     534                        break;
     535                    case 1:
     536                        if (ci < grid.m_W-1 && cj < grid.m_H-1 && grid.get(ci+1, cj+1) == owner)
     537                        {
     538                            ++ci;
     539                            ++cj;
     540                            cdir = 0;
     541                        }
     542                        else if (cj < grid.m_H-1 && grid.get(ci, cj+1) == owner)
     543                            ++cj;
     544                        else
     545                            cdir = 2;
     546                        break;
     547                    case 2:
     548                        if (ci > 0 && cj < grid.m_H-1 && grid.get(ci-1, cj+1) == owner)
     549                        {
     550                            --ci;
     551                            ++cj;
     552                            cdir = 1;
     553                        }
     554                        else if (ci > 0 && grid.get(ci-1, cj) == owner)
     555                            --ci;
     556                        else
     557                            cdir = 3;
     558                        break;
     559                    case 3:
     560                        if (ci > 0 && cj > 0 && grid.get(ci-1, cj-1) == owner)
     561                        {
     562                            --ci;
     563                            --cj;
     564                            cdir = 2;
     565                        }
     566                        else if (cj > 0 && grid.get(ci, cj-1) == owner)
     567                            --cj;
     568                        else
     569                            cdir = 0;
     570                        break;
     571                    }
     572
     573                    // Stop when we've reached the starting point again
     574                    if (ci == i && cj == j && cdir == dir)
     575                        break;
     576                }
     577
     578                // Zero out this whole territory with a simple flood fill, so we don't
     579                // process it a second time
     580                std::vector<std::pair<int, int> > tileStack;
     581
     582#define ZERO_AND_PUSH(i, j) STMT(grid.set(i, j, 0); tileStack.push_back(std::make_pair(i, j)); )
     583
     584                ZERO_AND_PUSH(i, j);
     585                while (!tileStack.empty())
     586                {
     587                    int ti = tileStack.back().first;
     588                    int tj = tileStack.back().second;
     589                    tileStack.pop_back();
     590
     591                    if (ti > 0 && grid.get(ti-1, tj) == owner)
     592                        ZERO_AND_PUSH(ti-1, tj);
     593                    if (ti < grid.m_W-1 && grid.get(ti+1, tj) == owner)
     594                        ZERO_AND_PUSH(ti+1, tj);
     595                    if (tj > 0 && grid.get(ti, tj-1) == owner)
     596                        ZERO_AND_PUSH(ti, tj-1);
     597                    if (tj < grid.m_H-1 && grid.get(ti, tj+1) == owner)
     598                        ZERO_AND_PUSH(ti, tj+1);
     599
     600                    if (ti > 0 && tj > 0 && grid.get(ti-1, tj-1) == owner)
     601                        ZERO_AND_PUSH(ti-1, tj-1);
     602                    if (ti > 0 && tj < grid.m_H-1 && grid.get(ti-1, tj+1) == owner)
     603                        ZERO_AND_PUSH(ti-1, tj+1);
     604                    if (ti < grid.m_W-1 && tj > 0 && grid.get(ti+1, tj-1) == owner)
     605                        ZERO_AND_PUSH(ti+1, tj-1);
     606                    if (ti < grid.m_W-1 && tj < grid.m_H-1 && grid.get(ti+1, tj+1) == owner)
     607                        ZERO_AND_PUSH(ti+1, tj+1);
     608                }
     609
     610#undef ZERO_AND_PUSH
     611            }
     612        }
     613    }
     614
     615    return boundaries;
     616}
     617
     618void CCmpTerritoryManager::UpdateBoundaryLines()
     619{
     620    PROFILE("update boundary lines");
     621
     622    m_BoundaryLines.clear();
     623
     624    std::vector<CCmpTerritoryManager::TerritoryBoundary> boundaries = ComputeBoundaries();
     625
     626    CTextureProperties texturePropsBase("art/textures/misc/territory_border.png");
     627    texturePropsBase.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE);
     628    texturePropsBase.SetMaxAnisotropy(2.f);
     629    CTexturePtr textureBase = g_Renderer.GetTextureManager().CreateTexture(texturePropsBase);
     630
     631    CTextureProperties texturePropsMask("art/textures/misc/territory_border_mask.png");
     632    texturePropsMask.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE);
     633    texturePropsMask.SetMaxAnisotropy(2.f);
     634    CTexturePtr textureMask = g_Renderer.GetTextureManager().CreateTexture(texturePropsMask);
     635
     636    CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY);
     637    if (cmpTerrain.null())
     638        return;
     639    CTerrain* terrain = cmpTerrain->GetCTerrain();
     640
     641    CmpPtr<ICmpPlayerManager> cmpPlayerManager(GetSimContext(), SYSTEM_ENTITY);
     642    if (cmpPlayerManager.null())
     643        return;
     644
     645    for (size_t i = 0; i < boundaries.size(); ++i)
     646    {
     647        if (boundaries[i].points.empty())
     648            continue;
     649
     650        CColor color(1, 0, 1, 1);
     651        CmpPtr<ICmpPlayer> cmpPlayer(GetSimContext(), cmpPlayerManager->GetPlayerByID(boundaries[i].owner));
     652        if (!cmpPlayer.null())
     653            color = cmpPlayer->GetColour();
     654
     655        m_BoundaryLines.push_back(SOverlayTexturedLine());
     656        m_BoundaryLines.back().m_Terrain = terrain;
     657        m_BoundaryLines.back().m_TextureBase = textureBase;
     658        m_BoundaryLines.back().m_TextureMask = textureMask;
     659        m_BoundaryLines.back().m_Color = color;
     660        m_BoundaryLines.back().m_Thickness = m_BorderThickness;
     661
     662        SimRender::SmoothPointsAverage(boundaries[i].points, true);
     663
     664        SimRender::InterpolatePointsRNS(boundaries[i].points, true, m_BorderSeparation);
     665
     666        std::vector<float>& points = m_BoundaryLines.back().m_Coords;
     667        for (size_t j = 0; j < boundaries[i].points.size(); ++j)
     668        {
     669            points.push_back(boundaries[i].points[j].X);
     670            points.push_back(boundaries[i].points[j].Y);
     671        }
     672    }
     673}
     674
     675void CCmpTerritoryManager::RenderSubmit(SceneCollector& collector)
     676{
     677    if (m_BoundaryLinesDirty)
     678    {
     679        UpdateBoundaryLines();
     680        m_BoundaryLinesDirty = false;
     681    }
     682
     683    for (size_t i = 0; i < m_BoundaryLines.size(); ++i)
     684        collector.Submit(&m_BoundaryLines[i]);
     685}
     686
    428687
    429688void TerritoryOverlay::StartRender()
  • ps/trunk/source/simulation2/helpers/Render.cpp

    r7817 r9929  
    2626#include "graphics/Terrain.h"
    2727#include "maths/MathUtil.h"
    28 
    29 static const float RENDER_HEIGHT_DELTA = 0.25f; // distance above terrain
    30 
    31 void SimRender::ConstructLineOnGround(const CSimContext& context, std::vector<float> xz,
    32         SOverlayLine& overlay, bool floating)
    33 {
     28#include "maths/Vector2D.h"
     29#include "ps/Profile.h"
     30
     31void SimRender::ConstructLineOnGround(const CSimContext& context, const std::vector<float>& xz,
     32        SOverlayLine& overlay, bool floating, float heightOffset)
     33{
     34    PROFILE("ConstructLineOnGround");
     35
    3436    overlay.m_Coords.clear();
    3537
     
    5557        float px = xz[i];
    5658        float pz = xz[i+1];
    57         float py = std::max(water, cmpTerrain->GetExactGroundLevel(px, pz)) + RENDER_HEIGHT_DELTA;
     59        float py = std::max(water, cmpTerrain->GetExactGroundLevel(px, pz)) + heightOffset;
    5860        overlay.m_Coords.push_back(px);
    5961        overlay.m_Coords.push_back(py);
     
    6365
    6466void SimRender::ConstructCircleOnGround(const CSimContext& context, float x, float z, float radius,
    65         SOverlayLine& overlay, bool floating)
     67        SOverlayLine& overlay, bool floating, float heightOffset)
    6668{
    6769    overlay.m_Coords.clear();
     
    8991        float px = x + radius * sin(a);
    9092        float pz = z + radius * cos(a);
    91         float py = std::max(water, cmpTerrain->GetExactGroundLevel(px, pz)) + RENDER_HEIGHT_DELTA;
     93        float py = std::max(water, cmpTerrain->GetExactGroundLevel(px, pz)) + heightOffset;
    9294        overlay.m_Coords.push_back(px);
    9395        overlay.m_Coords.push_back(py);
     
    114116
    115117void SimRender::ConstructSquareOnGround(const CSimContext& context, float x, float z, float w, float h, float a,
    116         SOverlayLine& overlay, bool floating)
     118        SOverlayLine& overlay, bool floating, float heightOffset)
    117119{
    118120    overlay.m_Coords.clear();
     
    151153        float px = coords[i].first;
    152154        float pz = coords[i].second;
    153         float py = std::max(water, cmpTerrain->GetExactGroundLevel(px, pz)) + RENDER_HEIGHT_DELTA;
     155        float py = std::max(water, cmpTerrain->GetExactGroundLevel(px, pz)) + heightOffset;
    154156        overlay.m_Coords.push_back(px);
    155157        overlay.m_Coords.push_back(py);
     
    157159    }
    158160}
     161
     162void SimRender::SmoothPointsAverage(std::vector<CVector2D>& points, bool closed)
     163{
     164    PROFILE("SmoothPointsAverage");
     165
     166    size_t n = points.size();
     167    if (n < 2)
     168        return; // avoid out-of-bounds array accesses, and leave the points unchanged
     169
     170    std::vector<CVector2D> newPoints;
     171    newPoints.resize(points.size());
     172
     173    // Handle the end points appropriately
     174    if (closed)
     175    {
     176        newPoints[0] = (points[n-1] + points[0] + points[1]) / 3.f;
     177        newPoints[n-1] = (points[n-2] + points[n-1] + points[0]) / 3.f;
     178    }
     179    else
     180    {
     181        newPoints[0] = points[0];
     182        newPoints[n-1] = points[n-1];
     183    }
     184
     185    // Average all the intermediate points
     186    for (size_t i = 1; i < n-1; ++i)
     187        newPoints[i] = (points[i-1] + points[i] + points[i+1]) / 3.f;
     188
     189    points.swap(newPoints);
     190}
     191
     192static CVector2D EvaluateSpline(float t, CVector2D a0, CVector2D a1, CVector2D a2, CVector2D a3, float offset)
     193{
     194    // Compute position on spline
     195    CVector2D p = a0*(t*t*t) + a1*(t*t) + a2*t + a3;
     196
     197    // Compute unit-vector direction of spline
     198    CVector2D dp = (a0*(3*t*t) + a1*(2*t) + a2).Normalized();
     199
     200    // Offset position perpendicularly
     201    return p + CVector2D(dp.Y*-offset, dp.X*offset);
     202}
     203
     204void SimRender::InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed, float offset)
     205{
     206    PROFILE("InterpolatePointsRNS");
     207
     208    std::vector<CVector2D> newPoints;
     209
     210    // (This does some redundant computations for adjacent vertices,
     211    // but it's fairly fast (<1ms typically) so we don't worry about it yet)
     212
     213    // TODO: Instead of doing a fixed number of line segments between each
     214    // control point, it should probably be somewhat adaptive to get a nicer
     215    // curve with fewer points
     216
     217    size_t n = points.size();
     218    if (n < 1)
     219        return; // can't do anything unless we have two points
     220
     221    size_t imax = closed ? n : n-1; // TODO: we probably need to do a bit more to handle non-closed paths
     222
     223    newPoints.reserve(imax*4);
     224
     225    for (size_t i = 0; i < imax; ++i)
     226    {
     227        // Get the relevant points for this spline segment
     228        CVector2D p0 = points[(i-1+n)%n];
     229        CVector2D p1 = points[i];
     230        CVector2D p2 = points[(i+1)%n];
     231        CVector2D p3 = points[(i+2)%n];
     232
     233        // Do the RNS computation (based on GPG4 "Nonuniform Splines")
     234        float l1 = (p2 - p1).Length(); // length of spline segment (i)..(i+1)
     235        CVector2D s0 = (p1 - p0).Normalized(); // unit vector of spline segment (i-1)..(i)
     236        CVector2D s1 = (p2 - p1).Normalized(); // unit vector of spline segment (i)..(i+1)
     237        CVector2D s2 = (p3 - p2).Normalized(); // unit vector of spline segment (i+1)..(i+2)
     238        CVector2D v1 = (s0 + s1).Normalized() * l1; // spline velocity at i
     239        CVector2D v2 = (s1 + s2).Normalized() * l1; // spline velocity at i+1
     240
     241        // Compute standard cubic spline parameters
     242        CVector2D a0 = p1*2 + p2*-2 + v1 + v2;
     243        CVector2D a1 = p1*-3 + p2*3 + v1*-2 + v2*-1;
     244        CVector2D a2 = v1;
     245        CVector2D a3 = p1;
     246
     247        // Interpolate at various points
     248        newPoints.push_back(EvaluateSpline(0.f, a0, a1, a2, a3, offset));
     249        newPoints.push_back(EvaluateSpline(1.f/4.f, a0, a1, a2, a3, offset));
     250        newPoints.push_back(EvaluateSpline(2.f/4.f, a0, a1, a2, a3, offset));
     251        newPoints.push_back(EvaluateSpline(3.f/4.f, a0, a1, a2, a3, offset));
     252    }
     253
     254    points.swap(newPoints);
     255}
  • ps/trunk/source/simulation2/helpers/Render.h

    r7655 r9929  
    2525
    2626class CSimContext;
     27class CVector2D;
    2728struct SOverlayLine;
    2829
     
    3435 * flattened on the terrain (or on the water if @p floating).
    3536 */
    36 void ConstructLineOnGround(const CSimContext& context, std::vector<float> xz, SOverlayLine& overlay, bool floating);
     37void ConstructLineOnGround(const CSimContext& context, const std::vector<float>& xz,
     38        SOverlayLine& overlay,
     39        bool floating, float heightOffset = 0.25f);
    3740
    3841/**
    3942 * Updates @p overlay so that it represents the given circle, flattened on the terrain.
    4043 */
    41 void ConstructCircleOnGround(const CSimContext& context, float x, float z, float radius, SOverlayLine& overlay, bool floating);
     44void ConstructCircleOnGround(const CSimContext& context, float x, float z, float radius,
     45        SOverlayLine& overlay,
     46        bool floating, float heightOffset = 0.25f);
    4247
    4348/**
     
    4550 * @p x and @p z are position of center, @p w and @p h are size of rectangle, @p a is clockwise angle.
    4651 */
    47 void ConstructSquareOnGround(const CSimContext& context, float x, float z, float w, float h, float a, SOverlayLine& overlay, bool floating);
     52void ConstructSquareOnGround(const CSimContext& context, float x, float z, float w, float h, float a,
     53        SOverlayLine& overlay,
     54        bool floating, float heightOffset = 0.25f);
     55
     56/**
     57 * Updates @p points so each point is averaged with its neighbours, resulting in
     58 * a somewhat smoother curve, assuming the points are roughly equally spaced.
     59 * If @p closed then the points are treated as a closed path (the last is connected
     60 * to the first).
     61 */
     62void SmoothPointsAverage(std::vector<CVector2D>& points, bool closed);
     63
     64/**
     65 * Updates @p points to include intermediate points interpolating between the original
     66 * control points, using a rounded nonuniform spline.
     67 * The points are also shifted by @p offset in a direction 90 degrees clockwise from
     68 * the direction of the curve.
     69 * If @p closed then the points are treated as a closed path (the last is connected
     70 * to the first).
     71 */
     72void InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed, float offset);
    4873
    4974} // namespace
Note: See TracChangeset for help on using the changeset viewer.