summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/tilebuf.cc
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref/source/tilebuf.cc')
-rw-r--r--crawl-ref/source/tilebuf.cc378
1 files changed, 340 insertions, 38 deletions
diff --git a/crawl-ref/source/tilebuf.cc b/crawl-ref/source/tilebuf.cc
index 1c9cbf2763..edaa3a4596 100644
--- a/crawl-ref/source/tilebuf.cc
+++ b/crawl-ref/source/tilebuf.cc
@@ -11,12 +11,87 @@
#include "tilebuf.h"
#include "tilefont.h"
-#include "tilesdl.h"
#include <SDL.h>
#include <SDL_opengl.h>
/////////////////////////////////////////////////////////////////////////////
+// GLState
+
+// Note: these defaults should match the OpenGL defaults
+GLState::GLState() :
+ array_vertex(false),
+ array_texcoord(false),
+ array_colour(false),
+ blend(false),
+ texture(false),
+ depthtest(false),
+ alphatest(false),
+ alpharef(0)
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// GLStateManager
+
+void GLStateManager::init()
+{
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glClearColor(0.0, 0.0, 0.0, 1.0f);
+ glDepthFunc(GL_LEQUAL);
+}
+
+void GLStateManager::set(const GLState& state)
+{
+ if (state.array_vertex)
+ glEnableClientState(GL_VERTEX_ARRAY);
+ else
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ if (state.array_texcoord)
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ else
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ if (state.array_colour)
+ {
+ glEnableClientState(GL_COLOR_ARRAY);
+ }
+ else
+ {
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ // [enne] This should *not* be necessary, but the Linux OpenGL
+ // driver that I'm using sets this to the last colour of the
+ // colour array. So, we need to unset it here.
+ glColor3f(1.0f, 1.0f, 1.0f);
+ }
+
+ if (state.texture)
+ glEnable(GL_TEXTURE_2D);
+ else
+ glDisable(GL_TEXTURE_2D);
+
+ if (state.blend)
+ glEnable(GL_BLEND);
+ else
+ glDisable(GL_BLEND);
+
+ if (state.depthtest)
+ glEnable(GL_DEPTH_TEST);
+ else
+ glDisable(GL_DEPTH_TEST);
+
+ if (state.alphatest)
+ {
+ glEnable(GL_ALPHA_TEST);
+ glAlphaFunc(GL_NOTEQUAL, state.alpharef);
+ }
+ else
+ glDisable(GL_ALPHA_TEST);
+}
+
+/////////////////////////////////////////////////////////////////////////////
// VColour
VColour VColour::white(255, 255, 255, 255);
@@ -46,6 +121,15 @@ static bool _valid(int num_verts, int prim)
#endif
template<>
+void VertBuffer<PTVert>::init_state()
+{
+ m_state.array_vertex = true;
+ m_state.array_texcoord = true;
+ m_state.blend = true;
+ m_state.texture = true;
+}
+
+template<>
void VertBuffer<PTVert>::draw() const
{
if (size() == 0)
@@ -54,12 +138,7 @@ void VertBuffer<PTVert>::draw() const
ASSERT(_valid(size(), m_prim));
ASSERT(m_tex);
- GLState state;
- state.array_vertex = true;
- state.array_texcoord = true;
- state.blend = true;
- state.texture = true;
- GLStateManager::set(state);
+ GLStateManager::set(m_state);
m_tex->bind();
glVertexPointer(2, GL_FLOAT, sizeof(Vert), &(*this)[0].pos_x);
@@ -68,6 +147,15 @@ void VertBuffer<PTVert>::draw() const
}
template<>
+void VertBuffer<PCVert>::init_state()
+{
+ m_state.array_vertex = true;
+ m_state.array_colour = true;
+ m_state.blend = true;
+ m_state.texture = false;
+}
+
+template<>
void VertBuffer<PCVert>::draw() const
{
if (size() == 0)
@@ -76,12 +164,7 @@ void VertBuffer<PCVert>::draw() const
ASSERT(_valid(size(), m_prim));
ASSERT(!m_tex);
- GLState state;
- state.array_vertex = true;
- state.array_colour = true;
- state.blend = true;
- state.texture = false;
- GLStateManager::set(state);
+ GLStateManager::set(m_state);
glVertexPointer(2, GL_FLOAT, sizeof(Vert), &(*this)[0].pos_x);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vert), &(*this)[0].col);
@@ -89,6 +172,16 @@ void VertBuffer<PCVert>::draw() const
}
template<>
+void VertBuffer<PTCVert>::init_state()
+{
+ m_state.array_vertex = true;
+ m_state.array_texcoord = true;
+ m_state.array_colour = true;
+ m_state.blend = true;
+ m_state.texture = true;
+}
+
+template<>
void VertBuffer<PTCVert>::draw() const
{
if (size() == 0)
@@ -97,13 +190,7 @@ void VertBuffer<PTCVert>::draw() const
ASSERT(_valid(size(), m_prim));
ASSERT(m_tex);
- GLState state;
- state.array_vertex = true;
- state.array_texcoord = true;
- state.array_colour = true;
- state.blend = true;
- state.texture = true;
- GLStateManager::set(state);
+ GLStateManager::set(m_state);
m_tex->bind();
glVertexPointer(2, GL_FLOAT, sizeof(Vert), &(*this)[0].pos_x);
@@ -112,6 +199,37 @@ void VertBuffer<PTCVert>::draw() const
glDrawArrays(m_prim, 0, size());
}
+template<>
+void VertBuffer<P3TCVert>::init_state()
+{
+ m_state.array_vertex = true;
+ m_state.array_texcoord = true;
+ m_state.array_colour = true;
+ m_state.blend = true;
+ m_state.texture = true;
+ m_state.depthtest = true;
+ m_state.alphatest = true;
+ m_state.alpharef = 0;
+}
+
+template<>
+void VertBuffer<P3TCVert>::draw() const
+{
+ if (size() == 0)
+ return;
+
+ ASSERT(_valid(size(), m_prim));
+ ASSERT(m_tex);
+
+ GLStateManager::set(m_state);
+
+ m_tex->bind();
+ glVertexPointer(3, GL_FLOAT, sizeof(Vert), &(*this)[0].pos_x);
+ glTexCoordPointer(2, GL_FLOAT, sizeof(Vert), &(*this)[0].tex_x);
+ glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vert), &(*this)[0].col);
+ glDrawArrays(m_prim, 0, size());
+}
+
/////////////////////////////////////////////////////////////////////////////
// FontBuffer
@@ -143,23 +261,17 @@ TileBuffer::TileBuffer(const TilesTexture *t) : VertBuffer<PTVert>(t, GL_QUADS)
void TileBuffer::add_unscaled(int idx, float x, float y, int ymax)
{
- const tile_info &inf = ((TilesTexture*)m_tex)->get_info(idx);
-
- float pos_sx = x + inf.offset_x;
- float pos_sy = y + inf.offset_y;
- float pos_ex = pos_sx + (inf.ex - inf.sx);
- int ey = inf.ey;
- if (ymax > 0)
- ey = std::min(inf.sy + ymax - inf.offset_y, ey);
- float pos_ey = pos_sy + (ey - inf.sy);
-
- float fwidth = m_tex->width();
- float fheight = m_tex->height();
+ float pos_sx = x;
+ float pos_sy = y;
+ float pos_ex, pos_ey, tex_sx, tex_sy, tex_ex, tex_ey;
+ TilesTexture *tex = (TilesTexture *)m_tex;
+ bool drawn = tex->get_coords(idx, 0, 0,
+ pos_sx, pos_sy, pos_ex, pos_ey,
+ tex_sx, tex_sy, tex_ex, tex_ey,
+ true, -1, ymax, 1.0f, 1.0f);
- float tex_sx = inf.sx / fwidth;
- float tex_sy = inf.sy / fheight;
- float tex_ex = inf.ex / fwidth;
- float tex_ey = inf.ey / fheight;
+ if (!drawn)
+ return;
size_t last = std::vector<Vert>::size();
std::vector<Vert>::resize(last + 4);
@@ -202,8 +314,13 @@ void TileBuffer::add(int idx, int x, int y, int ox, int oy, bool centre, int yma
float pos_sy = y;
float pos_ex, pos_ey, tex_sx, tex_sy, tex_ex, tex_ey;
TilesTexture *tex = (TilesTexture *)m_tex;
- tex->get_coords(idx, ox, oy, pos_sx, pos_sy, pos_ex, pos_ey,
- tex_sx, tex_sy, tex_ex, tex_ey, centre, ymax);
+ bool drawn = tex->get_coords(idx, ox, oy,
+ pos_sx, pos_sy, pos_ex, pos_ey,
+ tex_sx, tex_sy, tex_ex, tex_ey,
+ centre, -1, ymax, TILE_X, TILE_Y);
+
+ if (!drawn)
+ return;
size_t last = std::vector<Vert>::size();
std::vector<Vert>::resize(last + 4);
@@ -248,6 +365,191 @@ void TileBuffer::set_tex(const TilesTexture *new_tex)
}
/////////////////////////////////////////////////////////////////////////////
+// ColouredTileBuffer
+
+ColouredTileBuffer::ColouredTileBuffer(const TilesTexture *t) :
+ VertBuffer<P3TCVert>(t, GL_QUADS)
+{
+}
+
+static unsigned char _get_alpha(float lerp, int alpha_top, int alpha_bottom)
+{
+ if (lerp < 0.0f)
+ lerp = 0.0f;
+ if (lerp > 1.0f)
+ lerp = 1.0f;
+
+ int ret = alpha_top * (1.0f - lerp) + alpha_bottom * lerp;
+
+ ret = std::min(std::max(0, ret), 255);
+ return (static_cast<unsigned char>(ret));
+}
+
+void ColouredTileBuffer::add(int idx, int x, int y, int z,
+ int ox, int oy, int ymin, int ymax,
+ int alpha_top, int alpha_bottom)
+{
+ float pos_sx = x;
+ float pos_sy = y;
+ float pos_ex, pos_ey, tex_sx, tex_sy, tex_ex, tex_ey;
+ TilesTexture *tex = (TilesTexture *)m_tex;
+ bool drawn = tex->get_coords(idx, ox, oy,
+ pos_sx, pos_sy, pos_ex, pos_ey,
+ tex_sx, tex_sy, tex_ex, tex_ey,
+ true, -1, ymax);
+
+ if (!drawn)
+ return;
+
+ float lerp_s = (pos_sy - y);
+ float lerp_e = (pos_ey - y);
+
+ // Need to calculate the alpha at the top and bottom. As the quad
+ // being drawn may not actually start on the grid boundary, we need
+ // to figure out what alpha value its vertices should use.
+ unsigned char alpha_s = _get_alpha(lerp_s, alpha_top, alpha_bottom);
+ unsigned char alpha_e = _get_alpha(lerp_e, alpha_top, alpha_bottom);
+
+ VColour col_sy(255, 255, 255, alpha_s);
+ VColour col_ey(255, 255, 255, alpha_e);
+
+ float pos_z = (float)z;
+
+ size_t last = std::vector<Vert>::size();
+ std::vector<Vert>::resize(last + 4);
+ {
+ Vert &v = (*this)[last];
+ v.pos_x = pos_sx;
+ v.pos_y = pos_sy;
+ v.pos_z = pos_z;
+ v.tex_x = tex_sx;
+ v.tex_y = tex_sy;
+ v.col = col_sy;
+ }
+
+ {
+ Vert &v = (*this)[last + 1];
+ v.pos_x = pos_sx;
+ v.pos_y = pos_ey;
+ v.pos_z = pos_z;
+ v.tex_x = tex_sx;
+ v.tex_y = tex_ey;
+ v.col = col_ey;
+ }
+
+ {
+ Vert &v = (*this)[last + 2];
+ v.pos_x = pos_ex;
+ v.pos_y = pos_ey;
+ v.pos_z = pos_z;
+ v.tex_x = tex_ex;
+ v.tex_y = tex_ey;
+ v.col = col_ey;
+ }
+
+ {
+ Vert &v = (*this)[last + 3];
+ v.pos_x = pos_ex;
+ v.pos_y = pos_sy;
+ v.pos_z = pos_z;
+ v.tex_x = tex_ex;
+ v.tex_y = tex_sy;
+ v.col = col_sy;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// SubmergedTileBuffer
+
+
+SubmergedTileBuffer::SubmergedTileBuffer(const TilesTexture *tex,
+ int mask_idx, int above_max,
+ int below_min) :
+ m_mask_idx(mask_idx),
+ m_above_max(above_max),
+ m_below_min(below_min),
+ m_max_z(-1000),
+ m_below_water(tex),
+ m_mask(tex),
+ m_above_water(tex)
+{
+ // Adjust the state of the mask buffer so that we can use it to mask out
+ // the bottom half of the tile when drawing the character a second time.
+ // See the draw() function for more details.
+ GLState &state = m_mask.state();
+ state.blend = true;
+ state.depthtest = true;
+ state.alphatest = true;
+ state.alpharef = 255;
+}
+
+void SubmergedTileBuffer::add(int idx, int x, int y, int z, bool submerged,
+ bool ghost, int ox, int oy, int ymax)
+{
+ // Keep track of the maximum z. We'll use this for drawing the mask
+ // later, to ensure it is closer than all other tiles drawn.
+ m_max_z = std::max(m_max_z, z);
+
+ // Arbitrary alpha values for the top and bottom.
+ int alpha_top = ghost ? 100 : 255;
+ int alpha_bottom = ghost ? 0 : 40;
+
+ if (submerged)
+ {
+ m_below_water.add(idx, x, y, z, ox, oy, m_below_min, ymax,
+ alpha_top, alpha_bottom);
+
+ m_mask.add(m_mask_idx, x, y, 0, 0, 0, -1, -1, 255, 255);
+ int above_max = (ymax == -1) ? m_above_max
+ : std::min(ymax, m_above_max);
+ m_above_water.add(idx, x, y, z, ox, oy, -1, above_max,
+ alpha_top, alpha_top);
+ }
+ else
+ {
+ // Unsubmerged tiles don't need a mask.
+ m_above_water.add(idx, x, y, z, ox, oy, -1, ymax, alpha_top, alpha_top);
+ }
+}
+
+void SubmergedTileBuffer::draw() const
+{
+ // First, draw anything below water. Alpha blending is turned on,
+ // so these tiles will blend with anything previously drawn.
+ m_below_water.draw();
+
+ // Second, draw all of the mask tiles. The mask is white (255,255,255,255)
+ // above water and transparent (0,0,0,0) below water.
+ //
+ // Alpha testing is turned on with a ref value of 255, so none of the
+ // white pixels will be drawn because they will culled by alpha testing.
+ // The transparent pixels below water will be "drawn", but because they
+ // are fully transparent, they will not affect any colours on screen.
+ // Instead, they will will set the z-buffer to a z-value (depth) that is
+ // greater than the closest depth in either the below or above buffers.
+ // Because all of the above water tiles are at lower z-values than this
+ // maximum, they will not draw over these transparent pixels, effectively
+ // masking them out. (My kingdom for a shader.)
+
+ glPushMatrix();
+ glTranslatef(0, 0, m_max_z + 1);
+ m_mask.draw();
+ glPopMatrix();
+
+ // Now, draw all the above water tiles. Some of these may draw over top
+ // of part of the below water tiles, but that's fine as the mask will
+ // take care of only drawing the correct parts.
+ m_above_water.draw();
+}
+
+void SubmergedTileBuffer::clear()
+{
+ m_below_water.clear();
+ m_mask.clear();
+ m_above_water.clear();
+}
+
+/////////////////////////////////////////////////////////////////////////////
// ShapeBuffer
// [enne] - GL_POINTS should probably be used here, but there's (apparently)