diff options
Diffstat (limited to 'crawl-ref/source/tilebuf.cc')
-rw-r--r-- | crawl-ref/source/tilebuf.cc | 378 |
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) |