aboutsummaryrefslogtreecommitdiff
path: root/engines/sword25/util/glsprites/internal/sprite_tiled.c
diff options
context:
space:
mode:
authorEugene Sandulenko2010-07-29 19:53:02 +0000
committerEugene Sandulenko2010-10-12 21:38:20 +0000
commita683a420a9e43705c972b5e74d55e319729e1a81 (patch)
treebde6e4abd417bdfaec120aa951da9a19be36b654 /engines/sword25/util/glsprites/internal/sprite_tiled.c
parent7723d91c957d07205c51be32498d45cd0a78568f (diff)
downloadscummvm-rg350-a683a420a9e43705c972b5e74d55e319729e1a81.tar.gz
scummvm-rg350-a683a420a9e43705c972b5e74d55e319729e1a81.tar.bz2
scummvm-rg350-a683a420a9e43705c972b5e74d55e319729e1a81.zip
SWORD25: Importing original sources
svn-id: r53171
Diffstat (limited to 'engines/sword25/util/glsprites/internal/sprite_tiled.c')
-rwxr-xr-xengines/sword25/util/glsprites/internal/sprite_tiled.c370
1 files changed, 370 insertions, 0 deletions
diff --git a/engines/sword25/util/glsprites/internal/sprite_tiled.c b/engines/sword25/util/glsprites/internal/sprite_tiled.c
new file mode 100755
index 0000000000..82435a638e
--- /dev/null
+++ b/engines/sword25/util/glsprites/internal/sprite_tiled.c
@@ -0,0 +1,370 @@
+/******************************************************************************/
+/* This file is part of Broken Sword 2.5 */
+/* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer */
+/* */
+/* Broken Sword 2.5 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. */
+/* */
+/* Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+/******************************************************************************/
+
+/* --------------------------------------------------------------------------
+ INCLUDES
+ -------------------------------------------------------------------------- */
+
+#include <malloc.h>
+
+#include "glinclude.h"
+#include "core.h"
+#include "util.h"
+#include "sprite.h"
+#include "sprite_tiled.h"
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Result Delete(GLS_Sprite *);
+static GLS_Result Blit(GLS_Sprite *, const GLS_Rect *);
+static GLS_Result SetData(GLS_Sprite *, GLS_UInt32, GLS_UInt32, const void *, GLS_UInt32);
+static GLS_spriteFunctionTable tiledFunctionTable = { ST_TILED, Delete, Blit, SetData };
+
+/* -------------------------------------------------------------------------- */
+
+#define MIN_TEXTURE_SIZE_LOG2 6
+#define MIN_TEXTURE_SIZE (1 << MIN_TEXTURE_SIZE_LOG2)
+
+#define MAX_POSSIBLE_TEXTURE_SIZE_LOG2 31
+#define MAX_POSSIBLE_TEXTURE_SIZE (1 << MAX_POSSIBLE_TEXTURE_SIZE_LOG2)
+
+/* -------------------------------------------------------------------------- */
+
+typedef struct
+{
+ GLint textureID;
+ GLfloat x;
+ GLfloat y;
+ GLfloat width;
+ GLfloat height;
+} tile;
+
+/* -------------------------------------------------------------------------- */
+
+typedef struct
+{
+ /* These first three members have to be present at the start of each sprite type. */
+ GLS_spriteFunctionTable * functionTable;
+ GLS_UInt32 width;
+ GLS_UInt32 height;
+
+ GLS_UInt32 widthSubdivisionCount;
+ GLS_UInt32 heightSubdivisionCount;
+ tile tiles[1]; /* Has to be last member. */
+} spriteTiled;
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_UInt32 CalculateSubdivisions(GLS_UInt32 value, GLS_UInt32 maxTextureSize, GLS_UInt32 maxTextureSizeLog2,
+ GLS_UInt32 subdivisions[MAX_POSSIBLE_TEXTURE_SIZE_LOG2 + 1])
+{
+ /* This function takes a value and determines how it best can be divided up into pieces of power of 2 length.
+ The resulting parts are at least MIN_TEXTURE_SIZE long and at most maxTextureSize long.
+ If we do this for both width and height of a sprite, we can determine how this sprite can be split up into
+ smaller power of 2 textures using a simple two dimensional loop.
+
+ This returns the number of subdivisions as the return value.
+ The sizes of the different pieces are encoded in the subdivisions array. The indicies into the array determine the
+ power of 2 and the values in the array the number of pieces with that size.
+ E.g. if the array contains the value 3 at index 4 this means you have 3 pieces of size 16 (2^4 = 16).
+ */
+
+ GLS_UInt32 subdivisionCount = 0;
+ GLS_UInt32 maxSizeCount;
+ GLS_UInt32 counter = 0;
+
+ /* First determine how many pieces of maximal size we can fill and write this into the result array.
+ Update the value and the subdivision count accordingly. */
+ maxSizeCount = value / maxTextureSize;
+ subdivisions[maxTextureSizeLog2] = maxSizeCount;
+ subdivisionCount += maxSizeCount;
+ value -= maxSizeCount * maxTextureSize;
+
+ /* The remaining value is rounded up to a multiple of the minimal texture size. */
+ value = ((value + MIN_TEXTURE_SIZE - 1) / MIN_TEXTURE_SIZE) * MIN_TEXTURE_SIZE;
+
+ /* Determine the remaining pieces by using the binary representation of the value.
+ If a bit is set, we need to add a piece of the according size. */
+ while (value)
+ {
+ if (value & 1)
+ {
+ ++subdivisions[counter];
+ ++subdivisionCount;
+ }
+
+ ++counter;
+ value >>= 1;
+ }
+
+ return subdivisionCount;
+}
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_NewSpriteTiled(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool useAlphachannel, void * data, GLS_Sprite ** pSprite)
+{
+ GLS_UInt32 maxTextureSize, maxTextureSizeLog2;
+ GLS_UInt32 widthSubdivisions[MAX_POSSIBLE_TEXTURE_SIZE_LOG2 + 1] = { 0 };
+ GLS_UInt32 widthSubdivisionCount;
+ GLS_UInt32 heightSubdivisions[MAX_POSSIBLE_TEXTURE_SIZE_LOG2 + 1] = { 0 };
+ GLS_UInt32 heightSubdivisionCount;
+ GLS_UInt32 subdivisonX, subdivisionY, pixelX = 0, pixelY = 0, i;
+ spriteTiled * sprite;
+ tile * curTile;
+
+ /* Determine the maximal texture size to be used and make sure it is a power of two. */
+ maxTextureSize = GLS_TheOGLCaps.maxTextureSize > MAX_POSSIBLE_TEXTURE_SIZE ? MAX_POSSIBLE_TEXTURE_SIZE : GLS_TheOGLCaps.maxTextureSize;
+ maxTextureSize = GLS_IsPowerOf2(maxTextureSize) ? maxTextureSize : GLS_NextPowerOf2(maxTextureSize >> 1);
+
+ /* Determine the log2 of the maximal texture size. */
+ maxTextureSizeLog2 = GLS_Log2(maxTextureSize);
+
+ /* Determine the subdivisions along the width and the height. */
+ widthSubdivisionCount = CalculateSubdivisions(width, maxTextureSize, maxTextureSizeLog2, widthSubdivisions);
+ heightSubdivisionCount = CalculateSubdivisions(height, maxTextureSize, maxTextureSizeLog2, heightSubdivisions);
+
+ /* Allocate memory for sprite object */
+ sprite = (spriteTiled *) calloc(sizeof(spriteTiled) + sizeof(tile) * (widthSubdivisionCount * heightSubdivisionCount - 1), 1);
+ if (!sprite) return GLS_OUT_OF_MEMORY;
+
+ /* Initialize sprite object. */
+ sprite->functionTable = &tiledFunctionTable;
+ sprite->width = width;
+ sprite->height = height;
+ sprite->widthSubdivisionCount = widthSubdivisionCount;
+ sprite->heightSubdivisionCount = heightSubdivisionCount;
+
+ /* Create the textures. */
+ curTile = &sprite->tiles[0];
+ for (subdivisionY = MAX_POSSIBLE_TEXTURE_SIZE_LOG2;; --subdivisionY)
+ {
+ while (heightSubdivisions[subdivisionY]--)
+ {
+ for (subdivisonX = MAX_POSSIBLE_TEXTURE_SIZE_LOG2;; --subdivisonX)
+ {
+ for (i = 0; i < widthSubdivisions[subdivisonX]; ++i)
+ {
+ /* Initialize tile. Store as GLfloat to avoid unnecessary integer conversions when drawing. */
+ curTile->x = (GLfloat) pixelX;
+ curTile->y = (GLfloat) pixelY;
+ curTile->width = (GLfloat) (1 << subdivisonX);
+ curTile->height = (GLfloat) (1 << subdivisionY);
+
+ /* Create OpenGL texture */
+ glGenTextures(1, &(curTile->textureID));
+ glBindTexture(GL_TEXTURE_2D, curTile->textureID);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, useAlphachannel ? GL_RGBA8 : GL_RGB8, 1 << subdivisonX, 1 << subdivisionY, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+
+ /* Advance to the next empty tile. */
+ pixelX += 1 << subdivisonX;
+ ++curTile;
+ }
+
+ /* Test for loop end. Prevents underflow of unsigned integer. */
+ if (subdivisonX == 0) break;
+ }
+
+ /* Enter next row. */
+ pixelX = 0;
+ pixelY += 1 << subdivisionY;
+ }
+
+ /* Test for loop end. Prevents underflow of unsigned integer. */
+ if (subdivisionY == 0) break;
+ }
+
+ *pSprite = (GLS_Sprite) sprite;
+
+ /* Intialize the sprite with the supplied pixel data (if any). */
+ if (data) SetData(*pSprite, width, height, data, 0);
+
+ /* If an error occured the sprite is deleted and the function returns with an error. */
+ if (glGetError())
+ {
+ Delete(*pSprite);
+ return GLS_TEXTURE_CREATION_FAILED;
+ }
+
+ return GLS_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Result Delete(GLS_Sprite * sprite)
+{
+ spriteTiled * s = (spriteTiled *) sprite;
+ GLS_UInt32 tileCount = s->widthSubdivisionCount * s->heightSubdivisionCount;
+ GLS_UInt32 i;
+
+ /* Delete each texture. */
+ for (i = 0; i < tileCount; ++i)
+ {
+ if (s->tiles[i].textureID) glDeleteTextures(1, &s->tiles[i].textureID);
+ }
+
+ /* Free the memory used by the sprite. */
+ free(s);
+
+ return GLS_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Result Blit(GLS_Sprite * sprite,
+ const GLS_Rect * subImage)
+{
+ GLfloat width, height;
+ GLfloat texX1, texY1, texX2, texY2;
+ GLS_UInt32 tileIndex = 0;
+ GLS_UInt32 tileX, tileY;
+ GLfloat tileRectX1, tileRectY1;
+ GLfloat tileRectX2, tileRectY2;
+ GLfloat tileWidth, tileHeight;
+ GLfloat lastHeight = 0;
+ GLS_GLfloatRect tileRect, resultRect;
+ GLfloat x = 0.0f, y = 0.0f;
+ GLS_GLfloatRect subImageF = { (GLfloat) subImage->x1, (GLfloat) subImage->y1, (GLfloat) subImage->x2, (GLfloat) subImage->y2 };
+
+ spriteTiled * s = (spriteTiled *) sprite;
+
+ /* Determine width and height of the subimage to display. */
+ width = (float)(subImage->x2 - subImage->x1);
+ height = (float)(subImage->y2 - subImage->y1);
+
+ glEnable(GL_TEXTURE_2D);
+
+ /* Draw all required tiles. */
+ for (tileY = 0; tileY < s->heightSubdivisionCount; ++tileY)
+ {
+ for (tileX = 0; tileX < s->widthSubdivisionCount; ++tileX)
+ {
+ tileRect.x1 = s->tiles[tileIndex].x;
+ tileRect.y1 = s->tiles[tileIndex].y;
+ tileRect.x2 = tileRect.x1 + s->tiles[tileIndex].width;
+ tileRect.y2 = tileRect.y1 + s->tiles[tileIndex].height;
+
+ if (GLS_IntersectGLfloatRects(&tileRect, &subImageF, &resultRect))
+ {
+ tileRectX1 = resultRect.x1;
+ tileRectY1 = resultRect.y1;
+ tileRectX2 = resultRect.x2;
+ tileRectY2 = resultRect.y2;
+
+ glBindTexture(GL_TEXTURE_2D, s->tiles[tileIndex].textureID);
+
+ texX1 = (tileRectX1 - s->tiles[tileIndex].x) / (GLfloat) s->tiles[tileIndex].width;
+ texY1 = (tileRectY1 - s->tiles[tileIndex].y) / (GLfloat) s->tiles[tileIndex].height;
+ texX2 = (GLfloat) (tileRectX2 - s->tiles[tileIndex].x) / (GLfloat) s->tiles[tileIndex].width;
+ texY2 = (GLfloat) (tileRectY2 - s->tiles[tileIndex].y) / (GLfloat) s->tiles[tileIndex].height;
+
+ tileWidth = resultRect.x2 - resultRect.x1;
+ tileHeight = resultRect.y2 - resultRect.y1;
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(texX1, texY1);
+ glVertex2f(x, y);
+
+ glTexCoord2f(texX2, texY1);
+ glVertex2f(x + tileWidth, y);
+
+ glTexCoord2f(texX2, texY2);
+ glVertex2f(x + tileWidth, y + tileHeight);
+
+ glTexCoord2f(texX1, texY2);
+ glVertex2f(x, y + tileHeight);
+ glEnd();
+
+ x += tileWidth;
+ lastHeight = tileHeight;
+ }
+
+ ++tileIndex;
+ }
+
+ x = 0;
+ y += lastHeight;
+ }
+
+ glDisable(GL_TEXTURE_2D);
+
+ return glGetError() ? GLS_OPENGL_ERROR : GLS_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Result SetData(GLS_Sprite * sprite, GLS_UInt32 width, GLS_UInt32 height, const void * data, GLS_UInt32 stride)
+{
+ spriteTiled * s = (spriteTiled *) sprite;
+ GLS_UInt32 x = 0, y = 0, tileIndex = 0;
+ GLS_UInt32 tileX, tileY;
+ GLS_UInt32 copyWidth, copyHeight;
+ const GLS_UInt32 * pixelData = (const GLS_UInt32 *) data;
+
+ /* Set the source image row length and push the original value that it can be restored later. */
+ glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, width + stride);
+
+ /* Loop through all tiles width first. */
+ for (tileY = 0; tileY < s->heightSubdivisionCount; ++tileY)
+ {
+ /* Is there enough pixel data to reach the next tile? */
+ if (height > y)
+ {
+ for (tileX = 0; tileX < s->widthSubdivisionCount; ++tileX)
+ {
+ /* Is the enough pixel data to reach the next tile? */
+ if (width > x)
+ {
+ /* Determine the amount of pixels to be copied to the current tile.
+ They might either cover the whole tile, or just part of the tile if not enough
+ pixel data is left. */
+ copyWidth = GLS_MIN(width - x, (GLS_UInt32) s->tiles[tileIndex].width);
+ copyHeight = GLS_MIN(height - y, (GLS_UInt32) s->tiles[tileIndex].height);
+
+ /* Copy the pixels. */
+ glBindTexture(GL_TEXTURE_2D, s->tiles[tileIndex].textureID);
+
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, copyWidth, copyHeight, GL_RGBA, GL_UNSIGNED_BYTE,
+ &pixelData[x + y * (width + stride)]);
+ }
+
+ /* Update the x position inside the pixel data and advance to the next tile. */
+ x += (GLS_UInt32) s->tiles[tileIndex].width;
+ ++tileIndex;
+ }
+ }
+
+ /* Restet the x position inside the pixel data (we have reached another row) and update the y position. */
+ x = 0;
+ y += (GLS_UInt32) s->tiles[tileIndex - 1].height;
+ }
+
+ glPopClientAttrib();
+
+ return glGetError() ? GLS_OPENGL_ERROR : GLS_OK;
+}