mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-24 10:40:46 +00:00
Dynamic tilepacking for indexed colour textures
git-svn-id: https://svn.eduke32.com/eduke32@6883 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
parent
3ad44df555
commit
dcdffb73d0
8 changed files with 655 additions and 79 deletions
|
@ -228,7 +228,7 @@ else
|
|||
endif
|
||||
endif
|
||||
ifeq (1,$(USE_OPENGL))
|
||||
engine_objs += voxmodel.cpp mdsprite.cpp
|
||||
engine_objs += voxmodel.cpp mdsprite.cpp tilepacker.cpp
|
||||
engine_deps += glad
|
||||
ifeq (1,$(POLYMER))
|
||||
engine_objs += glbuild.cpp polymer.cpp
|
||||
|
|
|
@ -817,6 +817,14 @@ typedef struct {
|
|||
|
||||
EDUKE32_STATIC_ASSERT(sizeof(vec3d_t) == sizeof(double) * 3);
|
||||
|
||||
typedef struct {
|
||||
float x, y, z, w;
|
||||
} vec4f_t;
|
||||
|
||||
typedef struct {
|
||||
float x, y, z, w;
|
||||
} vec4d_t;
|
||||
|
||||
|
||||
////////// Language tricks that depend on size_t //////////
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ extern void Polymost_prepare_loadboard(void);
|
|||
|
||||
//void phex(char v, char *s);
|
||||
void uploadtexture(int32_t doalloc, vec2_t siz, int32_t texfmt, coltype *pic, vec2_t tsiz, int32_t dameth);
|
||||
void uploadtextureindexed(int32_t doalloc, vec2_t siz, vec2_t offset, intptr_t tile);
|
||||
void uploadbasepalette(int32_t basepalnum);
|
||||
void uploadpalswap(int32_t palookupnum);
|
||||
void polymost_drawsprite(int32_t snum);
|
||||
|
@ -45,13 +46,22 @@ void polymost_drawrooms(void);
|
|||
void polymost_resetVertexPointers(void);
|
||||
void polymost_disableProgram(void);
|
||||
void polymost_resetProgram(void);
|
||||
void polymost_setTexturePosSize(vec4f_t texturePosSize);
|
||||
void polymost_setFogEnabled(char fogEnabled);
|
||||
void polymost_useColorOnly(char useColorOnly);
|
||||
void polymost_usePaletteIndexing(char usePaletteIndexing);
|
||||
void polymost_useDetailMapping(char useDetailMapping);
|
||||
void polymost_useGlowMapping(char useGlowMapping);
|
||||
void polymost_activeTexture(GLenum texture);
|
||||
void polymost_bindTexture(GLenum target, uint32_t textureID);
|
||||
void useShaderProgram(uint32_t shaderID);
|
||||
|
||||
//POGOTODO: these wrappers won't be needed down the line -- remove them once proper draw call organization is finished
|
||||
#undef glActiveTexture
|
||||
#undef glBindTexture
|
||||
#define glActiveTexture polymost_activeTexture
|
||||
#define glBindTexture polymost_bindTexture
|
||||
|
||||
void polymost_glinit(void);
|
||||
void polymost_glreset(void);
|
||||
|
||||
|
|
54
source/build/include/tilepacker.h
Normal file
54
source/build/include/tilepacker.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* tilepacker.h
|
||||
* A k-d tree based bin packer that organizes rectangular tiles to fit
|
||||
* neatly into one texture.
|
||||
*
|
||||
* Copyright © 2018, Alex Dawson. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef TILEPACKER_H_
|
||||
#define TILEPACKER_H_
|
||||
|
||||
#define MAXTILESHEETS 64
|
||||
#define MAXPACKEDTILES MAXTILES+1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t u, v, width, height;
|
||||
} TileRect;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t tilesheetID;
|
||||
TileRect rect;
|
||||
} Tile;
|
||||
|
||||
// Initialize the specified tilesheet
|
||||
// tilesheetId must be less than MAXTILESHEETS
|
||||
// Re-initializing an existing tilesheet will discard all of its contents
|
||||
void tilepacker_initTilesheet(uint32_t tilesheetID, uint32_t tilesheetWidth, uint32_t tilesheetHeight);
|
||||
|
||||
// Adds a tile into sorted collection to be packed
|
||||
// uid can be any unique key -- picnums are used for indexed colour textures
|
||||
void tilepacker_addTile(uint32_t tileUID, uint32_t tileWidth, uint32_t tileHeight);
|
||||
|
||||
// Packs the sorted collection of tiles that have been added with tilepacker_addTile()
|
||||
// Returns true if all nodes could be packed succesfully into the specified tilesheet
|
||||
// tilepacker_pack can be called again with a different tilesheetId to pack the remaining tiles
|
||||
char tilepacker_pack(uint32_t tilesheetID);
|
||||
|
||||
// Discard the rejected tiles so that they will not be carried over to the next call to tilepacker_pack
|
||||
void tilepacker_discardRejects();
|
||||
|
||||
// Sets pOutput to contain the Tile information for a packed tile with the given tileUID
|
||||
// Returns true if the Tile has been packed, false otherwise
|
||||
// If pOutput is NULL, the function solely returns whether or not the Tile has been packed
|
||||
char tilepacker_getTile(uint32_t tileUID, Tile *pOutput);
|
||||
|
||||
// Returns true if the Tile with tileUID has been packed, false otherwise
|
||||
static inline char tilepacker_isTilePacked(uint32_t tileUID)
|
||||
{
|
||||
return tilepacker_getTile(tileUID, NULL);
|
||||
};
|
||||
|
||||
#endif /* TILEPACKER_H_ */
|
|
@ -2293,6 +2293,9 @@ static int32_t polymost_md3draw(md3model_t *m, const uspritetype *tspr)
|
|||
float const xpanning = (float)sext->xpanning * (1.f/256.f);
|
||||
float const ypanning = (float)sext->ypanning * (1.f/256.f);
|
||||
|
||||
polymost_usePaletteIndexing(false);
|
||||
polymost_setTexturePosSize({ 0.f, 0.f, 1.f, 1.f });
|
||||
|
||||
for (surfi=0; surfi<m->head.numsurfs; surfi++)
|
||||
{
|
||||
//PLAG : sorting stuff
|
||||
|
@ -2593,6 +2596,7 @@ static int32_t polymost_md3draw(md3model_t *m, const uspritetype *tspr)
|
|||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
polymost_usePaletteIndexing(true);
|
||||
polymost_resetVertexPointers();
|
||||
|
||||
globalnoeffect=0;
|
||||
|
@ -2756,19 +2760,9 @@ int32_t polymost_mddraw(const uspritetype *tspr)
|
|||
mdmodel_t *const vm = models[tile2model[Ptile2tile(tspr->picnum,
|
||||
(tspr->owner >= MAXSPRITES) ? tspr->pal : sprite[tspr->owner].pal)].modelid];
|
||||
if (vm->mdnum == 1)
|
||||
{
|
||||
polymost_usePaletteIndexing(false);
|
||||
int ret = polymost_voxdraw((voxmodel_t *)vm,tspr);
|
||||
polymost_usePaletteIndexing(true);
|
||||
return ret;
|
||||
}
|
||||
if (vm->mdnum == 3)
|
||||
{
|
||||
polymost_usePaletteIndexing(false);
|
||||
int ret = polymost_md3draw((md3model_t *)vm,tspr);
|
||||
polymost_usePaletteIndexing(true);
|
||||
return ret;
|
||||
}
|
||||
return polymost_voxdraw((voxmodel_t *)vm,tspr);
|
||||
else if (vm->mdnum == 3)
|
||||
return polymost_md3draw((md3model_t *)vm,tspr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ Ken Silverman's official web site: http://www.advsys.net/ken
|
|||
#include "texcache.h"
|
||||
#include "common.h"
|
||||
#include "palette.h"
|
||||
#include "tilepacker.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
extern int32_t filelength(int h); // kplib.c
|
||||
|
@ -79,7 +80,7 @@ int32_t drawpolyVertsBufferLength = r_drawpolyVertsBufferLength;
|
|||
static GLuint drawpolyVertsID = 0;
|
||||
static GLint drawpolyVertsOffset = 0;
|
||||
static int32_t drawpolyVertsSubBufferIndex = 0;
|
||||
static GLsync drawpolyVertsSync[3] = {0};
|
||||
static GLsync drawpolyVertsSync[3] = { 0 };
|
||||
static float defaultDrawpolyVertsArray[MAX_DRAWPOLY_VERTS*5];
|
||||
static float* drawpolyVerts = defaultDrawpolyVertsArray;
|
||||
|
||||
|
@ -136,8 +137,9 @@ static float fogresult, fogresult2;
|
|||
coltypef fogcol, fogtable[MAXPALOOKUPS];
|
||||
|
||||
static uint32_t currentShaderProgramID = 0;
|
||||
static GLenum currentActiveTexture = 0;
|
||||
static uint32_t currentTextureID = 0;
|
||||
|
||||
static GLuint blankTextureID = 0;
|
||||
static GLuint quadVertsID = 0;
|
||||
static GLuint polymost2BasicShaderProgramID = 0;
|
||||
static GLint texSamplerLoc = -1;
|
||||
|
@ -153,6 +155,9 @@ static GLint fogColorLoc = -1;
|
|||
|
||||
#define PALSWAP_TEXTURE_SIZE 2048
|
||||
int32_t r_useindexedcolortextures = -1;
|
||||
static GLuint tilesheetTexIDs[MAXTILESHEETS];
|
||||
static GLint tilesheetSize = 0;
|
||||
static vec2f_t tilesheetHalfTexelSize = { 0.f, 0.f };
|
||||
static int32_t lastbasepal = -1;
|
||||
static GLuint paletteTextureIDs[MAXBASEPALS];
|
||||
static GLuint palswapTextureID = 0;
|
||||
|
@ -164,11 +169,15 @@ static GLint polymost1PalSwapSamplerLoc = -1;
|
|||
static GLint polymost1PaletteSamplerLoc = -1;
|
||||
static GLint polymost1DetailSamplerLoc = -1;
|
||||
static GLint polymost1GlowSamplerLoc = -1;
|
||||
static GLint polymost1TexturePosSizeLoc = -1;
|
||||
static vec4f_t polymost1TexturePosSize = { 0.f, 0.f, 1.f, 1.f };
|
||||
static GLint polymost1HalfTexelSizeLoc = -1;
|
||||
static vec2f_t polymost1HalfTexelSize = { 0.f, 0.f };
|
||||
static GLint polymost1PalswapPosLoc = -1;
|
||||
static vec2f_t polymost1PalswapPos = {0.f, 0.f};
|
||||
static vec2f_t polymost1PalswapPos = { 0.f, 0.f };
|
||||
static GLint polymost1PalswapSizeLoc = -1;
|
||||
static vec2f_t polymost1PalswapSize = {0.f, 0.f};
|
||||
static vec2f_t polymost1PalswapInnerSize = {0.f, 0.f};
|
||||
static vec2f_t polymost1PalswapSize = { 0.f, 0.f };
|
||||
static vec2f_t polymost1PalswapInnerSize = { 0.f, 0.f };
|
||||
static GLint polymost1ShadeLoc = -1;
|
||||
static float polymost1Shade = 0.f;
|
||||
static GLint polymost1FogEnabledLoc = -1;
|
||||
|
@ -580,6 +589,8 @@ static void polymost_setCurrentShaderProgram(uint32_t programID)
|
|||
polymost1PaletteSamplerLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "s_palette");
|
||||
polymost1DetailSamplerLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "s_detail");
|
||||
polymost1GlowSamplerLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "s_glow");
|
||||
polymost1TexturePosSizeLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_texturePosSize");
|
||||
polymost1HalfTexelSizeLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_halfTexelSize");
|
||||
polymost1PalswapPosLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_palswapPos");
|
||||
polymost1PalswapSizeLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_palswapSize");
|
||||
polymost1ShadeLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_shade");
|
||||
|
@ -590,6 +601,8 @@ static void polymost_setCurrentShaderProgram(uint32_t programID)
|
|||
polymost1UseGlowMappingLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_useGlowMapping");
|
||||
|
||||
//set the uniforms to the current values
|
||||
glUniform4f(polymost1TexturePosSizeLoc, polymost1TexturePosSize.x, polymost1TexturePosSize.y, polymost1TexturePosSize.z, polymost1TexturePosSize.w);
|
||||
glUniform2f(polymost1HalfTexelSizeLoc, polymost1HalfTexelSize.x, polymost1HalfTexelSize.y);
|
||||
glUniform2f(polymost1PalswapPosLoc, polymost1PalswapPos.x, polymost1PalswapPos.y);
|
||||
glUniform2f(polymost1PalswapSizeLoc, polymost1PalswapInnerSize.x, polymost1PalswapInnerSize.y);
|
||||
glUniform1f(polymost1ShadeLoc, polymost1Shade);
|
||||
|
@ -600,6 +613,24 @@ static void polymost_setCurrentShaderProgram(uint32_t programID)
|
|||
glUniform1f(polymost1UseGlowMappingLoc, polymost1UseGlowMapping);
|
||||
}
|
||||
|
||||
void polymost_setTexturePosSize(vec4f_t texturePosSize)
|
||||
{
|
||||
if (currentShaderProgramID == polymost1CurrentShaderProgramID)
|
||||
{
|
||||
polymost1TexturePosSize = texturePosSize;
|
||||
glUniform4f(polymost1TexturePosSizeLoc, polymost1TexturePosSize.x, polymost1TexturePosSize.y, polymost1TexturePosSize.z, polymost1TexturePosSize.w);
|
||||
}
|
||||
}
|
||||
|
||||
static void polymost_setHalfTexelSize(vec2f_t halfTexelSize)
|
||||
{
|
||||
if (currentShaderProgramID == polymost1CurrentShaderProgramID)
|
||||
{
|
||||
polymost1HalfTexelSize = halfTexelSize;
|
||||
glUniform2f(polymost1HalfTexelSizeLoc, polymost1HalfTexelSize.x, polymost1HalfTexelSize.y);
|
||||
}
|
||||
}
|
||||
|
||||
static void polymost_setPalswap(uint32_t index)
|
||||
{
|
||||
if (currentShaderProgramID == polymost1CurrentShaderProgramID)
|
||||
|
@ -714,6 +745,55 @@ void polymost_useGlowMapping(char useGlowMapping)
|
|||
}
|
||||
}
|
||||
|
||||
void polymost_activeTexture(GLenum texture)
|
||||
{
|
||||
currentActiveTexture = texture;
|
||||
glad_glActiveTexture(texture);
|
||||
}
|
||||
|
||||
//POGOTODO: replace this and polymost_activeTexture with proper draw call organization
|
||||
void polymost_bindTexture(GLenum target, uint32_t textureID)
|
||||
{
|
||||
if (currentTextureID != textureID ||
|
||||
textureID == 0 ||
|
||||
currentActiveTexture != GL_TEXTURE0 ||
|
||||
videoGetRenderMode() != REND_POLYMOST)
|
||||
{
|
||||
glad_glBindTexture(target, textureID);
|
||||
if (currentActiveTexture == GL_TEXTURE0)
|
||||
{
|
||||
currentTextureID = textureID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void polymost_bindPth(pthtyp *pPth)
|
||||
{
|
||||
vec4f_t texturePosSize = { 0.f, 0.f, 1.f, 1.f };
|
||||
vec2f_t halfTexelSize = { 0.f, 0.f };
|
||||
if (r_useindexedcolortextures &&
|
||||
!(pPth->flags & PTH_HIGHTILE))
|
||||
{
|
||||
Tile tile;
|
||||
char tileIsPacked = tilepacker_getTile(waloff[pPth->picnum] ? pPth->picnum+1 : 0, &tile);
|
||||
//POGO: check the width and height to ensure that the tile hasn't been changed for a user tile that has different dimensions
|
||||
if (tileIsPacked &&
|
||||
(!waloff[pPth->picnum] ||
|
||||
(tile.rect.width == (uint32_t) tilesiz[pPth->picnum].y &&
|
||||
tile.rect.height == (uint32_t) tilesiz[pPth->picnum].x)))
|
||||
{
|
||||
texturePosSize = { tile.rect.u/(float) tilesheetSize,
|
||||
tile.rect.v/(float) tilesheetSize,
|
||||
tile.rect.width/(float) tilesheetSize,
|
||||
tile.rect.height/(float) tilesheetSize };
|
||||
halfTexelSize = tilesheetHalfTexelSize;
|
||||
}
|
||||
}
|
||||
polymost_setTexturePosSize(texturePosSize);
|
||||
polymost_setHalfTexelSize(halfTexelSize);
|
||||
glBindTexture(GL_TEXTURE_2D, pPth->glpic);
|
||||
}
|
||||
|
||||
void useShaderProgram(uint32_t shaderID)
|
||||
{
|
||||
glUseProgram(shaderID);
|
||||
|
@ -754,6 +834,8 @@ void polymost_glinit()
|
|||
}
|
||||
#endif
|
||||
|
||||
//POGOTODO: require a max texture size >= 2048
|
||||
|
||||
persistentStreamBuffer = r_persistentStreamBuffer;
|
||||
drawpolyVertsBufferLength = r_drawpolyVertsBufferLength;
|
||||
|
||||
|
@ -782,13 +864,43 @@ void polymost_glinit()
|
|||
}
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
const char blankTex[] = {255, 0, 0, 0, 255, 0, 0, 0,
|
||||
255, 0, 0, 0, 255, 0, 0, 0};
|
||||
glGenTextures(1, &blankTextureID);
|
||||
glBindTexture(GL_TEXTURE_2D, blankTextureID);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, blankTex);
|
||||
currentTextureID = 0;
|
||||
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &tilesheetSize);
|
||||
tilesheetHalfTexelSize = { 0.5f/tilesheetSize, 0.5f/tilesheetSize };
|
||||
vec2_t maxTexDimensions = { tilesheetSize, tilesheetSize };
|
||||
char allPacked = false;
|
||||
static uint32_t numTilesheets = 0;
|
||||
//POGO: only pack the tilesheets once
|
||||
if (numTilesheets == 0)
|
||||
{
|
||||
// add a blank texture for tileUID 0
|
||||
tilepacker_addTile(0, 2, 2);
|
||||
for (int picnum = 0; picnum < MAXTILES; ++picnum)
|
||||
{
|
||||
tilepacker_addTile(picnum+1, (uint32_t) tilesiz[picnum].y, (uint32_t) tilesiz[picnum].x);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
tilepacker_initTilesheet(numTilesheets, tilesheetSize, tilesheetSize);
|
||||
allPacked = tilepacker_pack(numTilesheets);
|
||||
++numTilesheets;
|
||||
} while (!allPacked && numTilesheets < MAXTILESHEETS);
|
||||
}
|
||||
for (uint32_t i = 0; i < numTilesheets; ++i)
|
||||
{
|
||||
glGenTextures(1, tilesheetTexIDs+i);
|
||||
glBindTexture(GL_TEXTURE_2D, tilesheetTexIDs[i]);
|
||||
uploadtextureindexed(true, {0, 0}, maxTexDimensions, (intptr_t) NULL);
|
||||
}
|
||||
|
||||
const char blankTex[] = {255, 255,
|
||||
255, 255};
|
||||
Tile blankTile;
|
||||
tilepacker_getTile(0, &blankTile);
|
||||
glBindTexture(GL_TEXTURE_2D, tilesheetTexIDs[blankTile.tilesheetID]);
|
||||
uploadtextureindexed(false, {(int32_t) blankTile.rect.u, (int32_t) blankTile.rect.v}, {2, 2}, (intptr_t) blankTex);
|
||||
|
||||
quadVertsID = ids[1];
|
||||
glBindBuffer(GL_ARRAY_BUFFER, quadVertsID);
|
||||
|
@ -890,9 +1002,12 @@ void polymost_glinit()
|
|||
const char* const POLYMOST1_BASIC_VERTEX_SHADER_CODE =
|
||||
"#version 110\n\
|
||||
\n\
|
||||
// output\n\
|
||||
varying vec4 v_color;\n\
|
||||
\n\
|
||||
//u_texturePosSize is the texture position & size packaged into a single vec4 as {pos.x, pos.y, size.x, size.y}\n\
|
||||
uniform vec4 u_texturePosSize;\n\
|
||||
uniform float u_usePalette;\n\
|
||||
\n\
|
||||
const float c_zero = 0.0;\n\
|
||||
const float c_one = 1.0;\n\
|
||||
\n\
|
||||
|
@ -904,6 +1019,11 @@ void polymost_glinit()
|
|||
eyeCoordPosition.xyz /= eyeCoordPosition.w;\n\
|
||||
\n\
|
||||
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;\n\
|
||||
gl_TexCoord[0] = mix(gl_TexCoord[0].xyzw, gl_TexCoord[0].yxzw, u_usePalette);\n\
|
||||
\n\
|
||||
gl_TexCoord[3] = gl_TextureMatrix[3] * gl_MultiTexCoord3;\n\
|
||||
gl_TexCoord[4] = gl_TextureMatrix[4] * gl_MultiTexCoord4;\n\
|
||||
\n\
|
||||
gl_FogFragCoord = abs(eyeCoordPosition.z);\n\
|
||||
//gl_FogFragCoord = clamp((gl_Fog.end-abs(eyeCoordPosition.z))*gl_Fog.scale, c_zero, c_one);\n\
|
||||
\n\
|
||||
|
@ -919,8 +1039,12 @@ void polymost_glinit()
|
|||
//s_palette is the base palette texture where u is the color index\n\
|
||||
uniform sampler2D s_palette;\n\
|
||||
\n\
|
||||
//u_texturePosSize is the texture position & size packaged into a single vec4 as {pos.x, pos.y, size.x, size.y}\n\
|
||||
uniform vec4 u_texturePosSize;\n\
|
||||
uniform vec2 u_halfTexelSize;\n\
|
||||
uniform vec2 u_palswapPos;\n\
|
||||
uniform vec2 u_palswapSize;\n\
|
||||
\n\
|
||||
uniform float u_shade;\n\
|
||||
uniform float u_fogEnabled;\n\
|
||||
\n\
|
||||
|
@ -933,15 +1057,20 @@ void polymost_glinit()
|
|||
const float c_basepalOffset = 0.5/256.0;\n\
|
||||
\n\
|
||||
const float c_zero = 0.0;\n\
|
||||
const float c_one = 1.0;\n\
|
||||
const float c_one = 1.0;\n\
|
||||
const float c_two = 2.0;\n\
|
||||
const vec4 c_vec4_one = vec4(c_one);\n\
|
||||
const float c_wrapThreshold = 0.90;\n\
|
||||
\n\
|
||||
void main()\n\
|
||||
{\n\
|
||||
vec2 texCoord = (gl_TextureMatrix[0] * gl_TexCoord[0]).xy;\n\
|
||||
texCoord = mix(texCoord.xy, texCoord.yx, u_usePalette);\n\
|
||||
//GLSL 130+ could alternatively use texture2DGrad()\n\
|
||||
vec2 transitionBlend = fwidth(floor(gl_TexCoord[0].xy));\n\
|
||||
transitionBlend = fwidth(transitionBlend)+transitionBlend;\n\
|
||||
vec2 texCoord = mix(fract(gl_TexCoord[0].xy), abs(c_one-mod(gl_TexCoord[0].xy+c_one, c_two)), transitionBlend);\n\
|
||||
texCoord = clamp(u_texturePosSize.zw*texCoord, u_halfTexelSize, u_texturePosSize.zw-u_halfTexelSize);\n\
|
||||
vec4 color = texture2D(s_texture, u_texturePosSize.xy+texCoord);\n\
|
||||
\n\
|
||||
vec4 color = texture2D(s_texture, texCoord);\n\
|
||||
float colorIndex = texture2D(s_palswap, u_palswapPos+u_palswapSize*vec2(color.r, u_shade)).r;\n\
|
||||
colorIndex = c_basepalOffset + c_basepalScale*colorIndex;\n\
|
||||
vec4 palettedColor = texture2D(s_palette, vec2(colorIndex, c_zero));\n\
|
||||
|
@ -980,8 +1109,12 @@ void polymost_glinit()
|
|||
uniform sampler2D s_detail;\n\
|
||||
uniform sampler2D s_glow;\n\
|
||||
\n\
|
||||
//u_texturePosSize is the texture position & size packaged into a single vec4 as {pos.x, pos.y, size.x, size.y}\n\
|
||||
uniform vec4 u_texturePosSize;\n\
|
||||
uniform vec2 u_halfTexelSize;\n\
|
||||
uniform vec2 u_palswapPos;\n\
|
||||
uniform vec2 u_palswapSize;\n\
|
||||
\n\
|
||||
uniform float u_shade;\n\
|
||||
uniform float u_fogEnabled;\n\
|
||||
\n\
|
||||
|
@ -997,15 +1130,20 @@ void polymost_glinit()
|
|||
const float c_basepalOffset = 0.5/256.0;\n\
|
||||
\n\
|
||||
const float c_zero = 0.0;\n\
|
||||
const float c_one = 1.0;\n\
|
||||
const float c_one = 1.0;\n\
|
||||
const float c_two = 2.0;\n\
|
||||
const vec4 c_vec4_one = vec4(c_one);\n\
|
||||
const float c_wrapThreshold = 0.95;\n\
|
||||
\n\
|
||||
void main()\n\
|
||||
{\n\
|
||||
vec2 texCoord = (gl_TextureMatrix[0] * gl_TexCoord[0]).xy;\n\
|
||||
texCoord = mix(texCoord.xy, texCoord.yx, u_usePalette);\n\
|
||||
//GLSL 130+ could alternatively use texture2DGrad()\n\
|
||||
vec2 transitionBlend = fwidth(floor(gl_TexCoord[0].xy));\n\
|
||||
transitionBlend = fwidth(transitionBlend)+transitionBlend;\n\
|
||||
vec2 texCoord = mix(fract(gl_TexCoord[0].xy), abs(c_one-mod(gl_TexCoord[0].xy+c_one, c_two)), transitionBlend);\n\
|
||||
texCoord = clamp(u_texturePosSize.zw*texCoord, u_halfTexelSize, u_texturePosSize.zw-u_halfTexelSize);\n\
|
||||
vec4 color = texture2D(s_texture, u_texturePosSize.xy+texCoord);\n\
|
||||
\n\
|
||||
vec4 color = texture2D(s_texture, texCoord);\n\
|
||||
float colorIndex = texture2D(s_palswap, u_palswapPos+u_palswapSize*vec2(color.r, u_shade)).r;\n\
|
||||
colorIndex = c_basepalOffset + c_basepalScale*colorIndex;\n\
|
||||
vec4 palettedColor = texture2D(s_palette, vec2(colorIndex, c_zero));\n\
|
||||
|
@ -1013,7 +1151,7 @@ void polymost_glinit()
|
|||
palettedColor.a = c_one-floor(color.r);\n\
|
||||
color = mix(color, palettedColor, u_usePalette);\n\
|
||||
\n\
|
||||
vec4 detailColor = texture2D(s_detail, (gl_TextureMatrix[3] * gl_TexCoord[0]).xy);\n\
|
||||
vec4 detailColor = texture2D(s_detail, gl_TexCoord[3].xy);\n\
|
||||
detailColor = mix(c_vec4_one, 2.0*detailColor, u_useDetailMapping*detailColor.a);\n\
|
||||
color.rgb *= detailColor.rgb;\n\
|
||||
\n\
|
||||
|
@ -1031,7 +1169,7 @@ void polymost_glinit()
|
|||
color.rgb *= v_color.rgb;\n\
|
||||
color.rgb = mix(gl_Fog.color.rgb, color.rgb, fogFactor);\n\
|
||||
\n\
|
||||
vec4 glowColor = texture2D(s_glow, (gl_TextureMatrix[4] * gl_TexCoord[0]).xy);\n\
|
||||
vec4 glowColor = texture2D(s_glow, gl_TexCoord[4].xy);\n\
|
||||
color.rgb = mix(color.rgb, glowColor.rgb, u_useGlowMapping*glowColor.a*(c_one-u_useColorOnly));\n\
|
||||
\n\
|
||||
color.a *= v_color.a;\n\
|
||||
|
@ -1060,10 +1198,6 @@ void polymost_glinit()
|
|||
glUniform1i(polymost1DetailSamplerLoc, 3);
|
||||
glUniform1i(polymost1GlowSamplerLoc, 4);
|
||||
polymost_setPalswapSize(256, numshades+1);
|
||||
glUniform1f(polymost1UseColorOnlyLoc, polymost1UseColorOnly);
|
||||
glUniform1f(polymost1UsePaletteLoc, polymost1UsePalette);
|
||||
glUniform1f(polymost1UseDetailMappingLoc, polymost1UseDetailMapping);
|
||||
glUniform1f(polymost1UseGlowMappingLoc, polymost1UseGlowMapping);
|
||||
polymost_setCurrentShaderProgram(polymost1BasicShaderProgramID);
|
||||
glUniform1i(polymost1TexSamplerLoc, 0);
|
||||
glUniform1i(polymost1PalSwapSamplerLoc, 1);
|
||||
|
@ -1760,16 +1894,22 @@ void uploadtexture(int32_t doalloc, vec2_t siz, int32_t texfmt,
|
|||
}
|
||||
}
|
||||
|
||||
void uploadtextureindexed(int32_t doalloc, vec2_t siz, intptr_t tile)
|
||||
void uploadtextureindexed(int32_t doalloc, vec2_t offset, vec2_t siz, intptr_t tile)
|
||||
{
|
||||
// don't use mipmaps for indexed color textures
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
|
||||
if (doalloc & 1)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, siz.y, siz.x, 0, GL_RED, GL_UNSIGNED_BYTE, (void*) tile);
|
||||
}
|
||||
else
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, siz.y, siz.x, GL_RED, GL_UNSIGNED_BYTE, (void*) tile);
|
||||
{
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, offset.x, offset.y, siz.y, siz.x, GL_RED, GL_UNSIGNED_BYTE, (void*) tile);
|
||||
}
|
||||
}
|
||||
|
||||
void uploadbasepalette(int32_t basepalnum)
|
||||
|
@ -1806,6 +1946,7 @@ void uploadbasepalette(int32_t basepalnum)
|
|||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
|
||||
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, GL_RGBA8, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, basepalWFullBrightInfo);
|
||||
|
@ -1840,6 +1981,7 @@ void uploadpalswap(int32_t palookupnum)
|
|||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
|
||||
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, GL_RED, PALSWAP_TEXTURE_SIZE, PALSWAP_TEXTURE_SIZE, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
|
||||
|
@ -1915,38 +2057,44 @@ static void gloadtile_art_indexed(int32_t dapic, int32_t dameth, pthtyp *pth, in
|
|||
//POGOTODO: if !glinfo.texnpot, then we could allocate a texture of the pow2 size, and then populate the subportion using buffersubdata func
|
||||
//if (!glinfo.texnpot)
|
||||
|
||||
Tile tile = {};
|
||||
if (waloff[dapic])
|
||||
{
|
||||
if (doalloc)
|
||||
char tileIsPacked = tilepacker_getTile(dapic+1, &tile);
|
||||
if (tileIsPacked &&
|
||||
tile.rect.width == (uint32_t) tsizart.y &&
|
||||
tile.rect.height == (uint32_t) tsizart.x)
|
||||
{
|
||||
pth->glpic = tilesheetTexIDs[tile.tilesheetID];
|
||||
doalloc = false;
|
||||
}
|
||||
else if (doalloc)
|
||||
{
|
||||
glGenTextures(1, (GLuint *)&pth->glpic);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D,pth->glpic);
|
||||
glBindTexture(GL_TEXTURE_2D, pth->glpic);
|
||||
|
||||
uploadtextureindexed(doalloc, siz, waloff[dapic]);
|
||||
if (doalloc)
|
||||
{
|
||||
const GLuint clamp_mode = glinfo.clamptoedge ? GL_CLAMP_TO_EDGE : GL_CLAMP;
|
||||
if (!(dameth & DAMETH_CLAMPED))
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp_if_tile_is_sky(dapic, clamp_mode));
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For sprite textures, clamping looks better than wrapping
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp_mode);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clamp_mode);
|
||||
}
|
||||
}
|
||||
uploadtextureindexed(doalloc, {(int32_t) tile.rect.u, (int32_t) tile.rect.v}, siz, waloff[dapic]);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Force invalid textures to draw something - an almost purely transparency texture
|
||||
//This allows the Z-buffer to be updated for mirrors (which are invalidated textures)
|
||||
pth->glpic = blankTextureID;
|
||||
}
|
||||
|
||||
const GLuint clamp_mode = glinfo.clamptoedge ? GL_CLAMP_TO_EDGE : GL_CLAMP;
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
if (!(dameth & DAMETH_CLAMPED))
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp_if_tile_is_sky(dapic, clamp_mode));
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For sprite textures, clamping looks better than wrapping
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp_mode);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clamp_mode);
|
||||
tilepacker_getTile(0, &tile);
|
||||
pth->glpic = tilesheetTexIDs[tile.tilesheetID];
|
||||
}
|
||||
|
||||
pth->picnum = dapic;
|
||||
|
@ -2122,7 +2270,7 @@ void gloadtile_art(int32_t dapic, int32_t dapal, int32_t tintpalnum, int32_t das
|
|||
}
|
||||
|
||||
if (doalloc) glGenTextures(1,(GLuint *)&pth->glpic); //# of textures (make OpenGL allocate structure)
|
||||
glBindTexture(GL_TEXTURE_2D,pth->glpic);
|
||||
glBindTexture(GL_TEXTURE_2D, pth->glpic);
|
||||
|
||||
fixtransparency(pic,tsiz,siz,dameth);
|
||||
|
||||
|
@ -2438,7 +2586,7 @@ int32_t gloadtile_hi(int32_t dapic,int32_t dapalnum, int32_t facen, hicreplctyp
|
|||
|
||||
if ((doalloc&3)==1)
|
||||
glGenTextures(1, &pth->glpic); //# of textures (make OpenGL allocate structure)
|
||||
glBindTexture(GL_TEXTURE_2D,pth->glpic);
|
||||
glBindTexture(GL_TEXTURE_2D, pth->glpic);
|
||||
|
||||
fixtransparency(pic,tsiz,siz,dameth);
|
||||
|
||||
|
@ -2642,12 +2790,13 @@ static void polymost2_drawVBO(GLenum mode,
|
|||
}
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, (pth && pth->flags & PTH_HASFULLBRIGHT && r_fullbrights) ? pth->ofb->glpic : blankTextureID);
|
||||
//POGO: temporarily swapped out blankTextureID for 0 (as the blank texture has been moved into the dynamic tilesheets)
|
||||
glBindTexture(GL_TEXTURE_2D, (pth && pth->flags & PTH_HASFULLBRIGHT && r_fullbrights) ? pth->ofb->glpic : 0);
|
||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, pth ? pth->glpic : blankTextureID);
|
||||
polymost_bindPth(pth);
|
||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
|
||||
|
||||
|
@ -2903,8 +3052,9 @@ static void polymost_drawpoly(vec2f_t const * const dpxy, int32_t const n, int32
|
|||
// just submit the geometry and don't mess with textures.
|
||||
if (videoGetRenderMode() == REND_POLYMOST)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, pth ? pth->glpic : blankTextureID);
|
||||
polymost_bindPth(pth);
|
||||
|
||||
//POGOTODO: I could move this into bindPth
|
||||
if (pth && !(pth->flags & PTH_INDEXED))
|
||||
polymost_usePaletteIndexing(false);
|
||||
|
||||
|
@ -7324,7 +7474,7 @@ void polymost_fillpolygon(int32_t npoints)
|
|||
glEnable(GL_ALPHA_TEST);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
pthtyp *pth = our_texcache_fetch(DAMETH_NOMASK | (videoGetRenderMode() == REND_POLYMOST && r_useindexedcolortextures ? PTH_INDEXED : 0));
|
||||
glBindTexture(GL_TEXTURE_2D, pth ? pth->glpic : blankTextureID);
|
||||
polymost_bindPth(pth);
|
||||
if (pth && !(pth->flags & PTH_INDEXED))
|
||||
{
|
||||
polymost_usePaletteIndexing(false);
|
||||
|
@ -7403,7 +7553,7 @@ int32_t polymost_drawtilescreen(int32_t tilex, int32_t tiley, int32_t wallnum, i
|
|||
loadedhitile[wallnum>>3] |= (1<<(wallnum&7));
|
||||
usehightile = ousehightile;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, pth ? pth->glpic : blankTextureID);
|
||||
polymost_bindPth(pth);
|
||||
if (pth && !(pth->flags & PTH_INDEXED))
|
||||
{
|
||||
polymost_usePaletteIndexing(false);
|
||||
|
@ -7531,8 +7681,9 @@ int32_t polymost_printext256(int32_t xpos, int32_t ypos, int16_t col, int16_t ba
|
|||
|
||||
if (videoGetRenderMode() < REND_POLYMOST || !in3dmode() || (!polymosttext && gen_font_glyph_tex() < 0))
|
||||
return -1;
|
||||
else
|
||||
glBindTexture(GL_TEXTURE_2D, polymosttext);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, polymosttext);
|
||||
polymost_setTexturePosSize({0, 0, 1, 1});
|
||||
|
||||
polymost_usePaletteIndexing(false);
|
||||
|
||||
|
|
358
source/build/src/tilepacker.cpp
Normal file
358
source/build/src/tilepacker.cpp
Normal file
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
* tilepacker.cpp
|
||||
* A k-d tree based bin packer that organizes rectangular tiles to fit
|
||||
* neatly into one texture.
|
||||
*
|
||||
* Copyright © 2018, Alex Dawson. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "compat.h"
|
||||
#include "build.h"
|
||||
#include "tilepacker.h"
|
||||
|
||||
typedef struct TreeNode
|
||||
{
|
||||
struct TreeNode *pParent, *pChild0, *pChild1;
|
||||
TileRect rect;
|
||||
uint32_t maxSide;
|
||||
uint32_t tileUID;
|
||||
} TreeNode;
|
||||
|
||||
// allocate all the memory we could ever need up front to avoid dynamic allocation
|
||||
#define NUM_NON_ROOT_NODES MAXPACKEDTILES*2
|
||||
#define NUM_NODES NUM_NON_ROOT_NODES+MAXTILESHEETS
|
||||
TreeNode nodes[NUM_NODES];
|
||||
uint32_t heapNodes = 0;
|
||||
uint32_t nextTreeNodeIndex = NUM_NON_ROOT_NODES-1;
|
||||
|
||||
Tile tiles[MAXPACKEDTILES];
|
||||
|
||||
// node rejection queue implemented using a circular buffer
|
||||
#define MAX_REJECTS (MAXPACKEDTILES-1)
|
||||
TreeNode rejectQueue[MAX_REJECTS];
|
||||
uint32_t rejectQueueHeadIndex = 0;
|
||||
uint32_t numRejected = 0;
|
||||
|
||||
static void maxheap_bubbleUp(uint32_t nodeIndex)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (nodeIndex == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t parentIndex = (nodeIndex-1)/2;
|
||||
if (nodes[parentIndex].maxSide >= nodes[nodeIndex].maxSide)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// bubble up
|
||||
TreeNode temp = nodes[nodeIndex];
|
||||
nodes[nodeIndex] = nodes[parentIndex];
|
||||
nodes[parentIndex] = temp;
|
||||
|
||||
nodeIndex = parentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
static void maxheap_bubbleDown(uint32_t nodeIndex)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
uint32_t largestChildIndex = 2*nodeIndex + 1;
|
||||
if (largestChildIndex >= heapNodes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t rightChildIndex = largestChildIndex+1;
|
||||
if (rightChildIndex < heapNodes &&
|
||||
nodes[rightChildIndex].maxSide > nodes[largestChildIndex].maxSide)
|
||||
{
|
||||
largestChildIndex = rightChildIndex;
|
||||
}
|
||||
|
||||
if (nodes[largestChildIndex].maxSide <= nodes[nodeIndex].maxSide)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// bubble down
|
||||
TreeNode temp = nodes[nodeIndex];
|
||||
nodes[nodeIndex] = nodes[largestChildIndex];
|
||||
nodes[largestChildIndex] = temp;
|
||||
|
||||
nodeIndex = largestChildIndex;
|
||||
}
|
||||
}
|
||||
|
||||
static TreeNode* maxheap_pop()
|
||||
{
|
||||
if (heapNodes == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// swap the root and the last node
|
||||
TreeNode temp = nodes[0];
|
||||
nodes[0] = nodes[heapNodes-1];
|
||||
nodes[heapNodes-1] = temp;
|
||||
--heapNodes;
|
||||
|
||||
maxheap_bubbleDown(0);
|
||||
|
||||
return nodes+heapNodes;
|
||||
}
|
||||
|
||||
static TreeNode* maxheap_reserveNode(TreeNode *pParent,
|
||||
TreeNode *pChild0,
|
||||
TreeNode *pChild1,
|
||||
TileRect rectangle,
|
||||
uint32_t tileUID)
|
||||
{
|
||||
if (heapNodes == nextTreeNodeIndex+1)
|
||||
{
|
||||
// our tree and heap are going to collide
|
||||
#ifdef DEBUGGINGAIDS
|
||||
OSD_Printf("tilepacker: maxheap_reserveNode(): out of nodes\n");
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nodes[heapNodes] = {(TreeNode*) pParent,
|
||||
(TreeNode*) pChild0,
|
||||
(TreeNode*) pChild1,
|
||||
rectangle,
|
||||
rectangle.width >= rectangle.height ? rectangle.width : rectangle.height,
|
||||
tileUID};
|
||||
++heapNodes;
|
||||
|
||||
return nodes+heapNodes-1;
|
||||
}
|
||||
|
||||
static TreeNode* kdtree_reserveNode(TreeNode *pParent,
|
||||
TreeNode *pChild0,
|
||||
TreeNode *pChild1,
|
||||
TileRect rectangle,
|
||||
uint32_t tileUID)
|
||||
{
|
||||
if (nextTreeNodeIndex == heapNodes-1)
|
||||
{
|
||||
// our tree and heap are going to collide
|
||||
#ifdef DEBUGGINGAIDS
|
||||
OSD_Printf("tilepacker: kdtree_reserveNode(): out of nodes\n");
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nodes[nextTreeNodeIndex] = {(TreeNode*) pParent,
|
||||
(TreeNode*) pChild0,
|
||||
(TreeNode*) pChild1,
|
||||
rectangle,
|
||||
rectangle.width >= rectangle.height ? rectangle.width : rectangle.height,
|
||||
tileUID};
|
||||
--nextTreeNodeIndex;
|
||||
|
||||
return nodes+nextTreeNodeIndex+1;
|
||||
}
|
||||
|
||||
static char kdtree_add(uint32_t treeIndex, TreeNode *pNode)
|
||||
{
|
||||
TreeNode *pCurrentNode = nodes+NUM_NODES-treeIndex-1;
|
||||
while (true)
|
||||
{
|
||||
// is this node large enough to contain the currentNode?
|
||||
if (pCurrentNode->rect.width >= pNode->rect.width &&
|
||||
pCurrentNode->rect.height >= pNode->rect.height)
|
||||
{
|
||||
if (pCurrentNode->tileUID != (uint32_t) -1)
|
||||
{
|
||||
// if we're not a leaf node, continue to tunnel down until we reach a leaf on the 0-side of the tree
|
||||
pCurrentNode = pCurrentNode->pChild0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// otherwise, we have the node we want to split to insert our new node
|
||||
break;
|
||||
}
|
||||
|
||||
// climb out until we find an unexplored 1-side branch to tunnel down
|
||||
TreeNode *lastNode;
|
||||
do
|
||||
{
|
||||
lastNode = pCurrentNode;
|
||||
pCurrentNode = pCurrentNode->pParent;
|
||||
|
||||
if (pCurrentNode == NULL)
|
||||
{
|
||||
// we've fully explored the tree and asked for the root's parent
|
||||
return false;
|
||||
}
|
||||
} while (pCurrentNode->pChild1 == lastNode || !pCurrentNode->pChild1);
|
||||
|
||||
pCurrentNode = pCurrentNode->pChild1;
|
||||
}
|
||||
|
||||
// assign the empty leaf node the tileUID we want to add, then create children to split the node's remaining space
|
||||
pCurrentNode->tileUID = pNode->tileUID;
|
||||
tiles[pNode->tileUID].tilesheetID = treeIndex;
|
||||
tiles[pNode->tileUID].rect = {pCurrentNode->rect.u,
|
||||
pCurrentNode->rect.v,
|
||||
pNode->rect.width,
|
||||
pNode->rect.height};
|
||||
|
||||
uint32_t rightSideWidth = pCurrentNode->rect.width - pNode->rect.width;
|
||||
uint32_t bottomSideHeight = pCurrentNode->rect.height - pNode->rect.height;
|
||||
TileRect rect0 = {pCurrentNode->rect.u+pNode->rect.width, pCurrentNode->rect.v, rightSideWidth, pCurrentNode->rect.height};
|
||||
TileRect rect1 = {pCurrentNode->rect.u, pCurrentNode->rect.v+pNode->rect.height, pNode->rect.width, bottomSideHeight};
|
||||
// decide which way to split
|
||||
if (rightSideWidth < bottomSideHeight)
|
||||
{
|
||||
//POGOTODO: instead of creating two new children and having the tree contain filled nodes
|
||||
// I should usurp the place of pCurrentNode with the largest child,
|
||||
// since the smaller child can always be contained within the larger one.
|
||||
// This requires an additional sort/bubbling step and always splitting width/height
|
||||
// based on level rather than max in order for the organization to work.
|
||||
|
||||
// we'll have a more width-confined space to our right, so chop it off horizontally
|
||||
// rather than create a tall, potentially narrow empty area to have to fill later
|
||||
rect0 = {pCurrentNode->rect.u, pCurrentNode->rect.v+pNode->rect.height, pCurrentNode->rect.width, bottomSideHeight};
|
||||
rect1 = {pCurrentNode->rect.u+pNode->rect.width, pCurrentNode->rect.v, rightSideWidth, pNode->rect.height};
|
||||
}
|
||||
// it's important that the larger area become child0
|
||||
pCurrentNode->pChild0 = kdtree_reserveNode(pCurrentNode,
|
||||
NULL,
|
||||
NULL,
|
||||
rect0,
|
||||
-1);
|
||||
pCurrentNode->pChild1 = kdtree_reserveNode(pCurrentNode,
|
||||
NULL,
|
||||
NULL,
|
||||
rect1,
|
||||
-1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static char rejectQueue_add(TreeNode *pNode)
|
||||
{
|
||||
if (numRejected >= MAX_REJECTS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
rejectQueue[(rejectQueueHeadIndex+numRejected) % MAX_REJECTS] = *pNode;
|
||||
++numRejected;
|
||||
return true;
|
||||
}
|
||||
|
||||
static TreeNode* rejectQueue_remove()
|
||||
{
|
||||
if (numRejected == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
--numRejected;
|
||||
TreeNode* pNode = rejectQueue+rejectQueueHeadIndex;
|
||||
rejectQueueHeadIndex = (rejectQueueHeadIndex+1) % MAX_REJECTS;
|
||||
return pNode;
|
||||
}
|
||||
|
||||
/*static void tilepacker_deleteTree()
|
||||
{
|
||||
//POGOTODO: this
|
||||
}*/
|
||||
|
||||
void tilepacker_initTilesheet(uint32_t tilesheetID, uint32_t tilesheetWidth, uint32_t tilesheetHeight)
|
||||
{
|
||||
//POGOTODO: delete the tree if it's already been initialized
|
||||
|
||||
nodes[NUM_NODES-tilesheetID-1] = {(TreeNode*) 0,
|
||||
(TreeNode*) 0,
|
||||
(TreeNode*) 0,
|
||||
{0, 0, tilesheetWidth, tilesheetHeight},
|
||||
tilesheetWidth >= tilesheetHeight ? tilesheetWidth : tilesheetHeight,
|
||||
(uint32_t) -1}; // use the maximum uint32_t to indicate this node is empty space
|
||||
}
|
||||
|
||||
void tilepacker_addTile(uint32_t tileUID, uint32_t tileWidth, uint32_t tileHeight)
|
||||
{
|
||||
if (nextTreeNodeIndex < heapNodes)
|
||||
{
|
||||
// cannot reserve any more tiles!
|
||||
#ifdef DEBUGGINGAIDS
|
||||
OSD_Printf("tilepacker: tilepacker_addTile(): out of nodes\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (tileWidth == 0 ||
|
||||
tileHeight == 0)
|
||||
{
|
||||
// don't allow adding tiles with a width or height of 0
|
||||
return;
|
||||
}
|
||||
|
||||
maxheap_reserveNode((TreeNode*) 0,
|
||||
(TreeNode*) 0,
|
||||
(TreeNode*) 0,
|
||||
{0, 0, tileWidth, tileHeight},
|
||||
tileUID);
|
||||
maxheap_bubbleUp(heapNodes-1);
|
||||
}
|
||||
|
||||
char tilepacker_pack(uint32_t tilesheetID)
|
||||
{
|
||||
if (tilesheetID >= MAXTILESHEETS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t numLeft = numRejected; numLeft > 0; --numLeft)
|
||||
{
|
||||
TreeNode *pNode = rejectQueue_remove();
|
||||
char success = kdtree_add(tilesheetID, pNode);
|
||||
if (!success)
|
||||
{
|
||||
rejectQueue_add(pNode);
|
||||
}
|
||||
}
|
||||
for (TreeNode *pNode = maxheap_pop(); pNode != NULL; pNode = maxheap_pop())
|
||||
{
|
||||
char success = kdtree_add(tilesheetID, pNode);
|
||||
if (!success)
|
||||
{
|
||||
rejectQueue_add(pNode);
|
||||
}
|
||||
}
|
||||
|
||||
return numRejected == 0;
|
||||
}
|
||||
|
||||
void tilepacker_discardRejects()
|
||||
{
|
||||
numRejected = 0;
|
||||
}
|
||||
|
||||
char tilepacker_getTile(uint32_t tileUID, Tile *pOutput)
|
||||
{
|
||||
if (tileUID >= MAXPACKEDTILES)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Tile tile = tiles[tileUID];
|
||||
if (tile.rect.width == 0)
|
||||
{
|
||||
// that tileUID has not been packed or didn't fit
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pOutput)
|
||||
{
|
||||
*pOutput = tile;
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -1017,6 +1017,7 @@ int32_t polymost_voxdraw(voxmodel_t *m, const uspritetype *tspr)
|
|||
glBindTexture(GL_TEXTURE_2D, m->texid[globalpal]);
|
||||
|
||||
polymost_usePaletteIndexing(false);
|
||||
polymost_setTexturePosSize({ 0.f, 0.f, 1.f, 1.f });
|
||||
|
||||
glBegin(GL_QUADS); // {{{
|
||||
|
||||
|
|
Loading…
Reference in a new issue