mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 05:13:58 -07:00
# 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. This was SVN commit r9929.
This commit is contained in:
parent
239685d754
commit
8fee3d8ef8
36 changed files with 1084 additions and 143 deletions
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TerritoryManager>
|
||||
<ImpassableCost>4</ImpassableCost>
|
||||
<BorderThickness>0.5</BorderThickness>
|
||||
<BorderSeparation>0.55</BorderSeparation>
|
||||
</TerritoryManager>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:33520d117aae35cc5dac67fa53a1791970e6764976b7b1b0cc940cb75c29e051
|
||||
size 407
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:472bb478e53b988c5c68b87c2bae50adb791de2fca371a50c4a1dd1237f3b405
|
||||
size 178
|
||||
4
binaries/data/mods/public/art/textures/misc/textures.xml
Normal file
4
binaries/data/mods/public/art/textures/misc/textures.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Textures>
|
||||
<File pattern="territory_border.png" format="rgba"/>
|
||||
</Textures>
|
||||
20
binaries/data/mods/public/shaders/overlayline.fp
Normal file
20
binaries/data/mods/public/shaders/overlayline.fp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
!!ARBfp1.0
|
||||
PARAM objectColor = program.local[0];
|
||||
TEMP base;
|
||||
TEMP mask;
|
||||
TEMP color;
|
||||
TEMP los;
|
||||
|
||||
// Combine base texture and color, using mask texture
|
||||
TEX base, fragment.texcoord[0], texture[0], 2D;
|
||||
TEX mask, fragment.texcoord[0], texture[1], 2D;
|
||||
LRP color.rgb, mask, objectColor, base;
|
||||
|
||||
// Multiply by LOS texture
|
||||
TEX los, fragment.texcoord[1], texture[2], 2D;
|
||||
MUL result.color.rgb, color, los.a;
|
||||
|
||||
// Use alpha from base texture
|
||||
MOV result.color.a, base.a;
|
||||
|
||||
END
|
||||
15
binaries/data/mods/public/shaders/overlayline.vp
Normal file
15
binaries/data/mods/public/shaders/overlayline.vp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
!!ARBvp1.0
|
||||
PARAM losTransform = program.local[0];
|
||||
ATTRIB position = vertex.position;
|
||||
|
||||
DP4 result.position.x, state.matrix.mvp.row[0], position;
|
||||
DP4 result.position.y, state.matrix.mvp.row[1], position;
|
||||
DP4 result.position.z, state.matrix.mvp.row[2], position;
|
||||
DP4 result.position.w, state.matrix.mvp.row[3], position;
|
||||
|
||||
MOV result.texcoord[0], vertex.texcoord[0];
|
||||
MAD result.texcoord[1], position.xzzz, losTransform.x, losTransform.y;
|
||||
|
||||
MOV result.color, vertex.color;
|
||||
|
||||
END
|
||||
17
binaries/data/mods/public/shaders/overlayline.xml
Normal file
17
binaries/data/mods/public/shaders/overlayline.xml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<program type="arb">
|
||||
|
||||
<vertex file="overlayline.vp">
|
||||
<stream name="pos"/>
|
||||
<stream name="uv0"/>
|
||||
<uniform name="losTransform" loc="0" type="vec2"/>
|
||||
</vertex>
|
||||
|
||||
<fragment file="overlayline.fp">
|
||||
<uniform name="baseTex" loc="0" type="sampler2D"/>
|
||||
<uniform name="maskTex" loc="1" type="sampler2D"/>
|
||||
<uniform name="losTex" loc="2" type="sampler2D"/>
|
||||
<uniform name="objectColor" loc="0" type="vec3"/>
|
||||
</fragment>
|
||||
|
||||
</program>
|
||||
|
|
@ -5,14 +5,12 @@
|
|||
<uniform name="sunColor" loc="0" type="vec3"/>
|
||||
<uniform name="losTransform" loc="1" type="vec2"/>
|
||||
<uniform name="shadowTransform" loc="2" type="mat4"/>
|
||||
<uniform name="territoryTransform" loc="6" type="vec2"/>
|
||||
</vertex>
|
||||
|
||||
<fragment file="terrain_common.fp">
|
||||
<uniform name="baseTex" loc="0" type="sampler2D"/>
|
||||
<uniform name="shadowTex" loc="2" type="sampler2DShadow"/>
|
||||
<uniform name="losTex" loc="3" type="sampler2D"/>
|
||||
<uniform name="territoryTex" loc="4" type="sampler2D"/>
|
||||
|
||||
<uniform name="ambient" loc="0" type="vec3"/>
|
||||
<uniform name="shadowOffsets1" loc="2" type="vec4"/>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
<uniform name="sunColor" loc="0" type="vec3"/>
|
||||
<uniform name="losTransform" loc="1" type="vec2"/>
|
||||
<uniform name="shadowTransform" loc="2" type="mat4"/>
|
||||
<uniform name="territoryTransform" loc="6" type="vec2"/>
|
||||
</vertex>
|
||||
|
||||
<fragment file="terrain_common.fp">
|
||||
|
|
@ -15,7 +14,6 @@
|
|||
<uniform name="blendTex" loc="1" type="sampler2D"/>
|
||||
<uniform name="shadowTex" loc="2" type="sampler2DShadow"/>
|
||||
<uniform name="losTex" loc="3" type="sampler2D"/>
|
||||
<uniform name="territoryTex" loc="4" type="sampler2D"/>
|
||||
|
||||
<uniform name="ambient" loc="0" type="vec3"/>
|
||||
<uniform name="shadowOffsets1" loc="2" type="vec4"/>
|
||||
|
|
|
|||
|
|
@ -81,10 +81,6 @@ TEX color, fragment.texcoord[0], texture[0], 2D;
|
|||
MUL color.rgb, color, temp;
|
||||
#endif
|
||||
|
||||
// Blend with the territory boundary texture
|
||||
TEX tex, fragment.texcoord[4], texture[4], 2D;
|
||||
LRP color.rgb, tex.a, tex, color;
|
||||
|
||||
// Multiply everything by the LOS texture
|
||||
TEX tex.a, fragment.texcoord[3], texture[3], 2D;
|
||||
MUL color.rgb, color, tex.a;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
PARAM sunColor = program.local[0];
|
||||
PARAM losTransform = program.local[1];
|
||||
PARAM shadowTransform[4] = { program.local[2..5] };
|
||||
PARAM territoryTransform = program.local[6];
|
||||
|
||||
TEMP lighting;
|
||||
|
||||
|
|
@ -39,6 +38,5 @@ MOV result.texcoord[0], vertex.texcoord[0];
|
|||
#endif
|
||||
|
||||
MAD result.texcoord[3], position.xzzz, losTransform.x, losTransform.y;
|
||||
MAD result.texcoord[4], position.xzzz, territoryTransform.x, territoryTransform.y;
|
||||
|
||||
END
|
||||
|
|
|
|||
|
|
@ -7,14 +7,12 @@
|
|||
<uniform name="sunColor" loc="0" type="vec3"/>
|
||||
<uniform name="losTransform" loc="1" type="vec2"/>
|
||||
<uniform name="shadowTransform" loc="2" type="mat4"/>
|
||||
<uniform name="territoryTransform" loc="6" type="vec2"/>
|
||||
</vertex>
|
||||
|
||||
<fragment file="terrain_common.fp">
|
||||
<uniform name="baseTex" loc="0" type="sampler2D"/>
|
||||
<uniform name="shadowTex" loc="2" type="sampler2DShadow"/>
|
||||
<uniform name="losTex" loc="3" type="sampler2D"/>
|
||||
<uniform name="territoryTex" loc="4" type="sampler2D"/>
|
||||
|
||||
<uniform name="ambient" loc="0" type="vec3"/>
|
||||
<uniform name="shadingColor" loc="1" type="vec3"/>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
<PassabilityClasses>
|
||||
|
||||
<!-- Unit pathfinding classes: -->
|
||||
<unrestricted/>
|
||||
<default>
|
||||
<MaxWaterDepth>2</MaxWaterDepth>
|
||||
<MaxTerrainSlope>1.0</MaxTerrainSlope>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TerritoryManager>
|
||||
<ImpassableCost>4</ImpassableCost>
|
||||
<BorderThickness>0.5</BorderThickness>
|
||||
<BorderSeparation>0.55</BorderSeparation>
|
||||
</TerritoryManager>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -18,10 +18,13 @@
|
|||
#ifndef INCLUDED_GRAPHICS_OVERLAY
|
||||
#define INCLUDED_GRAPHICS_OVERLAY
|
||||
|
||||
#include "graphics/RenderableObject.h"
|
||||
#include "graphics/Texture.h"
|
||||
#include "maths/Vector3D.h"
|
||||
#include "ps/Overlay.h" // CColor (TODO: that file has nothing to do with overlays, it should be renamed)
|
||||
|
||||
class CTerrain;
|
||||
|
||||
/**
|
||||
* Line-based overlay, with world-space coordinates, rendered in the world
|
||||
* potentially behind other objects. Designed for selection circles and debug info.
|
||||
|
|
@ -35,6 +38,24 @@ struct SOverlayLine
|
|||
u8 m_Thickness; // pixels
|
||||
};
|
||||
|
||||
/**
|
||||
* Textured line overlay, with world-space coordinates, rendered in the world
|
||||
* onto the terrain. Designed for territory borders.
|
||||
*/
|
||||
struct SOverlayTexturedLine
|
||||
{
|
||||
SOverlayTexturedLine() : m_Terrain(NULL), m_Thickness(1.0f) { }
|
||||
|
||||
CTerrain* m_Terrain;
|
||||
CTexturePtr m_TextureBase;
|
||||
CTexturePtr m_TextureMask;
|
||||
CColor m_Color;
|
||||
std::vector<float> m_Coords; // (x, z) vertex coordinate pairs; y is computed automatically; shape is automatically closed
|
||||
float m_Thickness; // world-space units
|
||||
|
||||
shared_ptr<CRenderData> m_RenderData; // cached renderer data (shared_ptr so that copies/deletes are automatic)
|
||||
};
|
||||
|
||||
/**
|
||||
* Billboard sprite overlay, with world-space coordinates, rendered on top
|
||||
* of all other objects. Designed for health bars and rank icons.
|
||||
|
|
|
|||
|
|
@ -223,6 +223,51 @@ void CTerrain::CalcNormalFixed(ssize_t i, ssize_t j, CFixedVector3D& normal) con
|
|||
normal.Normalize();
|
||||
}
|
||||
|
||||
CVector3D CTerrain::CalcExactNormal(float x, float z) const
|
||||
{
|
||||
// Clamp to size-2 so we can use the tiles (xi,zi)-(xi+1,zi+1)
|
||||
const ssize_t xi = clamp((ssize_t)floor(x/CELL_SIZE), (ssize_t)0, m_MapSize-2);
|
||||
const ssize_t zi = clamp((ssize_t)floor(z/CELL_SIZE), (ssize_t)0, m_MapSize-2);
|
||||
|
||||
const float xf = clamp(x/CELL_SIZE-xi, 0.0f, 1.0f);
|
||||
const float zf = clamp(z/CELL_SIZE-zi, 0.0f, 1.0f);
|
||||
|
||||
float h00 = m_Heightmap[zi*m_MapSize + xi];
|
||||
float h01 = m_Heightmap[(zi+1)*m_MapSize + xi];
|
||||
float h10 = m_Heightmap[zi*m_MapSize + (xi+1)];
|
||||
float h11 = m_Heightmap[(zi+1)*m_MapSize + (xi+1)];
|
||||
|
||||
// Determine which terrain triangle this point is on,
|
||||
// then compute the normal of that triangle's plane
|
||||
|
||||
if (GetTriangulationDir(xi, zi))
|
||||
{
|
||||
if (xf + zf <= 1.f)
|
||||
{
|
||||
// Lower-left triangle (don't use h11)
|
||||
return -CVector3D(CELL_SIZE, (h10-h00)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h01-h00)*HEIGHT_SCALE, CELL_SIZE)).Normalized();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Upper-right triangle (don't use h00)
|
||||
return -CVector3D(CELL_SIZE, (h11-h01)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h11-h10)*HEIGHT_SCALE, CELL_SIZE)).Normalized();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (xf <= zf)
|
||||
{
|
||||
// Upper-left triangle (don't use h10)
|
||||
return -CVector3D(CELL_SIZE, (h11-h01)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h01-h00)*HEIGHT_SCALE, CELL_SIZE)).Normalized();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Lower-right triangle (don't use h01)
|
||||
return -CVector3D(CELL_SIZE, (h10-h00)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h11-h10)*HEIGHT_SCALE, CELL_SIZE)).Normalized();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// GetPatch: return the patch at (i,j) in patch space, or null if the patch is
|
||||
// out of bounds
|
||||
|
|
@ -298,10 +343,36 @@ float CTerrain::GetExactGroundLevel(float x, float z) const
|
|||
float h01 = m_Heightmap[(zi+1)*m_MapSize + xi];
|
||||
float h10 = m_Heightmap[zi*m_MapSize + (xi+1)];
|
||||
float h11 = m_Heightmap[(zi+1)*m_MapSize + (xi+1)];
|
||||
// Linearly interpolate
|
||||
return (HEIGHT_SCALE * (
|
||||
(1 - zf) * ((1 - xf) * h00 + xf * h10)
|
||||
+ zf * ((1 - xf) * h01 + xf * h11)));
|
||||
|
||||
// Determine which terrain triangle this point is on,
|
||||
// then compute the linearly-interpolated height on that triangle's plane
|
||||
|
||||
if (GetTriangulationDir(xi, zi))
|
||||
{
|
||||
if (xf + zf <= 1.f)
|
||||
{
|
||||
// Lower-left triangle (don't use h11)
|
||||
return HEIGHT_SCALE * (h00 + (h10-h00)*xf + (h01-h00)*zf);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Upper-right triangle (don't use h00)
|
||||
return HEIGHT_SCALE * (h11 + (h01-h11)*(1-xf) + (h10-h11)*(1-zf));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (xf <= zf)
|
||||
{
|
||||
// Upper-left triangle (don't use h10)
|
||||
return HEIGHT_SCALE * (h00 + (h11-h01)*xf + (h01-h00)*zf);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Lower-right triangle (don't use h01)
|
||||
return HEIGHT_SCALE * (h00 + (h10-h00)*xf + (h11-h10)*zf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fixed CTerrain::GetExactGroundLevelFixed(fixed x, fixed z) const
|
||||
|
|
@ -328,6 +399,9 @@ fixed CTerrain::GetExactGroundLevelFixed(fixed x, fixed z) const
|
|||
// Linearly interpolate
|
||||
return ((one - zf).Multiply(xf1 * h00 + xf0 * h10)
|
||||
+ zf.Multiply(xf1 * h01 + xf0 * h11)) / (int)(HEIGHT_UNITS_PER_METRE / 2);
|
||||
|
||||
// TODO: This should probably be more like GetExactGroundLevel()
|
||||
// in handling triangulation properly
|
||||
}
|
||||
|
||||
bool CTerrain::GetTriangulationDir(ssize_t i, ssize_t j) const
|
||||
|
|
|
|||
|
|
@ -126,6 +126,8 @@ public:
|
|||
void CalcNormal(ssize_t i, ssize_t j, CVector3D& normal) const;
|
||||
void CalcNormalFixed(ssize_t i, ssize_t j, CFixedVector3D& normal) const;
|
||||
|
||||
CVector3D CalcExactNormal(float x, float z) const;
|
||||
|
||||
// flatten out an area of terrain (specified in world space coords); return
|
||||
// the average height of the flattened area
|
||||
float FlattenArea(float x0, float x1, float z0, float z1);
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ struct TPhash
|
|||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, a.m_Path);
|
||||
boost::hash_combine(seed, a.m_Filter);
|
||||
boost::hash_combine(seed, a.m_Wrap);
|
||||
boost::hash_combine(seed, a.m_WrapS);
|
||||
boost::hash_combine(seed, a.m_WrapT);
|
||||
boost::hash_combine(seed, a.m_Aniso);
|
||||
return seed;
|
||||
}
|
||||
|
|
@ -61,7 +62,8 @@ struct TPequal_to
|
|||
bool operator()(CTextureProperties const& a, CTextureProperties const& b) const
|
||||
{
|
||||
return a.m_Path == b.m_Path && a.m_Filter == b.m_Filter
|
||||
&& a.m_Wrap == b.m_Wrap && a.m_Aniso == b.m_Aniso;
|
||||
&& a.m_WrapS == b.m_WrapS && a.m_WrapT == b.m_WrapT
|
||||
&& a.m_Aniso == b.m_Aniso;
|
||||
}
|
||||
bool operator()(CTexturePtr const& a, CTexturePtr const& b) const
|
||||
{
|
||||
|
|
@ -186,7 +188,7 @@ public:
|
|||
(void)ogl_tex_get_average_colour(h, &texture->m_BaseColour);
|
||||
|
||||
// Set GL upload properties
|
||||
(void)ogl_tex_set_wrap(h, texture->m_Properties.m_Wrap);
|
||||
(void)ogl_tex_set_wrap(h, texture->m_Properties.m_WrapS, texture->m_Properties.m_WrapT);
|
||||
(void)ogl_tex_set_anisotropy(h, texture->m_Properties.m_Aniso);
|
||||
|
||||
// Prevent ogl_tex automatically generating mipmaps (which is slow and unwanted),
|
||||
|
|
|
|||
|
|
@ -129,7 +129,8 @@ public:
|
|||
* Use the given texture name, and default GL parameters.
|
||||
*/
|
||||
explicit CTextureProperties(const VfsPath& path) :
|
||||
m_Path(path), m_Filter(GL_LINEAR_MIPMAP_LINEAR), m_Wrap(GL_REPEAT), m_Aniso(1.0f)
|
||||
m_Path(path), m_Filter(GL_LINEAR_MIPMAP_LINEAR),
|
||||
m_WrapS(GL_REPEAT), m_WrapT(GL_REPEAT), m_Aniso(1.0f)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +142,13 @@ public:
|
|||
/**
|
||||
* Set wrapping mode (typically GL_REPEAT, GL_CLAMP_TO_EDGE, etc).
|
||||
*/
|
||||
void SetWrap(GLint wrap) { m_Wrap = wrap; }
|
||||
void SetWrap(GLint wrap) { m_WrapS = wrap; m_WrapT = wrap; }
|
||||
|
||||
/**
|
||||
* Set wrapping mode (typically GL_REPEAT, GL_CLAMP_TO_EDGE, etc),
|
||||
* separately for S and T.
|
||||
*/
|
||||
void SetWrap(GLint wrap_s, GLint wrap_t) { m_WrapS = wrap_s; m_WrapT = wrap_t; }
|
||||
|
||||
/**
|
||||
* Set maximum anisotropy value. Must be >= 1.0. Should be a power of 2.
|
||||
|
|
@ -168,7 +175,8 @@ public:
|
|||
private:
|
||||
VfsPath m_Path;
|
||||
GLint m_Filter;
|
||||
GLint m_Wrap;
|
||||
GLint m_WrapS;
|
||||
GLint m_WrapT;
|
||||
float m_Aniso;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -275,10 +275,8 @@ struct OglTexState
|
|||
// mipmaps aren't called for and filter could be NEAREST anyway).
|
||||
GLint filter;
|
||||
// .. wrap mode
|
||||
// note: to simplify things, we assume that apps will never want to
|
||||
// set S/T modes independently. it that becomes necessary,
|
||||
// it's easy to add.
|
||||
GLint wrap;
|
||||
GLint wrap_s;
|
||||
GLint wrap_t;
|
||||
// .. anisotropy
|
||||
// note: ignored unless EXT_texture_filter_anisotropic is supported.
|
||||
GLfloat anisotropy;
|
||||
|
|
@ -289,7 +287,8 @@ struct OglTexState
|
|||
static void state_set_to_defaults(OglTexState* ots)
|
||||
{
|
||||
ots->filter = default_filter;
|
||||
ots->wrap = GL_REPEAT;
|
||||
ots->wrap_s = GL_REPEAT;
|
||||
ots->wrap_t = GL_REPEAT;
|
||||
ots->anisotropy = 1.0f;
|
||||
}
|
||||
|
||||
|
|
@ -305,13 +304,14 @@ static void state_latch(OglTexState* ots)
|
|||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
|
||||
|
||||
// wrap
|
||||
const GLint wrap = ots->wrap;
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
|
||||
const GLint wrap_s = ots->wrap_s;
|
||||
const GLint wrap_t = ots->wrap_t;
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t);
|
||||
// .. only CLAMP and REPEAT are guaranteed to be available.
|
||||
// if we're using one of the others, we squelch the error that
|
||||
// may have resulted if this GL implementation is old.
|
||||
if(wrap != GL_CLAMP && wrap != GL_REPEAT)
|
||||
if((wrap_s != GL_CLAMP && wrap_s != GL_REPEAT) || (wrap_t != GL_CLAMP && wrap_t != GL_REPEAT))
|
||||
ogl_SquelchError(GL_INVALID_ENUM);
|
||||
|
||||
// anisotropy
|
||||
|
|
@ -481,16 +481,18 @@ static Status OglTex_validate(const OglTex* ot)
|
|||
// texture state
|
||||
if(!filter_valid(ot->state.filter))
|
||||
WARN_RETURN(ERR::_14);
|
||||
if(!wrap_valid(ot->state.wrap))
|
||||
if(!wrap_valid(ot->state.wrap_s))
|
||||
WARN_RETURN(ERR::_15);
|
||||
if(!wrap_valid(ot->state.wrap_t))
|
||||
WARN_RETURN(ERR::_16);
|
||||
|
||||
// misc
|
||||
if(!q_flags_valid(ot->q_flags))
|
||||
WARN_RETURN(ERR::_16);
|
||||
if(ot->tmu >= 128) // unexpected that there will ever be this many
|
||||
WARN_RETURN(ERR::_17);
|
||||
if(ot->flags > OT_ALL_FLAGS)
|
||||
if(ot->tmu >= 128) // unexpected that there will ever be this many
|
||||
WARN_RETURN(ERR::_18);
|
||||
if(ot->flags > OT_ALL_FLAGS)
|
||||
WARN_RETURN(ERR::_19);
|
||||
// .. note: don't check ot->fmt and ot->int_fmt - they aren't set
|
||||
// until during ogl_tex_upload.
|
||||
|
||||
|
|
@ -620,19 +622,22 @@ Status ogl_tex_set_filter(Handle ht, GLint filter)
|
|||
|
||||
// override default wrap mode (GL_REPEAT) for this texture.
|
||||
// must be called before uploading (raises a warning if called afterwards).
|
||||
// wrap is as defined by OpenGL and applies to both S and T coordinates
|
||||
// (rationale: see OglTexState).
|
||||
Status ogl_tex_set_wrap(Handle ht, GLint wrap)
|
||||
// wrap is as defined by OpenGL.
|
||||
Status ogl_tex_set_wrap(Handle ht, GLint wrap_s, GLint wrap_t)
|
||||
{
|
||||
H_DEREF(ht, OglTex, ot);
|
||||
|
||||
if(!wrap_valid(wrap))
|
||||
if(!wrap_valid(wrap_s))
|
||||
WARN_RETURN(ERR::INVALID_PARAM);
|
||||
|
||||
if(ot->state.wrap != wrap)
|
||||
if(!wrap_valid(wrap_t))
|
||||
WARN_RETURN(ERR::INVALID_PARAM);
|
||||
|
||||
if(ot->state.wrap_s != wrap_s || ot->state.wrap_t != wrap_t)
|
||||
{
|
||||
warn_if_uploaded(ht, ot);
|
||||
ot->state.wrap = wrap;
|
||||
ot->state.wrap_s = wrap_s;
|
||||
ot->state.wrap_t = wrap_t;
|
||||
}
|
||||
return INFO::OK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -289,13 +289,13 @@ extern Status ogl_tex_set_filter(Handle ht, GLint filter);
|
|||
* Override default wrap mode (GL_REPEAT) for this texture.
|
||||
*
|
||||
* @param ht Texture handle
|
||||
* @param wrap OpenGL wrap mode (for both S and T coordinates)
|
||||
* (rationale: see {@link OglTexState})
|
||||
* @param wrap_s OpenGL wrap mode for S coordinates
|
||||
* @param wrap_t OpenGL wrap mode for T coordinates
|
||||
* @return Status
|
||||
*
|
||||
* Must be called before uploading (raises a warning if called afterwards).
|
||||
*/
|
||||
extern Status ogl_tex_set_wrap(Handle ht, GLint wrap);
|
||||
extern Status ogl_tex_set_wrap(Handle ht, GLint wrap_s, GLint wrap_t);
|
||||
|
||||
/**
|
||||
* Override default maximum anisotropic filtering for this texture.
|
||||
|
|
|
|||
|
|
@ -48,14 +48,14 @@ namespace
|
|||
Noise2D::Noise2D(int f)
|
||||
{
|
||||
freq = f;
|
||||
grads = new CVector2D_Maths*[freq];
|
||||
grads = new CVector2D*[freq];
|
||||
for(int i=0; i<freq; i++)
|
||||
{
|
||||
grads[i] = new CVector2D_Maths[freq];
|
||||
grads[i] = new CVector2D[freq];
|
||||
for(int j=0; j<freq; j++)
|
||||
{
|
||||
float a = randFloat() * 2 * (float)M_PI;
|
||||
grads[i][j] = CVector2D_Maths(cos(a), sin(a));
|
||||
grads[i][j] = CVector2D(cos(a), sin(a));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -86,10 +86,10 @@ float Noise2D::operator()(float x, float y)
|
|||
int ix1 = (ix+1) % freq;
|
||||
int iy1 = (iy+1) % freq;
|
||||
|
||||
float s = grads[ix][iy].Dot(CVector2D_Maths(fx, fy));
|
||||
float t = grads[ix1][iy].Dot(CVector2D_Maths(fx-1, fy));
|
||||
float u = grads[ix][iy1].Dot(CVector2D_Maths(fx, fy-1));
|
||||
float v = grads[ix1][iy1].Dot(CVector2D_Maths(fx-1, fy-1));
|
||||
float s = grads[ix][iy].Dot(CVector2D(fx, fy));
|
||||
float t = grads[ix1][iy].Dot(CVector2D(fx-1, fy));
|
||||
float u = grads[ix][iy1].Dot(CVector2D(fx, fy-1));
|
||||
float v = grads[ix1][iy1].Dot(CVector2D(fx-1, fy-1));
|
||||
|
||||
float ex = easeCurve(fx);
|
||||
float ey = easeCurve(fy);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class Noise2D
|
|||
int freq;
|
||||
|
||||
/// freq*freq random gradient vectors in the unit cube
|
||||
CVector2D_Maths** grads;
|
||||
CVector2D** grads;
|
||||
public:
|
||||
Noise2D(int freq);
|
||||
~Noise2D();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -27,79 +27,104 @@
|
|||
#include <math.h>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CVector2D_Maths:
|
||||
class CVector2D_Maths
|
||||
// CVector2D:
|
||||
class CVector2D
|
||||
{
|
||||
public:
|
||||
CVector2D_Maths() {}
|
||||
CVector2D_Maths(float x,float y) { X=x; Y=y; }
|
||||
CVector2D_Maths(const CVector2D_Maths& p) { X=p.X; Y=p.Y; }
|
||||
CVector2D() {}
|
||||
CVector2D(float x, float y) : X(x), Y(y) {}
|
||||
|
||||
operator float*() {
|
||||
operator float*()
|
||||
{
|
||||
return &X;
|
||||
}
|
||||
|
||||
operator const float*() const {
|
||||
operator const float*() const
|
||||
{
|
||||
return &X;
|
||||
}
|
||||
|
||||
CVector2D_Maths operator-() const {
|
||||
return CVector2D_Maths(-X, -Y);
|
||||
CVector2D operator-() const
|
||||
{
|
||||
return CVector2D(-X, -Y);
|
||||
}
|
||||
|
||||
CVector2D_Maths operator+(const CVector2D_Maths& t) const {
|
||||
return CVector2D_Maths(X+t.X, Y+t.Y);
|
||||
CVector2D operator+(const CVector2D& t) const
|
||||
{
|
||||
return CVector2D(X + t.X, Y + t.Y);
|
||||
}
|
||||
|
||||
CVector2D_Maths operator-(const CVector2D_Maths& t) const {
|
||||
return CVector2D_Maths(X-t.X, Y-t.Y);
|
||||
CVector2D operator-(const CVector2D& t) const
|
||||
{
|
||||
return CVector2D(X - t.X, Y - t.Y);
|
||||
}
|
||||
|
||||
CVector2D_Maths operator*(float f) const {
|
||||
return CVector2D_Maths(X*f, Y*f);
|
||||
CVector2D operator*(float f) const
|
||||
{
|
||||
return CVector2D(X * f, Y * f);
|
||||
}
|
||||
|
||||
CVector2D_Maths operator/(float f) const {
|
||||
float inv=1.0f/f;
|
||||
return CVector2D_Maths(X*inv, Y*inv);
|
||||
CVector2D operator/(float f) const
|
||||
{
|
||||
float inv = 1.0f / f;
|
||||
return CVector2D(X * inv, Y * inv);
|
||||
}
|
||||
|
||||
CVector2D_Maths& operator+=(const CVector2D_Maths& t) {
|
||||
X+=t.X; Y+=t.Y;
|
||||
return *this;
|
||||
CVector2D& operator+=(const CVector2D& t)
|
||||
{
|
||||
X += t.X;
|
||||
Y += t.Y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CVector2D_Maths& operator-=(const CVector2D_Maths& t) {
|
||||
X-=t.X; Y-=t.Y;
|
||||
return *this;
|
||||
CVector2D& operator-=(const CVector2D& t)
|
||||
{
|
||||
X -= t.X;
|
||||
Y -= t.Y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CVector2D_Maths& operator*=(float f) {
|
||||
X*=f; Y*=f;
|
||||
return *this;
|
||||
CVector2D& operator*=(float f)
|
||||
{
|
||||
X *= f;
|
||||
Y *= f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CVector2D_Maths& operator/=(float f) {
|
||||
float invf=1.0f/f;
|
||||
X*=invf; Y*=invf;
|
||||
return *this;
|
||||
CVector2D& operator/=(float f)
|
||||
{
|
||||
float invf = 1.0f / f;
|
||||
X *= invf;
|
||||
Y *= invf;
|
||||
return *this;
|
||||
}
|
||||
|
||||
float Dot(const CVector2D_Maths& a) const {
|
||||
return X*a.X + Y*a.Y;
|
||||
float Dot(const CVector2D& a) const
|
||||
{
|
||||
return X * a.X + Y * a.Y;
|
||||
}
|
||||
|
||||
float LengthSquared() const {
|
||||
return Dot(*this);
|
||||
float LengthSquared() const
|
||||
{
|
||||
return Dot(*this);
|
||||
}
|
||||
|
||||
float Length() const {
|
||||
return (float) sqrt(LengthSquared());
|
||||
float Length() const
|
||||
{
|
||||
return (float)sqrt(LengthSquared());
|
||||
}
|
||||
|
||||
void Normalize() {
|
||||
float mag=Length();
|
||||
X/=mag; Y/=mag;
|
||||
void Normalize()
|
||||
{
|
||||
float mag = Length();
|
||||
X /= mag;
|
||||
Y /= mag;
|
||||
}
|
||||
|
||||
CVector2D Normalized()
|
||||
{
|
||||
float mag = Length();
|
||||
return CVector2D(X / mag, Y / mag);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -19,17 +19,59 @@
|
|||
|
||||
#include "OverlayRenderer.h"
|
||||
|
||||
#include "graphics/LOSTexture.h"
|
||||
#include "graphics/Overlay.h"
|
||||
#include "graphics/ShaderManager.h"
|
||||
#include "graphics/Terrain.h"
|
||||
#include "graphics/TextureManager.h"
|
||||
#include "lib/ogl.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "renderer/Renderer.h"
|
||||
#include "renderer/VertexBuffer.h"
|
||||
#include "renderer/VertexBufferManager.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "simulation2/components/ICmpWaterManager.h"
|
||||
|
||||
struct OverlayRendererInternals
|
||||
{
|
||||
std::vector<SOverlayLine*> lines;
|
||||
std::vector<SOverlayTexturedLine*> texlines;
|
||||
std::vector<SOverlaySprite*> sprites;
|
||||
};
|
||||
|
||||
class CTexturedLineRData : public CRenderData
|
||||
{
|
||||
public:
|
||||
CTexturedLineRData(SOverlayTexturedLine* line) :
|
||||
m_Line(line), m_VB(NULL), m_VBIndices(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
~CTexturedLineRData()
|
||||
{
|
||||
if (m_VB)
|
||||
g_VBMan.Release(m_VB);
|
||||
if (m_VBIndices)
|
||||
g_VBMan.Release(m_VBIndices);
|
||||
}
|
||||
|
||||
struct SVertex
|
||||
{
|
||||
SVertex(CVector3D pos, short u, short v) : m_Position(pos) { m_UVs[0] = u; m_UVs[1] = v; }
|
||||
CVector3D m_Position;
|
||||
GLshort m_UVs[2];
|
||||
};
|
||||
cassert(sizeof(SVertex) == 16);
|
||||
|
||||
void Update();
|
||||
|
||||
SOverlayTexturedLine* m_Line;
|
||||
|
||||
CVertexBuffer::VBChunk* m_VB;
|
||||
CVertexBuffer::VBChunk* m_VBIndices;
|
||||
};
|
||||
|
||||
OverlayRenderer::OverlayRenderer()
|
||||
{
|
||||
m = new OverlayRendererInternals();
|
||||
|
|
@ -40,9 +82,22 @@ OverlayRenderer::~OverlayRenderer()
|
|||
delete m;
|
||||
}
|
||||
|
||||
void OverlayRenderer::Submit(SOverlayLine* overlay)
|
||||
void OverlayRenderer::Submit(SOverlayLine* line)
|
||||
{
|
||||
m->lines.push_back(overlay);
|
||||
ENSURE(line->m_Coords.size() % 3 == 0);
|
||||
|
||||
m->lines.push_back(line);
|
||||
}
|
||||
|
||||
void OverlayRenderer::Submit(SOverlayTexturedLine* line)
|
||||
{
|
||||
// Simplify the rest of the code by guaranteeing non-empty lines
|
||||
if (line->m_Coords.empty())
|
||||
return;
|
||||
|
||||
ENSURE(line->m_Coords.size() % 2 == 0);
|
||||
|
||||
m->texlines.push_back(line);
|
||||
}
|
||||
|
||||
void OverlayRenderer::Submit(SOverlaySprite* overlay)
|
||||
|
|
@ -53,6 +108,7 @@ void OverlayRenderer::Submit(SOverlaySprite* overlay)
|
|||
void OverlayRenderer::EndFrame()
|
||||
{
|
||||
m->lines.clear();
|
||||
m->texlines.clear();
|
||||
m->sprites.clear();
|
||||
// this should leave the capacity unchanged, which is okay since it
|
||||
// won't be very large or very variable
|
||||
|
|
@ -60,12 +116,30 @@ void OverlayRenderer::EndFrame()
|
|||
|
||||
void OverlayRenderer::PrepareForRendering()
|
||||
{
|
||||
PROFILE("prepare overlays");
|
||||
|
||||
// This is where we should do something like sort the overlays by
|
||||
// colour/sprite/etc for more efficient rendering
|
||||
|
||||
for (size_t i = 0; i < m->texlines.size(); ++i)
|
||||
{
|
||||
SOverlayTexturedLine* line = m->texlines[i];
|
||||
if (!line->m_RenderData)
|
||||
{
|
||||
line->m_RenderData = shared_ptr<CRenderData>(new CTexturedLineRData(line));
|
||||
static_cast<CTexturedLineRData*>(line->m_RenderData.get())->Update();
|
||||
// We assume the overlay line will get replaced by the caller
|
||||
// if terrain changes, so we don't need to detect that here and
|
||||
// call Update again. Also we assume the caller won't change
|
||||
// any of the parameters after first submitting the line.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OverlayRenderer::RenderOverlays()
|
||||
void OverlayRenderer::RenderOverlaysBeforeWater()
|
||||
{
|
||||
PROFILE("render overlays (before water)");
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
|
|
@ -90,8 +164,76 @@ void OverlayRenderer::RenderOverlays()
|
|||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
void OverlayRenderer::RenderOverlaysAfterWater()
|
||||
{
|
||||
PROFILE("render overlays (after water)");
|
||||
|
||||
// Only supported in shader modes
|
||||
// (TODO: should support in non-shader too)
|
||||
if (g_Renderer.GetRenderPath() != CRenderer::RP_SHADER)
|
||||
return;
|
||||
|
||||
if (!m->texlines.empty())
|
||||
{
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
glDepthMask(0);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
CShaderManager& shaderManager = g_Renderer.GetShaderManager();
|
||||
CShaderProgramPtr shaderTexLine(shaderManager.LoadProgram("overlayline", std::map<CStr, CStr>()));
|
||||
|
||||
shaderTexLine->Bind();
|
||||
|
||||
CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
|
||||
shaderTexLine->BindTexture("losTex", los.GetTexture());
|
||||
shaderTexLine->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
|
||||
|
||||
for (size_t i = 0; i < m->texlines.size(); ++i)
|
||||
{
|
||||
SOverlayTexturedLine* line = m->texlines[i];
|
||||
if (!line->m_RenderData)
|
||||
continue;
|
||||
|
||||
shaderTexLine->BindTexture("baseTex", line->m_TextureBase->GetHandle());
|
||||
shaderTexLine->BindTexture("maskTex", line->m_TextureMask->GetHandle());
|
||||
shaderTexLine->Uniform("objectColor", line->m_Color);
|
||||
|
||||
CTexturedLineRData* rdata = static_cast<CTexturedLineRData*>(line->m_RenderData.get());
|
||||
|
||||
GLsizei stride = sizeof(CTexturedLineRData::SVertex);
|
||||
CTexturedLineRData::SVertex* base = reinterpret_cast<CTexturedLineRData::SVertex*>(rdata->m_VB->m_Owner->Bind());
|
||||
|
||||
glVertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]);
|
||||
glTexCoordPointer(2, GL_SHORT, stride, &base->m_UVs[0]);
|
||||
|
||||
u8* indexBase = rdata->m_VBIndices->m_Owner->Bind();
|
||||
glDrawElements(GL_QUAD_STRIP, rdata->m_VBIndices->m_Count, GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*rdata->m_VBIndices->m_Index);
|
||||
|
||||
g_Renderer.GetStats().m_OverlayTris += rdata->m_VBIndices->m_Count - 2;
|
||||
}
|
||||
|
||||
shaderTexLine->Unbind();
|
||||
|
||||
// TODO: the shader should probably be responsible for unbinding its textures
|
||||
g_Renderer.BindTexture(1, 0);
|
||||
g_Renderer.BindTexture(0, 0);
|
||||
|
||||
CVertexBuffer::Unbind();
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
glDepthMask(1);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
}
|
||||
|
||||
void OverlayRenderer::RenderForegroundOverlays(const CCamera& viewCamera)
|
||||
{
|
||||
PROFILE("render overlays (fg)");
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
|
@ -132,3 +274,126 @@ void OverlayRenderer::RenderForegroundOverlays(const CCamera& viewCamera)
|
|||
glDisable(GL_BLEND);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
void CTexturedLineRData::Update()
|
||||
{
|
||||
if (m_VB)
|
||||
{
|
||||
g_VBMan.Release(m_VB);
|
||||
m_VB = NULL;
|
||||
}
|
||||
|
||||
if (m_VBIndices)
|
||||
{
|
||||
g_VBMan.Release(m_VBIndices);
|
||||
m_VBIndices = NULL;
|
||||
}
|
||||
|
||||
CmpPtr<ICmpWaterManager> cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
|
||||
|
||||
std::vector<SVertex> vertices;
|
||||
std::vector<u16> indices;
|
||||
|
||||
short v = 0;
|
||||
|
||||
size_t n = m_Line->m_Coords.size() / 2;
|
||||
ENSURE(n >= 1);
|
||||
|
||||
CTerrain* terrain = m_Line->m_Terrain;
|
||||
|
||||
// TODO: this assumes paths are closed loops; probably should extend this to
|
||||
// handle non-closed paths too
|
||||
|
||||
// In each iteration, p1 is the position of vertex i, p0 is i-1, p2 is i+1.
|
||||
// To avoid slightly expensive terrain computations we cycle these around and
|
||||
// recompute p2 at the end of each iteration.
|
||||
CVector3D p0 = CVector3D(m_Line->m_Coords[(n-1)*2], 0, m_Line->m_Coords[(n-1)*2+1]);
|
||||
CVector3D p1 = CVector3D(m_Line->m_Coords[0], 0, m_Line->m_Coords[1]);
|
||||
CVector3D p2 = CVector3D(m_Line->m_Coords[(1 % n)*2], 0, m_Line->m_Coords[(1 % n)*2+1]);
|
||||
bool p1floating = false;
|
||||
bool p2floating = false;
|
||||
|
||||
// Compute terrain heights, clamped to the water height (and remember whether
|
||||
// each point was floating on water, for normal computation later)
|
||||
|
||||
// TODO: if we ever support more than one water level per map, recompute this per point
|
||||
float w = cmpWaterManager->GetExactWaterLevel(p0.X, p0.Z);
|
||||
|
||||
p0.Y = terrain->GetExactGroundLevel(p0.X, p0.Z);
|
||||
if (p0.Y < w)
|
||||
p0.Y = w;
|
||||
|
||||
p1.Y = terrain->GetExactGroundLevel(p1.X, p1.Z);
|
||||
if (p1.Y < w)
|
||||
{
|
||||
p1.Y = w;
|
||||
p1floating = true;
|
||||
}
|
||||
|
||||
p2.Y = terrain->GetExactGroundLevel(p2.X, p2.Z);
|
||||
if (p2.Y < w)
|
||||
{
|
||||
p2.Y = w;
|
||||
p2floating = true;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
// For vertex i, compute bisector of lines (i-1)..(i) and (i)..(i+1)
|
||||
// perpendicular to terrain normal
|
||||
|
||||
// Normal is vertical if on water, else computed from terrain
|
||||
CVector3D norm;
|
||||
if (p1floating)
|
||||
norm = CVector3D(0, 1, 0);
|
||||
else
|
||||
norm = m_Line->m_Terrain->CalcExactNormal(p1.X, p1.Z);
|
||||
|
||||
CVector3D b = ((p1 - p0).Normalized() + (p2 - p1).Normalized()).Cross(norm);
|
||||
|
||||
// Adjust bisector length to match the line thickness, along the line's width
|
||||
float l = b.Dot((p2 - p1).Normalized().Cross(norm));
|
||||
if (fabs(l) > 0.000001f) // avoid unlikely divide-by-zero
|
||||
b *= m_Line->m_Thickness / l;
|
||||
|
||||
// Raise off the terrain a little bit
|
||||
const float raised = 0.2f;
|
||||
|
||||
vertices.push_back(SVertex(p1 + b + norm*raised, 0, v));
|
||||
indices.push_back(vertices.size() - 1);
|
||||
|
||||
vertices.push_back(SVertex(p1 - b + norm*raised, 1, v));
|
||||
indices.push_back(vertices.size() - 1);
|
||||
|
||||
// Alternate V coordinate for debugging
|
||||
v = 1 - v;
|
||||
|
||||
// Cycle the p's and compute the new p2
|
||||
p0 = p1;
|
||||
p1 = p2;
|
||||
p1floating = p2floating;
|
||||
p2 = CVector3D(m_Line->m_Coords[((i+2) % n)*2], 0, m_Line->m_Coords[((i+2) % n)*2+1]);
|
||||
p2.Y = terrain->GetExactGroundLevel(p2.X, p2.Z);
|
||||
if (p2.Y < w)
|
||||
{
|
||||
p2.Y = w;
|
||||
p2floating = true;
|
||||
}
|
||||
else
|
||||
p2floating = false;
|
||||
}
|
||||
|
||||
// Close the path
|
||||
indices.push_back(0);
|
||||
indices.push_back(1);
|
||||
|
||||
m_VB = g_VBMan.Allocate(sizeof(SVertex), vertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
|
||||
m_VB->m_Owner->UpdateChunkVertices(m_VB, &vertices[0]);
|
||||
|
||||
// Update the indices to include the base offset of the vertex data
|
||||
for (size_t k = 0; k < indices.size(); ++k)
|
||||
indices[k] += m_VB->m_Index;
|
||||
|
||||
m_VBIndices = g_VBMan.Allocate(sizeof(u16), indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
|
||||
m_VBIndices->m_Owner->UpdateChunkVertices(m_VBIndices, &indices[0]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#define INCLUDED_OVERLAYRENDERER
|
||||
|
||||
struct SOverlayLine;
|
||||
struct SOverlayTexturedLine;
|
||||
struct SOverlaySprite;
|
||||
class CCamera;
|
||||
|
||||
|
|
@ -39,6 +40,11 @@ public:
|
|||
*/
|
||||
void Submit(SOverlayLine* overlay);
|
||||
|
||||
/**
|
||||
* Add a textured line overlay for rendering in this frame.
|
||||
*/
|
||||
void Submit(SOverlayTexturedLine* overlay);
|
||||
|
||||
/**
|
||||
* Add a sprite overlay for rendering in this frame.
|
||||
*/
|
||||
|
|
@ -58,9 +64,17 @@ public:
|
|||
|
||||
/**
|
||||
* Render all the submitted overlays that are embedded in the world
|
||||
* (i.e. rendered behind other objects, underwater, etc).
|
||||
* (i.e. rendered behind other objects in the normal 3D way)
|
||||
* and should be drawn before water (i.e. may be visible under the water)
|
||||
*/
|
||||
void RenderOverlays();
|
||||
void RenderOverlaysBeforeWater();
|
||||
|
||||
/**
|
||||
* Render all the submitted overlays that are embedded in the world
|
||||
* (i.e. rendered behind other objects in the normal 3D way)
|
||||
* and should be drawn after water (i.e. may be visible on top of the water)
|
||||
*/
|
||||
void RenderOverlaysAfterWater();
|
||||
|
||||
/**
|
||||
* Render all the submitted overlays that should appear on top of everything
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ private:
|
|||
Row_TerrainTris,
|
||||
Row_WaterTris,
|
||||
Row_ModelTris,
|
||||
Row_OverlayTris,
|
||||
Row_BlendSplats,
|
||||
Row_Particles,
|
||||
Row_VBReserved,
|
||||
|
|
@ -175,6 +176,12 @@ CStr CRendererStatsTable::GetCellText(size_t row, size_t col)
|
|||
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_ModelTris);
|
||||
return buf;
|
||||
|
||||
case Row_OverlayTris:
|
||||
if (col == 0)
|
||||
return "# overlay tris";
|
||||
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_OverlayTris);
|
||||
return buf;
|
||||
|
||||
case Row_BlendSplats:
|
||||
if (col == 0)
|
||||
return "# blend splats";
|
||||
|
|
@ -1573,10 +1580,8 @@ void CRenderer::RenderSubmissions()
|
|||
TerrainOverlay::RenderOverlays();
|
||||
ogl_WarnIfError();
|
||||
|
||||
// render other debug-related overlays before water (so they can be displayed when underwater)
|
||||
PROFILE_START("render overlays");
|
||||
m->overlayRenderer.RenderOverlays();
|
||||
PROFILE_END("render overlays");
|
||||
// render other debug-related overlays before water (so they can be seen when underwater)
|
||||
m->overlayRenderer.RenderOverlaysBeforeWater();
|
||||
ogl_WarnIfError();
|
||||
|
||||
RenderModels();
|
||||
|
|
@ -1603,6 +1608,10 @@ void CRenderer::RenderSubmissions()
|
|||
ogl_WarnIfError();
|
||||
}
|
||||
|
||||
// render some other overlays after water (so they can be displayed on top of water)
|
||||
m->overlayRenderer.RenderOverlaysAfterWater();
|
||||
ogl_WarnIfError();
|
||||
|
||||
// particles are transparent so render after water
|
||||
RenderParticles();
|
||||
ogl_WarnIfError();
|
||||
|
|
@ -1622,9 +1631,7 @@ void CRenderer::RenderSubmissions()
|
|||
}
|
||||
|
||||
// render overlays that should appear on top of all other objects
|
||||
PROFILE_START("render fg overlays");
|
||||
m->overlayRenderer.RenderForegroundOverlays(m_ViewCamera);
|
||||
PROFILE_END("render fg overlays");
|
||||
ogl_WarnIfError();
|
||||
}
|
||||
|
||||
|
|
@ -1722,6 +1729,11 @@ void CRenderer::Submit(SOverlayLine* overlay)
|
|||
m->overlayRenderer.Submit(overlay);
|
||||
}
|
||||
|
||||
void CRenderer::Submit(SOverlayTexturedLine* overlay)
|
||||
{
|
||||
m->overlayRenderer.Submit(overlay);
|
||||
}
|
||||
|
||||
void CRenderer::Submit(SOverlaySprite* overlay)
|
||||
{
|
||||
m->overlayRenderer.Submit(overlay);
|
||||
|
|
@ -1932,7 +1944,7 @@ int CRenderer::LoadAlphaMaps()
|
|||
(void)tex_wrap(total_w, total_h, 8, TEX_GREY, data, 0, &t);
|
||||
m_hCompositeAlphaMap = ogl_tex_wrap(&t, g_VFS, key);
|
||||
(void)ogl_tex_set_filter(m_hCompositeAlphaMap, GL_LINEAR);
|
||||
(void)ogl_tex_set_wrap (m_hCompositeAlphaMap, GL_CLAMP_TO_EDGE);
|
||||
(void)ogl_tex_set_wrap (m_hCompositeAlphaMap, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
|
||||
int ret = ogl_tex_upload(m_hCompositeAlphaMap, 0, 0, GL_INTENSITY);
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -116,6 +116,8 @@ public:
|
|||
size_t m_WaterTris;
|
||||
// number of (non-transparent) model triangles drawn
|
||||
size_t m_ModelTris;
|
||||
// number of overlay triangles drawn
|
||||
size_t m_OverlayTris;
|
||||
// number of splat passes for alphamapping
|
||||
size_t m_BlendSplats;
|
||||
// number of particles
|
||||
|
|
@ -334,6 +336,7 @@ protected:
|
|||
//BEGIN: Implementation of SceneCollector
|
||||
void Submit(CPatch* patch);
|
||||
void Submit(SOverlayLine* overlay);
|
||||
void Submit(SOverlayTexturedLine* overlay);
|
||||
void Submit(SOverlaySprite* overlay);
|
||||
void Submit(CModelDecal* decal);
|
||||
void Submit(CParticleEmitter* emitter);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ class CPatch;
|
|||
class CLOSTexture;
|
||||
class CTerritoryTexture;
|
||||
struct SOverlayLine;
|
||||
struct SOverlayTexturedLine;
|
||||
struct SOverlaySprite;
|
||||
|
||||
class SceneCollector;
|
||||
|
|
@ -92,6 +93,11 @@ public:
|
|||
*/
|
||||
virtual void Submit(SOverlayLine* overlay) = 0;
|
||||
|
||||
/**
|
||||
* Submit a textured line overlay.
|
||||
*/
|
||||
virtual void Submit(SOverlayTexturedLine* overlay) = 0;
|
||||
|
||||
/**
|
||||
* Submit a sprite overlay.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@
|
|||
#include "VertexBufferManager.h"
|
||||
#include "ps/CLogger.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CVertexBuffer constructor
|
||||
CVertexBuffer::CVertexBuffer(size_t vertexSize, GLenum usage, GLenum target)
|
||||
: m_VertexSize(vertexSize), m_Handle(0), m_SysMem(0), m_Usage(usage), m_Target(target)
|
||||
{
|
||||
|
|
@ -66,22 +64,27 @@ CVertexBuffer::CVertexBuffer(size_t vertexSize, GLenum usage, GLenum target)
|
|||
m_FreeList.push_front(chunk);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CVertexBuffer destructor
|
||||
CVertexBuffer::~CVertexBuffer()
|
||||
{
|
||||
if (m_Handle)
|
||||
pglDeleteBuffersARB(1, &m_Handle);
|
||||
|
||||
if (m_SysMem)
|
||||
delete[] m_SysMem;
|
||||
delete[] m_SysMem;
|
||||
|
||||
// janwas 2004-06-14: release freelist
|
||||
typedef std::list<VBChunk*>::iterator Iter;
|
||||
for (Iter iter = m_FreeList.begin(); iter != m_FreeList.end(); ++iter)
|
||||
delete *iter;
|
||||
}
|
||||
|
||||
|
||||
bool CVertexBuffer::CompatibleVertexType(size_t vertexSize, GLenum usage, GLenum target)
|
||||
{
|
||||
if (usage != m_Usage || target != m_Target || vertexSize != m_VertexSize)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Allocate: try to allocate a buffer of given number of vertices (each of
|
||||
// given size), with the given type, and using the given texture - return null
|
||||
|
|
@ -89,7 +92,7 @@ CVertexBuffer::~CVertexBuffer()
|
|||
CVertexBuffer::VBChunk* CVertexBuffer::Allocate(size_t vertexSize, size_t numVertices, GLenum usage, GLenum target)
|
||||
{
|
||||
// check this is the right kind of buffer
|
||||
if (usage != m_Usage || target != m_Target || vertexSize != m_VertexSize)
|
||||
if (!CompatibleVertexType(vertexSize, usage, target))
|
||||
return 0;
|
||||
|
||||
// quick check there's enough vertices spare to allocate
|
||||
|
|
@ -138,11 +141,34 @@ CVertexBuffer::VBChunk* CVertexBuffer::Allocate(size_t vertexSize, size_t numVer
|
|||
// Release: return given chunk to this buffer
|
||||
void CVertexBuffer::Release(VBChunk* chunk)
|
||||
{
|
||||
// add to free list
|
||||
// TODO, RC - need to merge available chunks where possible to avoid
|
||||
// excessive fragmentation of vertex buffer space
|
||||
m_FreeList.push_front(chunk);
|
||||
// Update total free count before potentially modifying this chunk's count
|
||||
m_FreeVertices += chunk->m_Count;
|
||||
|
||||
typedef std::list<VBChunk*>::iterator Iter;
|
||||
|
||||
// Coalesce with any free-list items that are adjacent to this chunk;
|
||||
// merge the found chunk with the new one, and remove the old one
|
||||
// from the list, and repeat until no more are found
|
||||
bool coalesced;
|
||||
do
|
||||
{
|
||||
coalesced = false;
|
||||
for (Iter iter = m_FreeList.begin(); iter != m_FreeList.end(); ++iter)
|
||||
{
|
||||
if ((*iter)->m_Index == chunk->m_Index + chunk->m_Count
|
||||
|| (*iter)->m_Index + (*iter)->m_Count == chunk->m_Index)
|
||||
{
|
||||
chunk->m_Index = std::min(chunk->m_Index, (*iter)->m_Index);
|
||||
chunk->m_Count += (*iter)->m_Count;
|
||||
m_FreeList.erase(iter);
|
||||
coalesced = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (coalesced);
|
||||
|
||||
m_FreeList.push_front(chunk);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -205,3 +231,17 @@ size_t CVertexBuffer::GetBytesAllocated() const
|
|||
{
|
||||
return (m_MaxVertices - m_FreeVertices) * m_VertexSize;
|
||||
}
|
||||
|
||||
void CVertexBuffer::DumpStatus()
|
||||
{
|
||||
debug_printf(L"freeverts = %d\n", m_FreeVertices);
|
||||
|
||||
size_t maxSize = 0;
|
||||
typedef std::list<VBChunk*>::iterator Iter;
|
||||
for (Iter iter = m_FreeList.begin(); iter != m_FreeList.end(); ++iter)
|
||||
{
|
||||
debug_printf(L"free chunk %p: size=%d\n", *iter, (*iter)->m_Count);
|
||||
maxSize = std::max((*iter)->m_Count, maxSize);
|
||||
}
|
||||
debug_printf(L"max size = %d\n", maxSize);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,13 @@ public:
|
|||
size_t m_Index;
|
||||
// number of vertices used by chunk
|
||||
size_t m_Count;
|
||||
|
||||
private:
|
||||
// Only CVertexBuffer can construct/delete these
|
||||
// (Other people should use g_VBMan.Allocate, g_VBMan.Release)
|
||||
friend class CVertexBuffer;
|
||||
VBChunk() {}
|
||||
~VBChunk() {}
|
||||
};
|
||||
|
||||
public:
|
||||
|
|
@ -70,6 +77,10 @@ public:
|
|||
size_t GetBytesReserved() const;
|
||||
size_t GetBytesAllocated() const;
|
||||
|
||||
bool CompatibleVertexType(size_t vertexSize, GLenum usage, GLenum target);
|
||||
|
||||
void DumpStatus();
|
||||
|
||||
protected:
|
||||
friend class CVertexBufferManager; // allow allocate only via CVertexBufferManager
|
||||
|
||||
|
|
|
|||
|
|
@ -26,14 +26,10 @@
|
|||
#include "lib/ogl.h"
|
||||
#include "ps/CLogger.h"
|
||||
|
||||
#define DUMP_VB_STATS 0 // for debugging
|
||||
|
||||
CVertexBufferManager g_VBMan;
|
||||
|
||||
// janwas 2004-06-14: added dtor
|
||||
|
||||
CVertexBufferManager::~CVertexBufferManager()
|
||||
{
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Explicit shutdown of the vertex buffer subsystem.
|
||||
// This avoids the ordering issues that arise when using destructors of
|
||||
|
|
@ -61,9 +57,22 @@ CVertexBuffer::VBChunk* CVertexBufferManager::Allocate(size_t vertexSize, size_t
|
|||
|
||||
// TODO, RC - run some sanity checks on allocation request
|
||||
|
||||
typedef std::list<CVertexBuffer*>::iterator Iter;
|
||||
|
||||
#if DUMP_VB_STATS
|
||||
debug_printf(L"\n============================\n# allocate vsize=%d nverts=%d\n\n", vertexSize, numVertices);
|
||||
for (Iter iter = m_Buffers.begin(); iter != m_Buffers.end(); ++iter) {
|
||||
CVertexBuffer* buffer = *iter;
|
||||
if (buffer->CompatibleVertexType(vertexSize, usage, target))
|
||||
{
|
||||
debug_printf(L"%p\n", buffer);
|
||||
buffer->DumpStatus();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// iterate through all existing buffers testing for one that'll
|
||||
// satisfy the allocation
|
||||
typedef std::list<CVertexBuffer*>::iterator Iter;
|
||||
for (Iter iter = m_Buffers.begin(); iter != m_Buffers.end(); ++iter) {
|
||||
CVertexBuffer* buffer = *iter;
|
||||
result = buffer->Allocate(vertexSize, numVertices, usage, target);
|
||||
|
|
@ -89,6 +98,9 @@ CVertexBuffer::VBChunk* CVertexBufferManager::Allocate(size_t vertexSize, size_t
|
|||
void CVertexBufferManager::Release(CVertexBuffer::VBChunk* chunk)
|
||||
{
|
||||
ENSURE(chunk);
|
||||
#if DUMP_VB_STATS
|
||||
debug_printf(L"\n============================\n# release %p nverts=%d\n\n", chunk, chunk->m_Count);
|
||||
#endif
|
||||
chunk->m_Owner->Release(chunk);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,9 +30,6 @@
|
|||
class CVertexBufferManager
|
||||
{
|
||||
public:
|
||||
CVertexBufferManager() {}
|
||||
~CVertexBufferManager();
|
||||
|
||||
// Explicit shutdown of the vertex buffer subsystem
|
||||
void Shutdown();
|
||||
|
||||
|
|
|
|||
|
|
@ -20,15 +20,22 @@
|
|||
#include "simulation2/system/Component.h"
|
||||
#include "ICmpTerritoryManager.h"
|
||||
|
||||
#include "graphics/Overlay.h"
|
||||
#include "graphics/Terrain.h"
|
||||
#include "graphics/TextureManager.h"
|
||||
#include "maths/MathUtil.h"
|
||||
#include "maths/Vector2D.h"
|
||||
#include "ps/Overlay.h"
|
||||
#include "renderer/Renderer.h"
|
||||
#include "renderer/Scene.h"
|
||||
#include "renderer/TerrainOverlay.h"
|
||||
#include "simulation2/MessageTypes.h"
|
||||
#include "simulation2/components/ICmpObstruction.h"
|
||||
#include "simulation2/components/ICmpObstructionManager.h"
|
||||
#include "simulation2/components/ICmpOwnership.h"
|
||||
#include "simulation2/components/ICmpPathfinder.h"
|
||||
#include "simulation2/components/ICmpPlayer.h"
|
||||
#include "simulation2/components/ICmpPlayerManager.h"
|
||||
#include "simulation2/components/ICmpPosition.h"
|
||||
#include "simulation2/components/ICmpSettlement.h"
|
||||
#include "simulation2/components/ICmpTerrain.h"
|
||||
|
|
@ -36,6 +43,7 @@
|
|||
#include "simulation2/helpers/Geometry.h"
|
||||
#include "simulation2/helpers/Grid.h"
|
||||
#include "simulation2/helpers/PriorityQueue.h"
|
||||
#include "simulation2/helpers/Render.h"
|
||||
|
||||
class CCmpTerritoryManager;
|
||||
|
||||
|
|
@ -58,6 +66,7 @@ public:
|
|||
componentManager.SubscribeGloballyToMessageType(MT_OwnershipChanged);
|
||||
componentManager.SubscribeGloballyToMessageType(MT_PositionChanged);
|
||||
componentManager.SubscribeToMessageType(MT_TerrainChanged);
|
||||
componentManager.SubscribeToMessageType(MT_RenderSubmit);
|
||||
}
|
||||
|
||||
DEFAULT_COMPONENT_ALLOCATOR(TerritoryManager)
|
||||
|
|
@ -67,16 +76,30 @@ public:
|
|||
return "<a:component type='system'/><empty/>";
|
||||
}
|
||||
|
||||
u8 m_ImpassableCost;
|
||||
float m_BorderThickness;
|
||||
float m_BorderSeparation;
|
||||
|
||||
Grid<u8>* m_Territories;
|
||||
TerritoryOverlay* m_DebugOverlay;
|
||||
std::vector<SOverlayTexturedLine> m_BoundaryLines;
|
||||
bool m_BoundaryLinesDirty;
|
||||
|
||||
virtual void Init(const CParamNode& UNUSED(paramNode))
|
||||
{
|
||||
m_Territories = NULL;
|
||||
m_DebugOverlay = NULL;
|
||||
// m_DebugOverlay = new TerritoryOverlay(*this);
|
||||
m_BoundaryLinesDirty = true;
|
||||
|
||||
m_DirtyID = 1;
|
||||
|
||||
CParamNode externalParamNode;
|
||||
CParamNode::LoadXML(externalParamNode, L"simulation/data/territorymanager.xml");
|
||||
|
||||
m_ImpassableCost = externalParamNode.GetChild("TerritoryManager").GetChild("ImpassableCost").ToInt();
|
||||
m_BorderThickness = externalParamNode.GetChild("TerritoryManager").GetChild("BorderThickness").ToFixed().ToFloat();
|
||||
m_BorderSeparation = externalParamNode.GetChild("TerritoryManager").GetChild("BorderSeparation").ToFixed().ToFloat();
|
||||
}
|
||||
|
||||
virtual void Deinit()
|
||||
|
|
@ -116,6 +139,12 @@ public:
|
|||
MakeDirty();
|
||||
break;
|
||||
}
|
||||
case MT_RenderSubmit:
|
||||
{
|
||||
const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg);
|
||||
RenderSubmit(msgData.collector);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -148,6 +177,7 @@ public:
|
|||
{
|
||||
SAFE_DELETE(m_Territories);
|
||||
++m_DirtyID;
|
||||
m_BoundaryLinesDirty = true;
|
||||
}
|
||||
|
||||
virtual bool NeedUpdate(size_t* dirtyID)
|
||||
|
|
@ -169,6 +199,18 @@ public:
|
|||
* or 1+c if the influence have cost c (assumed between 0 and 254).
|
||||
*/
|
||||
void RasteriseInfluences(CComponentManager::InterfaceList& infls, Grid<u8>& grid);
|
||||
|
||||
struct TerritoryBoundary
|
||||
{
|
||||
player_id_t owner;
|
||||
std::vector<CVector2D> points;
|
||||
};
|
||||
|
||||
std::vector<TerritoryBoundary> ComputeBoundaries();
|
||||
|
||||
void UpdateBoundaryLines();
|
||||
|
||||
void RenderSubmit(SceneCollector& collector);
|
||||
};
|
||||
|
||||
REGISTER_COMPONENT_TYPE(TerritoryManager)
|
||||
|
|
@ -252,15 +294,19 @@ void CCmpTerritoryManager::CalculateTerritories()
|
|||
Grid<u8> influenceGrid(tilesW, tilesH);
|
||||
|
||||
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSimContext(), SYSTEM_ENTITY);
|
||||
ICmpPathfinder::pass_class_t passClass = cmpPathfinder->GetPassabilityClass("default");
|
||||
ICmpPathfinder::pass_class_t passClassUnrestricted = cmpPathfinder->GetPassabilityClass("unrestricted");
|
||||
ICmpPathfinder::pass_class_t passClassDefault = cmpPathfinder->GetPassabilityClass("default");
|
||||
const Grid<u16>& passGrid = cmpPathfinder->GetPassabilityGrid();
|
||||
for (u32 j = 0; j < tilesH; ++j)
|
||||
{
|
||||
for (u32 i = 0; i < tilesW; ++i)
|
||||
{
|
||||
u8 g = passGrid.get(i, j);
|
||||
u8 cost;
|
||||
if (passGrid.get(i, j) & passClass)
|
||||
cost = 4; // TODO: should come from some XML file
|
||||
if (g & passClassUnrestricted)
|
||||
cost = 255; // off the world; use maximum cost
|
||||
else if (g & passClassDefault)
|
||||
cost = m_ImpassableCost;
|
||||
else
|
||||
cost = 1;
|
||||
influenceGrid.set(i, j, cost);
|
||||
|
|
@ -425,6 +471,219 @@ void CCmpTerritoryManager::RasteriseInfluences(CComponentManager::InterfaceList&
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<CCmpTerritoryManager::TerritoryBoundary> CCmpTerritoryManager::ComputeBoundaries()
|
||||
{
|
||||
PROFILE("ComputeBoundaries");
|
||||
|
||||
std::vector<CCmpTerritoryManager::TerritoryBoundary> boundaries;
|
||||
|
||||
CalculateTerritories();
|
||||
|
||||
// Copy the territories grid so we can mess with it
|
||||
Grid<u8> grid (*m_Territories);
|
||||
|
||||
// Some constants for the border walk
|
||||
CVector2D edgeOffsets[] = {
|
||||
CVector2D(0.5f, 0.0f),
|
||||
CVector2D(1.0f, 0.5f),
|
||||
CVector2D(0.5f, 1.0f),
|
||||
CVector2D(0.0f, 0.5f)
|
||||
};
|
||||
|
||||
// Try to find an assigned tile
|
||||
for (int j = 0; j < grid.m_H; ++j)
|
||||
{
|
||||
for (int i = 0; i < grid.m_W; ++i)
|
||||
{
|
||||
u8 owner = grid.get(i, j);
|
||||
if (owner)
|
||||
{
|
||||
// Found the first tile (which must be the lowest j value of any non-zero tile);
|
||||
// start at the bottom edge of it and chase anticlockwise around the border until
|
||||
// we reach the starting point again
|
||||
|
||||
boundaries.push_back(TerritoryBoundary());
|
||||
boundaries.back().owner = owner;
|
||||
std::vector<CVector2D>& points = boundaries.back().points;
|
||||
|
||||
int dir = 0; // 0 == bottom edge of tile, 1 == right, 2 == top, 3 == left
|
||||
|
||||
int cdir = dir;
|
||||
int ci = i, cj = j;
|
||||
|
||||
while (true)
|
||||
{
|
||||
points.push_back((CVector2D(ci, cj) + edgeOffsets[cdir]) * CELL_SIZE);
|
||||
|
||||
// Given that we're on an edge on a continuous boundary and aiming anticlockwise,
|
||||
// we can either carry on straight or turn left or turn right, so examine each
|
||||
// of the three possible cases (depending on initial direction):
|
||||
switch (cdir)
|
||||
{
|
||||
case 0:
|
||||
if (ci < grid.m_W-1 && cj > 0 && grid.get(ci+1, cj-1) == owner)
|
||||
{
|
||||
++ci;
|
||||
--cj;
|
||||
cdir = 3;
|
||||
}
|
||||
else if (ci < grid.m_W-1 && grid.get(ci+1, cj) == owner)
|
||||
++ci;
|
||||
else
|
||||
cdir = 1;
|
||||
break;
|
||||
case 1:
|
||||
if (ci < grid.m_W-1 && cj < grid.m_H-1 && grid.get(ci+1, cj+1) == owner)
|
||||
{
|
||||
++ci;
|
||||
++cj;
|
||||
cdir = 0;
|
||||
}
|
||||
else if (cj < grid.m_H-1 && grid.get(ci, cj+1) == owner)
|
||||
++cj;
|
||||
else
|
||||
cdir = 2;
|
||||
break;
|
||||
case 2:
|
||||
if (ci > 0 && cj < grid.m_H-1 && grid.get(ci-1, cj+1) == owner)
|
||||
{
|
||||
--ci;
|
||||
++cj;
|
||||
cdir = 1;
|
||||
}
|
||||
else if (ci > 0 && grid.get(ci-1, cj) == owner)
|
||||
--ci;
|
||||
else
|
||||
cdir = 3;
|
||||
break;
|
||||
case 3:
|
||||
if (ci > 0 && cj > 0 && grid.get(ci-1, cj-1) == owner)
|
||||
{
|
||||
--ci;
|
||||
--cj;
|
||||
cdir = 2;
|
||||
}
|
||||
else if (cj > 0 && grid.get(ci, cj-1) == owner)
|
||||
--cj;
|
||||
else
|
||||
cdir = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Stop when we've reached the starting point again
|
||||
if (ci == i && cj == j && cdir == dir)
|
||||
break;
|
||||
}
|
||||
|
||||
// Zero out this whole territory with a simple flood fill, so we don't
|
||||
// process it a second time
|
||||
std::vector<std::pair<int, int> > tileStack;
|
||||
|
||||
#define ZERO_AND_PUSH(i, j) STMT(grid.set(i, j, 0); tileStack.push_back(std::make_pair(i, j)); )
|
||||
|
||||
ZERO_AND_PUSH(i, j);
|
||||
while (!tileStack.empty())
|
||||
{
|
||||
int ti = tileStack.back().first;
|
||||
int tj = tileStack.back().second;
|
||||
tileStack.pop_back();
|
||||
|
||||
if (ti > 0 && grid.get(ti-1, tj) == owner)
|
||||
ZERO_AND_PUSH(ti-1, tj);
|
||||
if (ti < grid.m_W-1 && grid.get(ti+1, tj) == owner)
|
||||
ZERO_AND_PUSH(ti+1, tj);
|
||||
if (tj > 0 && grid.get(ti, tj-1) == owner)
|
||||
ZERO_AND_PUSH(ti, tj-1);
|
||||
if (tj < grid.m_H-1 && grid.get(ti, tj+1) == owner)
|
||||
ZERO_AND_PUSH(ti, tj+1);
|
||||
|
||||
if (ti > 0 && tj > 0 && grid.get(ti-1, tj-1) == owner)
|
||||
ZERO_AND_PUSH(ti-1, tj-1);
|
||||
if (ti > 0 && tj < grid.m_H-1 && grid.get(ti-1, tj+1) == owner)
|
||||
ZERO_AND_PUSH(ti-1, tj+1);
|
||||
if (ti < grid.m_W-1 && tj > 0 && grid.get(ti+1, tj-1) == owner)
|
||||
ZERO_AND_PUSH(ti+1, tj-1);
|
||||
if (ti < grid.m_W-1 && tj < grid.m_H-1 && grid.get(ti+1, tj+1) == owner)
|
||||
ZERO_AND_PUSH(ti+1, tj+1);
|
||||
}
|
||||
|
||||
#undef ZERO_AND_PUSH
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return boundaries;
|
||||
}
|
||||
|
||||
void CCmpTerritoryManager::UpdateBoundaryLines()
|
||||
{
|
||||
PROFILE("update boundary lines");
|
||||
|
||||
m_BoundaryLines.clear();
|
||||
|
||||
std::vector<CCmpTerritoryManager::TerritoryBoundary> boundaries = ComputeBoundaries();
|
||||
|
||||
CTextureProperties texturePropsBase("art/textures/misc/territory_border.png");
|
||||
texturePropsBase.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE);
|
||||
texturePropsBase.SetMaxAnisotropy(2.f);
|
||||
CTexturePtr textureBase = g_Renderer.GetTextureManager().CreateTexture(texturePropsBase);
|
||||
|
||||
CTextureProperties texturePropsMask("art/textures/misc/territory_border_mask.png");
|
||||
texturePropsMask.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE);
|
||||
texturePropsMask.SetMaxAnisotropy(2.f);
|
||||
CTexturePtr textureMask = g_Renderer.GetTextureManager().CreateTexture(texturePropsMask);
|
||||
|
||||
CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY);
|
||||
if (cmpTerrain.null())
|
||||
return;
|
||||
CTerrain* terrain = cmpTerrain->GetCTerrain();
|
||||
|
||||
CmpPtr<ICmpPlayerManager> cmpPlayerManager(GetSimContext(), SYSTEM_ENTITY);
|
||||
if (cmpPlayerManager.null())
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < boundaries.size(); ++i)
|
||||
{
|
||||
if (boundaries[i].points.empty())
|
||||
continue;
|
||||
|
||||
CColor color(1, 0, 1, 1);
|
||||
CmpPtr<ICmpPlayer> cmpPlayer(GetSimContext(), cmpPlayerManager->GetPlayerByID(boundaries[i].owner));
|
||||
if (!cmpPlayer.null())
|
||||
color = cmpPlayer->GetColour();
|
||||
|
||||
m_BoundaryLines.push_back(SOverlayTexturedLine());
|
||||
m_BoundaryLines.back().m_Terrain = terrain;
|
||||
m_BoundaryLines.back().m_TextureBase = textureBase;
|
||||
m_BoundaryLines.back().m_TextureMask = textureMask;
|
||||
m_BoundaryLines.back().m_Color = color;
|
||||
m_BoundaryLines.back().m_Thickness = m_BorderThickness;
|
||||
|
||||
SimRender::SmoothPointsAverage(boundaries[i].points, true);
|
||||
|
||||
SimRender::InterpolatePointsRNS(boundaries[i].points, true, m_BorderSeparation);
|
||||
|
||||
std::vector<float>& points = m_BoundaryLines.back().m_Coords;
|
||||
for (size_t j = 0; j < boundaries[i].points.size(); ++j)
|
||||
{
|
||||
points.push_back(boundaries[i].points[j].X);
|
||||
points.push_back(boundaries[i].points[j].Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCmpTerritoryManager::RenderSubmit(SceneCollector& collector)
|
||||
{
|
||||
if (m_BoundaryLinesDirty)
|
||||
{
|
||||
UpdateBoundaryLines();
|
||||
m_BoundaryLinesDirty = false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_BoundaryLines.size(); ++i)
|
||||
collector.Submit(&m_BoundaryLines[i]);
|
||||
}
|
||||
|
||||
|
||||
void TerritoryOverlay::StartRender()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,12 +25,14 @@
|
|||
#include "graphics/Overlay.h"
|
||||
#include "graphics/Terrain.h"
|
||||
#include "maths/MathUtil.h"
|
||||
#include "maths/Vector2D.h"
|
||||
#include "ps/Profile.h"
|
||||
|
||||
static const float RENDER_HEIGHT_DELTA = 0.25f; // distance above terrain
|
||||
|
||||
void SimRender::ConstructLineOnGround(const CSimContext& context, std::vector<float> xz,
|
||||
SOverlayLine& overlay, bool floating)
|
||||
void SimRender::ConstructLineOnGround(const CSimContext& context, const std::vector<float>& xz,
|
||||
SOverlayLine& overlay, bool floating, float heightOffset)
|
||||
{
|
||||
PROFILE("ConstructLineOnGround");
|
||||
|
||||
overlay.m_Coords.clear();
|
||||
|
||||
CmpPtr<ICmpTerrain> cmpTerrain(context, SYSTEM_ENTITY);
|
||||
|
|
@ -54,7 +56,7 @@ void SimRender::ConstructLineOnGround(const CSimContext& context, std::vector<fl
|
|||
{
|
||||
float px = xz[i];
|
||||
float pz = xz[i+1];
|
||||
float py = std::max(water, cmpTerrain->GetExactGroundLevel(px, pz)) + RENDER_HEIGHT_DELTA;
|
||||
float py = std::max(water, cmpTerrain->GetExactGroundLevel(px, pz)) + heightOffset;
|
||||
overlay.m_Coords.push_back(px);
|
||||
overlay.m_Coords.push_back(py);
|
||||
overlay.m_Coords.push_back(pz);
|
||||
|
|
@ -62,7 +64,7 @@ void SimRender::ConstructLineOnGround(const CSimContext& context, std::vector<fl
|
|||
}
|
||||
|
||||
void SimRender::ConstructCircleOnGround(const CSimContext& context, float x, float z, float radius,
|
||||
SOverlayLine& overlay, bool floating)
|
||||
SOverlayLine& overlay, bool floating, float heightOffset)
|
||||
{
|
||||
overlay.m_Coords.clear();
|
||||
|
||||
|
|
@ -88,7 +90,7 @@ void SimRender::ConstructCircleOnGround(const CSimContext& context, float x, flo
|
|||
float a = i * 2 * (float)M_PI / numPoints;
|
||||
float px = x + radius * sin(a);
|
||||
float pz = z + radius * cos(a);
|
||||
float py = std::max(water, cmpTerrain->GetExactGroundLevel(px, pz)) + RENDER_HEIGHT_DELTA;
|
||||
float py = std::max(water, cmpTerrain->GetExactGroundLevel(px, pz)) + heightOffset;
|
||||
overlay.m_Coords.push_back(px);
|
||||
overlay.m_Coords.push_back(py);
|
||||
overlay.m_Coords.push_back(pz);
|
||||
|
|
@ -113,7 +115,7 @@ static void SplitLine(std::vector<std::pair<float, float> >& coords, float x1, f
|
|||
}
|
||||
|
||||
void SimRender::ConstructSquareOnGround(const CSimContext& context, float x, float z, float w, float h, float a,
|
||||
SOverlayLine& overlay, bool floating)
|
||||
SOverlayLine& overlay, bool floating, float heightOffset)
|
||||
{
|
||||
overlay.m_Coords.clear();
|
||||
|
||||
|
|
@ -150,9 +152,104 @@ void SimRender::ConstructSquareOnGround(const CSimContext& context, float x, flo
|
|||
{
|
||||
float px = coords[i].first;
|
||||
float pz = coords[i].second;
|
||||
float py = std::max(water, cmpTerrain->GetExactGroundLevel(px, pz)) + RENDER_HEIGHT_DELTA;
|
||||
float py = std::max(water, cmpTerrain->GetExactGroundLevel(px, pz)) + heightOffset;
|
||||
overlay.m_Coords.push_back(px);
|
||||
overlay.m_Coords.push_back(py);
|
||||
overlay.m_Coords.push_back(pz);
|
||||
}
|
||||
}
|
||||
|
||||
void SimRender::SmoothPointsAverage(std::vector<CVector2D>& points, bool closed)
|
||||
{
|
||||
PROFILE("SmoothPointsAverage");
|
||||
|
||||
size_t n = points.size();
|
||||
if (n < 2)
|
||||
return; // avoid out-of-bounds array accesses, and leave the points unchanged
|
||||
|
||||
std::vector<CVector2D> newPoints;
|
||||
newPoints.resize(points.size());
|
||||
|
||||
// Handle the end points appropriately
|
||||
if (closed)
|
||||
{
|
||||
newPoints[0] = (points[n-1] + points[0] + points[1]) / 3.f;
|
||||
newPoints[n-1] = (points[n-2] + points[n-1] + points[0]) / 3.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
newPoints[0] = points[0];
|
||||
newPoints[n-1] = points[n-1];
|
||||
}
|
||||
|
||||
// Average all the intermediate points
|
||||
for (size_t i = 1; i < n-1; ++i)
|
||||
newPoints[i] = (points[i-1] + points[i] + points[i+1]) / 3.f;
|
||||
|
||||
points.swap(newPoints);
|
||||
}
|
||||
|
||||
static CVector2D EvaluateSpline(float t, CVector2D a0, CVector2D a1, CVector2D a2, CVector2D a3, float offset)
|
||||
{
|
||||
// Compute position on spline
|
||||
CVector2D p = a0*(t*t*t) + a1*(t*t) + a2*t + a3;
|
||||
|
||||
// Compute unit-vector direction of spline
|
||||
CVector2D dp = (a0*(3*t*t) + a1*(2*t) + a2).Normalized();
|
||||
|
||||
// Offset position perpendicularly
|
||||
return p + CVector2D(dp.Y*-offset, dp.X*offset);
|
||||
}
|
||||
|
||||
void SimRender::InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed, float offset)
|
||||
{
|
||||
PROFILE("InterpolatePointsRNS");
|
||||
|
||||
std::vector<CVector2D> newPoints;
|
||||
|
||||
// (This does some redundant computations for adjacent vertices,
|
||||
// but it's fairly fast (<1ms typically) so we don't worry about it yet)
|
||||
|
||||
// TODO: Instead of doing a fixed number of line segments between each
|
||||
// control point, it should probably be somewhat adaptive to get a nicer
|
||||
// curve with fewer points
|
||||
|
||||
size_t n = points.size();
|
||||
if (n < 1)
|
||||
return; // can't do anything unless we have two points
|
||||
|
||||
size_t imax = closed ? n : n-1; // TODO: we probably need to do a bit more to handle non-closed paths
|
||||
|
||||
newPoints.reserve(imax*4);
|
||||
|
||||
for (size_t i = 0; i < imax; ++i)
|
||||
{
|
||||
// Get the relevant points for this spline segment
|
||||
CVector2D p0 = points[(i-1+n)%n];
|
||||
CVector2D p1 = points[i];
|
||||
CVector2D p2 = points[(i+1)%n];
|
||||
CVector2D p3 = points[(i+2)%n];
|
||||
|
||||
// Do the RNS computation (based on GPG4 "Nonuniform Splines")
|
||||
float l1 = (p2 - p1).Length(); // length of spline segment (i)..(i+1)
|
||||
CVector2D s0 = (p1 - p0).Normalized(); // unit vector of spline segment (i-1)..(i)
|
||||
CVector2D s1 = (p2 - p1).Normalized(); // unit vector of spline segment (i)..(i+1)
|
||||
CVector2D s2 = (p3 - p2).Normalized(); // unit vector of spline segment (i+1)..(i+2)
|
||||
CVector2D v1 = (s0 + s1).Normalized() * l1; // spline velocity at i
|
||||
CVector2D v2 = (s1 + s2).Normalized() * l1; // spline velocity at i+1
|
||||
|
||||
// Compute standard cubic spline parameters
|
||||
CVector2D a0 = p1*2 + p2*-2 + v1 + v2;
|
||||
CVector2D a1 = p1*-3 + p2*3 + v1*-2 + v2*-1;
|
||||
CVector2D a2 = v1;
|
||||
CVector2D a3 = p1;
|
||||
|
||||
// Interpolate at various points
|
||||
newPoints.push_back(EvaluateSpline(0.f, a0, a1, a2, a3, offset));
|
||||
newPoints.push_back(EvaluateSpline(1.f/4.f, a0, a1, a2, a3, offset));
|
||||
newPoints.push_back(EvaluateSpline(2.f/4.f, a0, a1, a2, a3, offset));
|
||||
newPoints.push_back(EvaluateSpline(3.f/4.f, a0, a1, a2, a3, offset));
|
||||
}
|
||||
|
||||
points.swap(newPoints);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
*/
|
||||
|
||||
class CSimContext;
|
||||
class CVector2D;
|
||||
struct SOverlayLine;
|
||||
|
||||
namespace SimRender
|
||||
|
|
@ -33,18 +34,42 @@ namespace SimRender
|
|||
* Updates @p overlay so that it represents the given line (a list of x, z coordinate pairs),
|
||||
* flattened on the terrain (or on the water if @p floating).
|
||||
*/
|
||||
void ConstructLineOnGround(const CSimContext& context, std::vector<float> xz, SOverlayLine& overlay, bool floating);
|
||||
void ConstructLineOnGround(const CSimContext& context, const std::vector<float>& xz,
|
||||
SOverlayLine& overlay,
|
||||
bool floating, float heightOffset = 0.25f);
|
||||
|
||||
/**
|
||||
* Updates @p overlay so that it represents the given circle, flattened on the terrain.
|
||||
*/
|
||||
void ConstructCircleOnGround(const CSimContext& context, float x, float z, float radius, SOverlayLine& overlay, bool floating);
|
||||
void ConstructCircleOnGround(const CSimContext& context, float x, float z, float radius,
|
||||
SOverlayLine& overlay,
|
||||
bool floating, float heightOffset = 0.25f);
|
||||
|
||||
/**
|
||||
* Updates @p overlay so that it represents the given square, flattened on the terrain.
|
||||
* @p x and @p z are position of center, @p w and @p h are size of rectangle, @p a is clockwise angle.
|
||||
*/
|
||||
void ConstructSquareOnGround(const CSimContext& context, float x, float z, float w, float h, float a, SOverlayLine& overlay, bool floating);
|
||||
void ConstructSquareOnGround(const CSimContext& context, float x, float z, float w, float h, float a,
|
||||
SOverlayLine& overlay,
|
||||
bool floating, float heightOffset = 0.25f);
|
||||
|
||||
/**
|
||||
* Updates @p points so each point is averaged with its neighbours, resulting in
|
||||
* a somewhat smoother curve, assuming the points are roughly equally spaced.
|
||||
* If @p closed then the points are treated as a closed path (the last is connected
|
||||
* to the first).
|
||||
*/
|
||||
void SmoothPointsAverage(std::vector<CVector2D>& points, bool closed);
|
||||
|
||||
/**
|
||||
* Updates @p points to include intermediate points interpolating between the original
|
||||
* control points, using a rounded nonuniform spline.
|
||||
* The points are also shifted by @p offset in a direction 90 degrees clockwise from
|
||||
* the direction of the curve.
|
||||
* If @p closed then the points are treated as a closed path (the last is connected
|
||||
* to the first).
|
||||
*/
|
||||
void InterpolatePointsRNS(std::vector<CVector2D>& points, bool closed, float offset);
|
||||
|
||||
} // namespace
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue