warzone2100/lib/ivis_opengl/pieblitfunc.cpp

483 lines
14 KiB
C++

/*
This file is part of Warzone 2100.
Copyright (C) 1999-2004 Eidos Interactive
Copyright (C) 2005-2013 Warzone 2100 Project
Warzone 2100 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Warzone 2100 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/***************************************************************************/
/*
* pieBlitFunc.c
*
* patch for exisitng ivis rectangle draw functions.
*
*/
/***************************************************************************/
#include "lib/framework/frame.h"
#include "lib/framework/opengl.h"
#include <time.h>
#include "lib/ivis_opengl/bitimage.h"
#include "lib/ivis_opengl/pieblitfunc.h"
#include "lib/ivis_opengl/piedef.h"
#include "lib/ivis_opengl/piemode.h"
#include "lib/ivis_opengl/piestate.h"
#include "lib/ivis_opengl/pieclip.h"
#include "lib/ivis_opengl/piefunc.h"
#include "lib/ivis_opengl/piepalette.h"
#include "lib/ivis_opengl/tex.h"
#include "piematrix.h"
#include "screen.h"
/***************************************************************************/
/*
* Local Variables
*/
/***************************************************************************/
static GFX *radarGfx = NULL;
struct PIERECT ///< Screen rectangle.
{
float x, y, w, h;
};
/***************************************************************************/
/*
* Static function forward declarations
*/
/***************************************************************************/
static bool assertValidImage(IMAGEFILE *imageFile, unsigned id);
static Vector2i makePieImage(IMAGEFILE *imageFile, unsigned id, PIERECT *dest = NULL, int x = 0, int y = 0);
/***************************************************************************/
/*
* Source
*/
/***************************************************************************/
GFX::GFX(GFXTYPE type, GLenum drawType, int coordsPerVertex) : mType(type), mdrawType(drawType), mCoordsPerVertex(coordsPerVertex), mSize(0)
{
glGenBuffers(VBO_MINIMAL, mBuffers);
if (type == GFX_TEXTURE)
{
glGenTextures(1, &mTexture);
}
}
void GFX::loadTexture(const char *filename, GLenum filter)
{
ASSERT(mType == GFX_TEXTURE, "Wrong GFX type");
const char *extension = strrchr(filename, '.'); // determine the filetype
iV_Image image;
if (!extension || strcmp(extension, ".png") != 0)
{
debug(LOG_ERROR, "Bad image filename: %s", filename);
return;
}
if (iV_loadImage_PNG(filename, &image))
{
makeTexture(image.width, image.height, filter, iV_getPixelFormat(&image), image.bmp);
iV_unloadImage(&image);
}
}
void GFX::makeTexture(int width, int height, GLenum filter, GLenum format, const GLvoid *image)
{
ASSERT(mType == GFX_TEXTURE, "Wrong GFX type");
pie_SetTexturePage(TEXPAGE_EXTERN);
glBindTexture(GL_TEXTURE_2D, mTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, format, GL_UNSIGNED_BYTE, image);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
mWidth = width;
mHeight = height;
mFormat = format;
}
void GFX::updateTexture(const void *image, int width, int height)
{
ASSERT(mType == GFX_TEXTURE, "Wrong GFX type");
if (width == -1) width = mWidth;
if (height == -1) height = mHeight;
pie_SetTexturePage(TEXPAGE_EXTERN);
glBindTexture(GL_TEXTURE_2D, mTexture);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, mFormat, GL_UNSIGNED_BYTE, image);
}
void GFX::buffers(int vertices, const GLvoid *vertBuf, const GLvoid *auxBuf)
{
glBindBuffer(GL_ARRAY_BUFFER, mBuffers[VBO_VERTEX]);
glBufferData(GL_ARRAY_BUFFER, vertices * mCoordsPerVertex * sizeof(GLfloat), vertBuf, GL_STATIC_DRAW);
if (mType == GFX_TEXTURE)
{
glBindBuffer(GL_ARRAY_BUFFER, mBuffers[VBO_TEXCOORD]);
glBufferData(GL_ARRAY_BUFFER, vertices * 2 * sizeof(GLfloat), auxBuf, GL_STATIC_DRAW);
}
else if (mType == GFX_COLOUR)
{
// reusing texture buffer for colours for now
glBindBuffer(GL_ARRAY_BUFFER, mBuffers[VBO_TEXCOORD]);
glBufferData(GL_ARRAY_BUFFER, vertices * 4 * sizeof(GLbyte), auxBuf, GL_STATIC_DRAW);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
mSize = vertices;
}
void GFX::draw()
{
if (mType == GFX_TEXTURE)
{
pie_SetTexturePage(TEXPAGE_EXTERN);
glBindTexture(GL_TEXTURE_2D, mTexture);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, mBuffers[VBO_TEXCOORD]); glTexCoordPointer(2, GL_FLOAT, 0, NULL);
}
else if (mType == GFX_COLOUR)
{
pie_SetTexturePage(TEXPAGE_NONE);
glEnableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, mBuffers[VBO_TEXCOORD]); glColorPointer(4, GL_UNSIGNED_BYTE, 0, NULL);
}
glEnableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, mBuffers[VBO_VERTEX]); glVertexPointer(mCoordsPerVertex, GL_FLOAT, 0, NULL);
glDrawArrays(mdrawType, 0, mSize);
glDisableClientState(GL_VERTEX_ARRAY);
if (mType == GFX_TEXTURE)
{
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
else if (mType == GFX_COLOUR)
{
glDisableClientState(GL_COLOR_ARRAY);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
GFX::~GFX()
{
glDeleteBuffers(VBO_MINIMAL, mBuffers);
if (mType == GFX_TEXTURE)
{
glDeleteTextures(1, &mTexture);
}
}
void iV_Line(int x0, int y0, int x1, int y1, PIELIGHT colour)
{
pie_SetTexturePage(TEXPAGE_NONE);
glColor4ubv(colour.vector);
glBegin(GL_LINES);
glVertex2i(x0, y0);
glVertex2i(x1, y1);
glEnd();
}
/**
* Assumes render mode set up externally, draws filled rectangle.
*/
static void pie_DrawRect(float x0, float y0, float x1, float y1, PIELIGHT colour)
{
glColor4ubv(colour.vector);
glBegin(GL_TRIANGLE_STRIP);
glVertex2f(x0, y0);
glVertex2f(x1, y0);
glVertex2f(x0, y1);
glVertex2f(x1, y1);
glEnd();
}
void iV_ShadowBox(int x0, int y0, int x1, int y1, int pad, PIELIGHT first, PIELIGHT second, PIELIGHT fill)
{
pie_SetRendMode(REND_OPAQUE);
pie_SetTexturePage(TEXPAGE_NONE);
pie_DrawRect(x0 + pad, y0 + pad, x1 - pad, y1 - pad, fill); // necessary side-effect: sets alpha test off
iV_Box2(x0, y0, x1, y1, first, second);
}
/***************************************************************************/
void iV_Box2(int x0,int y0, int x1, int y1, PIELIGHT first, PIELIGHT second)
{
pie_SetTexturePage(TEXPAGE_NONE);
glColor4ubv(first.vector);
glBegin(GL_LINES);
glVertex2i(x0, y1);
glVertex2i(x0, y0);
glVertex2i(x0, y0);
glVertex2i(x1, y0);
glEnd();
glColor4ubv(second.vector);
glBegin(GL_LINES);
glVertex2i(x1, y0);
glVertex2i(x1, y1);
glVertex2i(x0, y1);
glVertex2i(x1, y1);
glEnd();
}
/***************************************************************************/
void pie_BoxFill(int x0,int y0, int x1, int y1, PIELIGHT colour)
{
pie_SetRendMode(REND_OPAQUE);
pie_SetTexturePage(TEXPAGE_NONE);
pie_DrawRect(x0, y0, x1, y1, colour);
}
/***************************************************************************/
void iV_TransBoxFill(float x0, float y0, float x1, float y1)
{
pie_UniTransBoxFill(x0, y0, x1, y1, WZCOL_TRANSPARENT_BOX);
}
/***************************************************************************/
void pie_UniTransBoxFill(float x0, float y0, float x1, float y1, PIELIGHT light)
{
pie_SetTexturePage(TEXPAGE_NONE);
pie_SetRendMode(REND_ALPHA);
pie_DrawRect(x0, y0, x1, y1, light);
}
/***************************************************************************/
static bool assertValidImage(IMAGEFILE *imageFile, unsigned id)
{
ASSERT_OR_RETURN(false, id < imageFile->imageDefs.size(), "Out of range 1: %u/%d", id, (int)imageFile->imageDefs.size());
ASSERT_OR_RETURN(false, imageFile->imageDefs[id].TPageID < imageFile->pages.size(), "Out of range 2: %u", imageFile->imageDefs[id].TPageID);
return true;
}
static void pie_DrawImage(IMAGEFILE *imageFile, int id, Vector2i size, const PIERECT *dest, PIELIGHT colour = WZCOL_WHITE)
{
ImageDef const &image2 = imageFile->imageDefs[id];
GLuint texPage = imageFile->pages[image2.TPageID].id;
GLfloat invTextureSize = 1.f / imageFile->pages[image2.TPageID].size;
int tu = image2.Tu;
int tv = image2.Tv;
pie_SetTexturePage(texPage);
glColor4ubv(colour.vector);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(tu * invTextureSize, tv * invTextureSize);
glVertex2f(dest->x, dest->y);
glTexCoord2f((tu + size.x) * invTextureSize, tv * invTextureSize);
glVertex2f(dest->x + dest->w, dest->y);
glTexCoord2f(tu * invTextureSize, (tv + size.y) * invTextureSize);
glVertex2f(dest->x, dest->y + dest->h);
glTexCoord2f((tu + size.x) * invTextureSize, (tv + size.y) * invTextureSize);
glVertex2f(dest->x + dest->w, dest->y + dest->h);
glEnd();
}
static Vector2i makePieImage(IMAGEFILE *imageFile, unsigned id, PIERECT *dest, int x, int y)
{
ImageDef const &image = imageFile->imageDefs[id];
Vector2i pieImage;
pieImage.x = image.Width;
pieImage.y = image.Height;
if (dest != NULL)
{
dest->x = x + image.XOffset;
dest->y = y + image.YOffset;
dest->w = image.Width;
dest->h = image.Height;
}
return pieImage;
}
void iV_DrawImage2(const QString &filename, float x, float y, float width, float height)
{
ImageDef *image = iV_GetImage(filename, x, y);
const GLfloat invTextureSize = image->invTextureSize;
const int tu = image->Tu;
const int tv = image->Tv;
const int w = width > 0 ? width : image->Width;
const int h = height > 0 ? height : image->Height;
x += image->XOffset;
y += image->YOffset;
pie_SetTexturePage(image->textureId);
glColor4ubv(WZCOL_WHITE.vector);
pie_SetRendMode(REND_ALPHA);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(tu * image->invTextureSize, tv * invTextureSize);
glVertex2f(x, y);
glTexCoord2f((tu + image->Width) * invTextureSize, tv * invTextureSize);
glVertex2f(x + w, y);
glTexCoord2f(tu * invTextureSize, (tv + image->Height) * invTextureSize);
glVertex2f(x, y + h);
glTexCoord2f((tu + image->Width) * invTextureSize, (tv + image->Height) * invTextureSize);
glVertex2f(x + w, y + h);
glEnd();
}
void iV_DrawImage(IMAGEFILE *ImageFile, UWORD ID, int x, int y)
{
if (!assertValidImage(ImageFile, ID))
{
return;
}
PIERECT dest;
Vector2i pieImage = makePieImage(ImageFile, ID, &dest, x, y);
pie_SetRendMode(REND_ALPHA);
pie_DrawImage(ImageFile, ID, pieImage, &dest);
}
void iV_DrawImageTc(Image image, Image imageTc, int x, int y, PIELIGHT colour)
{
if (!assertValidImage(image.images, image.id) || !assertValidImage(imageTc.images, imageTc.id))
{
return;
}
PIERECT dest;
Vector2i pieImage = makePieImage(image.images, image.id, &dest, x, y);
Vector2i pieImageTc = makePieImage(imageTc.images, imageTc.id);
pie_SetRendMode(REND_ALPHA);
pie_DrawImage(image.images, image.id, pieImage, &dest);
pie_DrawImage(imageTc.images, imageTc.id, pieImageTc, &dest, colour);
}
// Repeat a texture
void iV_DrawImageRepeatX(IMAGEFILE *ImageFile, UWORD ID, int x, int y, int Width)
{
int hRep, hRemainder;
assertValidImage(ImageFile, ID);
const ImageDef *Image = &ImageFile->imageDefs[ID];
pie_SetRendMode(REND_OPAQUE);
PIERECT dest;
Vector2i pieImage = makePieImage(ImageFile, ID, &dest, x, y);
hRemainder = Width % Image->Width;
for (hRep = 0; hRep < Width / Image->Width; hRep++)
{
pie_DrawImage(ImageFile, ID, pieImage, &dest);
dest.x += Image->Width;
}
// draw remainder
if (hRemainder > 0)
{
pieImage.x = hRemainder;
dest.w = hRemainder;
pie_DrawImage(ImageFile, ID, pieImage, &dest);
}
}
void iV_DrawImageRepeatY(IMAGEFILE *ImageFile, UWORD ID, int x, int y, int Height)
{
int vRep, vRemainder;
assertValidImage(ImageFile, ID);
const ImageDef *Image = &ImageFile->imageDefs[ID];
pie_SetRendMode(REND_OPAQUE);
PIERECT dest;
Vector2i pieImage = makePieImage(ImageFile, ID, &dest, x, y);
vRemainder = Height % Image->Height;
for (vRep = 0; vRep < Height / Image->Height; vRep++)
{
pie_DrawImage(ImageFile, ID, pieImage, &dest);
dest.y += Image->Height;
}
// draw remainder
if (vRemainder > 0)
{
pieImage.y = vRemainder;
dest.h = vRemainder;
pie_DrawImage(ImageFile, ID, pieImage, &dest);
}
}
bool pie_InitRadar(void)
{
radarGfx = new GFX(GFX_TEXTURE, GL_TRIANGLE_STRIP, 2);
return true;
}
bool pie_ShutdownRadar(void)
{
delete radarGfx;
radarGfx = NULL;
return true;
}
void pie_SetRadar(GLfloat x, GLfloat y, GLfloat width, GLfloat height, int twidth, int theight, bool filter)
{
radarGfx->makeTexture(twidth, theight, filter ? GL_LINEAR : GL_NEAREST);
GLfloat texcoords[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f };
GLfloat vertices[] = { x, y, x + width, y, x, y + height, x + width, y + height };
radarGfx->buffers(4, vertices, texcoords);
}
/** Store radar texture with given width and height. */
void pie_DownLoadRadar(UDWORD *buffer)
{
radarGfx->updateTexture(buffer);
}
/** Display radar texture using the given height and width, depending on zoom level. */
void pie_RenderRadar()
{
pie_SetRendMode(REND_ALPHA);
glColor4ubv(WZCOL_WHITE.vector); // hack
radarGfx->draw();
}
/// Load and display a random backdrop picture.
void pie_LoadBackDrop(SCREENTYPE screenType)
{
switch (screenType)
{
case SCREEN_RANDOMBDROP:
screen_SetRandomBackdrop("texpages/bdrops/", "backdrop");
break;
case SCREEN_MISSIONEND:
screen_SetRandomBackdrop("texpages/bdrops/", "missionend");
break;
case SCREEN_CREDITS:
screen_SetRandomBackdrop("texpages/bdrops/", "credits");
break;
}
}