mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-30 16:10:53 +00:00
1ea74c6269
This was caused by an out-by one error when setting up the skin: if cmap was 0 then the gl_skin struct would be taken from index -1 of the array and thus cause all sorts of grief.
268 lines
6.6 KiB
C
268 lines
6.6 KiB
C
/*
|
|
glsl_skin.c
|
|
|
|
GLSL Skin support
|
|
|
|
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
|
|
|
|
Author: Bill Currie <bill@taniwha.org>
|
|
Date: 2012/1/23
|
|
|
|
This program 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.
|
|
|
|
This program 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 this program; if not, write to:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_STRING_H
|
|
# include "string.h"
|
|
#endif
|
|
#ifdef HAVE_STRINGS_H
|
|
# include "strings.h"
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "QF/cvar.h"
|
|
#include "QF/image.h"
|
|
#include "QF/model.h"
|
|
#include "QF/skin.h"
|
|
#include "QF/sys.h"
|
|
#include "QF/vid.h"
|
|
|
|
#include "QF/GL/defines.h"
|
|
#include "QF/GL/funcs.h"
|
|
#include "QF/GL/qf_textures.h"
|
|
|
|
#include "mod_internal.h"
|
|
#include "r_internal.h"
|
|
|
|
typedef struct {
|
|
tex_t *tex;
|
|
tex_t *fb_tex;
|
|
qboolean fb;
|
|
} glskin_t;
|
|
|
|
static int skin_textures;
|
|
static int skin_fb_textures;
|
|
static byte skin_cmap[MAX_TRANSLATIONS][256];
|
|
|
|
static glskin_t skins[MAX_TRANSLATIONS];
|
|
static glskin_t player_skin;
|
|
|
|
static void
|
|
do_fb_skin (glskin_t *s)
|
|
{
|
|
int size = s->tex->width * s->tex->height;
|
|
|
|
s->fb_tex = realloc (s->fb_tex, field_offset(tex_t, data[size]));
|
|
s->fb_tex->width = s->tex->width;
|
|
s->fb_tex->height = s->tex->height;
|
|
s->fb_tex->format = tex_palette;
|
|
s->fb_tex->palette = vid.palette;
|
|
s->fb = Mod_CalcFullbright (s->tex->data, s->fb_tex->data, size);
|
|
}
|
|
|
|
void
|
|
gl_Skin_SetPlayerSkin (int width, int height, const byte *data)
|
|
{
|
|
int size = width * height;
|
|
glskin_t *s;
|
|
|
|
s = &player_skin;
|
|
s->tex = realloc (s->tex, field_offset(tex_t, data[size]));
|
|
s->tex->width = width;
|
|
s->tex->height = height;
|
|
s->tex->format = tex_palette;
|
|
s->tex->palette = vid.palette;
|
|
memcpy (s->tex->data, data, size);
|
|
|
|
do_fb_skin (s);
|
|
}
|
|
|
|
static void
|
|
build_skin_8 (tex_t *tex, int texnum, byte *translate,
|
|
unsigned scaled_width, unsigned scaled_height, qboolean alpha)
|
|
{
|
|
// Improvements should be mirrored in GL_ResampleTexture in gl_textures.c
|
|
byte *inrow;
|
|
byte pixels[512 * 256], *out;
|
|
unsigned int i, j;
|
|
unsigned int frac, fracstep;
|
|
|
|
out = pixels;
|
|
memset (pixels, 0, sizeof (pixels));
|
|
fracstep = tex->width * 0x10000 / scaled_width;
|
|
for (i = 0; i < scaled_height; i++, out += scaled_width) {
|
|
inrow = tex->data + tex->width * (i * tex->height / scaled_height);
|
|
frac = fracstep >> 1;
|
|
for (j = 0; j < scaled_width; j++) {
|
|
out[j] = translate[inrow[frac >> 16]];
|
|
frac += fracstep;
|
|
}
|
|
}
|
|
|
|
GL_Upload8_EXT ((byte *) pixels, scaled_width, scaled_height, false,
|
|
alpha);
|
|
}
|
|
|
|
static void
|
|
build_skin_32 (tex_t *tex, int texnum, byte *translate,
|
|
unsigned scaled_width, unsigned scaled_height, qboolean alpha)
|
|
{
|
|
// Improvements should be mirrored in GL_ResampleTexture in gl_textures.c
|
|
byte *inrow;
|
|
unsigned i, j;
|
|
int samples = alpha ? gl_alpha_format : gl_solid_format;
|
|
unsigned frac, fracstep;
|
|
byte pixels[512 * 256 * 4], *out, *pal;
|
|
byte c;
|
|
|
|
out = pixels;
|
|
memset (pixels, 0, sizeof (pixels));
|
|
fracstep = tex->width * 0x10000 / scaled_width;
|
|
for (i = 0; i < scaled_height; i++) {
|
|
inrow = tex->data + tex->width * (i * tex->height / scaled_height);
|
|
frac = fracstep >> 1;
|
|
for (j = 0; j < scaled_width; j++) {
|
|
c = translate[inrow[frac >> 16]];
|
|
pal = vid.palette + c * 3;
|
|
*out++ = *pal++;
|
|
*out++ = *pal++;
|
|
*out++ = *pal++;
|
|
*out++ = (alpha && c == 255) ? 0 : 255;
|
|
frac += fracstep;
|
|
}
|
|
}
|
|
|
|
qfglBindTexture (GL_TEXTURE_2D, texnum);
|
|
qfglTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0,
|
|
GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
|
|
|
qfglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
qfglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
if (gl_Anisotropy)
|
|
qfglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
|
|
gl_aniso);
|
|
}
|
|
|
|
static void
|
|
build_skin (skin_t *skin, int cmap)
|
|
{
|
|
glskin_t *s;
|
|
unsigned scaled_width, scaled_height;
|
|
int texnum, fb_texnum;
|
|
|
|
// FIXME deek: This 512x256 limit sucks!
|
|
scaled_width = min (gl_max_size->int_val, 512);
|
|
scaled_height = min (gl_max_size->int_val, 256);
|
|
|
|
// allow users to crunch sizes down even more if they want
|
|
scaled_width >>= gl_playermip->int_val;
|
|
scaled_height >>= gl_playermip->int_val;
|
|
scaled_width = max (scaled_width, 1);
|
|
scaled_height = max (scaled_height, 1);
|
|
|
|
s = skins + cmap;
|
|
if (!s->tex)
|
|
s = &player_skin;
|
|
if (!s->tex) // we haven't loaded the player model yet
|
|
return;
|
|
|
|
texnum = skin_textures + cmap;
|
|
fb_texnum = 0;
|
|
if (s->fb)
|
|
fb_texnum = skin_fb_textures + cmap;
|
|
if (skin) {
|
|
skin->texnum = texnum;
|
|
skin->auxtex = fb_texnum;
|
|
}
|
|
if (vid.is8bit) {
|
|
build_skin_8 (s->tex, texnum, skin_cmap[cmap],
|
|
scaled_width, scaled_height, false);
|
|
if (s->fb && s->fb_tex)
|
|
build_skin_8 (s->fb_tex, fb_texnum, skin_cmap[cmap],
|
|
scaled_width, scaled_height, true);
|
|
} else {
|
|
build_skin_32 (s->tex, texnum, skin_cmap[cmap],
|
|
scaled_width, scaled_height, false);
|
|
if (s->fb && s->fb_tex)
|
|
build_skin_32 (s->fb_tex, fb_texnum, skin_cmap[cmap],
|
|
scaled_width, scaled_height, true);
|
|
}
|
|
}
|
|
|
|
void
|
|
gl_Skin_ProcessTranslation (int cmap, const byte *translation)
|
|
{
|
|
int changed;
|
|
|
|
// simplify cmap usage (texture offset/array index)
|
|
cmap--;
|
|
// skip over the colormap (GL can't use it) to the translated palette
|
|
translation += VID_GRADES * 256;
|
|
changed = memcmp (skin_cmap[cmap], translation, 256);
|
|
memcpy (skin_cmap[cmap], translation, 256);
|
|
if (!changed)
|
|
return;
|
|
build_skin (0, cmap);
|
|
}
|
|
|
|
void
|
|
gl_Skin_SetupSkin (skin_t *skin, int cmap)
|
|
{
|
|
int changed;
|
|
glskin_t *s;
|
|
|
|
skin->texnum = 0;
|
|
skin->auxtex = 0;
|
|
if (!cmap) {
|
|
return;
|
|
}
|
|
// simplify cmap usage (texture offset/array index)
|
|
cmap--;
|
|
s = skins + cmap;
|
|
changed = (s->tex != skin->texels);
|
|
s->tex = skin->texels;
|
|
if (!changed) {
|
|
skin->texnum = skin_textures + cmap;
|
|
if (s->fb)
|
|
skin->auxtex = skin_fb_textures + cmap;
|
|
return;
|
|
}
|
|
if (s->tex)
|
|
do_fb_skin (s);
|
|
build_skin (skin, cmap);
|
|
}
|
|
|
|
void
|
|
gl_Skin_InitTranslations (void)
|
|
{
|
|
}
|
|
|
|
int
|
|
gl_Skin_Init_Textures (int base)
|
|
{
|
|
skin_textures = base;
|
|
base += MAX_TRANSLATIONS;
|
|
skin_fb_textures = base;
|
|
base += MAX_TRANSLATIONS;
|
|
return base;
|
|
}
|