mirror of
https://github.com/nzp-team/dquakeplus.git
synced 2025-01-31 11:40:36 +00:00
add a median cut implementation for converting wad3 8bpp textures to 4bpp
This commit is contained in:
parent
070e651d7d
commit
b2c43cbe5a
4 changed files with 289 additions and 13 deletions
|
@ -29,7 +29,7 @@ void GL_Upload8(int texture_index, const byte *data, int width, int height);
|
|||
void GL_Upload16(int texture_index, const byte *data, int width, int height);
|
||||
int GL_LoadTexture(const char *identifier, int width, int height, const byte *data, qboolean stretch_to_power_of_two, int filter, int mipmap_level);
|
||||
// CLUT4
|
||||
int GL_LoadTexture4(const char *identifier, unsigned int width, unsigned int height, const byte *data, int filter);
|
||||
int GL_LoadTexture4(const char *identifier, unsigned int width, unsigned int height, const byte *data, int filter, qboolean swizzled);
|
||||
|
||||
int GL_LoadTextureLM (const char *identifier, int width, int height, const byte *data, int bpp, int filter, qboolean update, int forcopy);
|
||||
int GL_LoadImages (const char *identifier, int width, int height, const byte *data, qboolean stretch_to_power_of_two, int filter, int mipmap_level, int bpp);
|
||||
|
@ -42,6 +42,8 @@ void GL_GetPixelsBGR (byte *buffer, int width, int height, int i);
|
|||
void GL_GetPixelsRGB (byte *buffer, int width, int height, int i);
|
||||
void GL_GetPixelsRGBA(byte *buffer, int width, int height, int i);
|
||||
|
||||
void swizzle_fast(u8* out, const u8* in, unsigned int width, unsigned int height);
|
||||
|
||||
|
||||
#define PAL_RGB 24
|
||||
#define PAL_RGBA 32
|
||||
|
@ -329,7 +331,7 @@ void EmitSkyPolys (msurface_t *fa);
|
|||
void EmitReflectivePolys (msurface_t *fa);
|
||||
void EmitScrollPolys (msurface_t *fa);
|
||||
void EmitBothSkyLayers (msurface_t *fa);
|
||||
void EmitUnderWaterPolys (void);
|
||||
// void EmitUnderWaterPolys (void);
|
||||
void EmitDetailPolys (void);
|
||||
void R_DrawSkyChain (msurface_t *s);
|
||||
int R_FrustumCheckBox (vec3_t mins, vec3_t maxs);
|
||||
|
@ -418,3 +420,6 @@ int D_DrawParticleBuffered (psp_particle* vertices, particle2_t *pparticl
|
|||
|
||||
|
||||
extern int zombie_skins[2][2];
|
||||
|
||||
|
||||
extern int faces_rejected, faces_checked, faces_clipped;
|
|
@ -97,7 +97,6 @@ typedef struct
|
|||
|
||||
int loadtextureimage (char* filename, int matchwidth, int matchheight, qboolean complain, int filter);
|
||||
void VID_SetPalette4(unsigned char* clut4pal);
|
||||
int vid_palmode;
|
||||
|
||||
#define MAX_GLTEXTURES 1024
|
||||
gltexture_t gltextures[MAX_GLTEXTURES];
|
||||
|
@ -116,7 +115,6 @@ void GL_Bind4Part(int texture_index)
|
|||
{
|
||||
const gltexture_t& texture = gltextures[texture_index];
|
||||
VID_SetPalette4(texture.palette);
|
||||
vid_palmode = GU_PSM_T4;
|
||||
|
||||
sceGuTexMode(texture.format, 0, 0, GU_FALSE);
|
||||
|
||||
|
@ -209,7 +207,10 @@ void GL_Bind (int texture_index)
|
|||
if (last_palette_wasnt_tx == qtrue)
|
||||
VID_SetPaletteTX();
|
||||
|
||||
vid_palmode = GU_PSM_T8;
|
||||
if (texture.format == GU_PSM_T4) {
|
||||
VID_SetPalette4(texture.palette);
|
||||
}
|
||||
|
||||
sceGuTexMode(texture.format, texture.mipmaps , 0, texture.swizzle);
|
||||
|
||||
// Set the Texture filter.
|
||||
|
@ -1712,7 +1713,7 @@ void swizzle(u8* out, const u8* in, unsigned int width, unsigned int height)
|
|||
swizzle_fast
|
||||
================
|
||||
*/
|
||||
static void swizzle_fast(u8* out, const u8* in, unsigned int width, unsigned int height)
|
||||
void swizzle_fast(u8* out, const u8* in, unsigned int width, unsigned int height)
|
||||
{
|
||||
unsigned int blockx, blocky;
|
||||
unsigned int j;
|
||||
|
@ -3338,13 +3339,12 @@ void GL_Upload4(int texture_index, const byte *data, int width, int height)
|
|||
|
||||
memcpy(texture.ram, data, buffer_size);
|
||||
memcpy(texture.palette, data + buffer_size, 16 * 4);
|
||||
int i;
|
||||
|
||||
// Flush the data cache.
|
||||
sceKernelDcacheWritebackRange(texture.ram, buffer_size);
|
||||
}
|
||||
|
||||
int GL_LoadTexture4(const char *identifier, unsigned int width, unsigned int height, const byte *data, int filter)
|
||||
int GL_LoadTexture4(const char *identifier, unsigned int width, unsigned int height, const byte *data, int filter, qboolean swizzled)
|
||||
{
|
||||
int texture_index = -1;
|
||||
|
||||
|
@ -3390,6 +3390,7 @@ int GL_LoadTexture4(const char *identifier, unsigned int width, unsigned int hei
|
|||
texture.original_width = texture.width = width;
|
||||
texture.original_height = texture.height = height;
|
||||
texture.stretch_to_power_of_two = qfalse;
|
||||
texture.swizzle = swizzled;
|
||||
|
||||
// Fill in the texture description.
|
||||
texture.format = GU_PSM_T4;
|
||||
|
|
|
@ -573,7 +573,7 @@ void Mod_LoadTextures (lump_t *l)
|
|||
}
|
||||
|
||||
if (!f) {
|
||||
Con_Printf("Loading texture %s as WAD3\n", mt->name); // didn't find the texture in the folder
|
||||
Con_Printf("Loading texture %s as WAD3, %dx%d\n", mt->name, mt->width, mt->height); // didn't find the texture in the folder
|
||||
|
||||
// naievil -- try to push wad3 loading
|
||||
int index = WAD3_LoadTexture(mt);
|
||||
|
@ -611,7 +611,7 @@ void Mod_LoadTextures (lump_t *l)
|
|||
w = *((int*)(f + 4));
|
||||
h = *((int*)(f + 8));
|
||||
|
||||
tx->gl_texturenum = GL_LoadTexture4(mt->name, w, h, (byte*)(f + 16), GU_LINEAR);
|
||||
tx->gl_texturenum = GL_LoadTexture4(mt->name, w, h, (byte*)(f + 16), GU_LINEAR, qfalse);
|
||||
mapTextureNameList.push_back(tx->gl_texturenum);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ extern "C"
|
|||
#include "../quakedef.h"
|
||||
}
|
||||
#include <pspgu.h>
|
||||
|
||||
#include <malloc.h>
|
||||
#include <list>
|
||||
|
||||
std::list<FILE*> UnloadFileList;
|
||||
|
@ -189,6 +189,276 @@ int ConvertWad3ToRGBA(miptex_t *tex)
|
|||
return index;
|
||||
}
|
||||
|
||||
struct colentry_t {
|
||||
byte r;
|
||||
byte g;
|
||||
byte b;
|
||||
byte a;
|
||||
unsigned short pixcount;
|
||||
};
|
||||
colentry_t colentries[256];
|
||||
typedef byte bucket_t[256];
|
||||
|
||||
bucket_t buckets[16];
|
||||
int bucket_counts[16];
|
||||
int num_buckets = 0;
|
||||
|
||||
byte byte_to_halfbyte_map[256];
|
||||
|
||||
void add_to_bucket(int bucket, int color) {
|
||||
buckets[bucket][bucket_counts[bucket]] = color;
|
||||
bucket_counts[bucket]++;
|
||||
}
|
||||
|
||||
void remove_last_from_bucket(int bucket) {
|
||||
bucket_counts[bucket]--;
|
||||
buckets[bucket][bucket_counts[bucket]] = 0;
|
||||
}
|
||||
|
||||
|
||||
void init_buckets() {
|
||||
memset(buckets, 0, sizeof(buckets));
|
||||
memset(bucket_counts, 0, sizeof(bucket_counts));
|
||||
memset(colentries, 0, sizeof(colentries));
|
||||
memset(byte_to_halfbyte_map, 0, sizeof(byte_to_halfbyte_map));
|
||||
num_buckets = 0;
|
||||
}
|
||||
|
||||
int get_new_bucket() {
|
||||
// Con_Printf("Create new bucket %d\n", num_buckets);
|
||||
num_buckets += 1;
|
||||
return num_buckets - 1;
|
||||
}
|
||||
|
||||
colentry_t * color_of_bucket(int bucket, int index) {
|
||||
return &(colentries[buckets[bucket][index]]);
|
||||
}
|
||||
|
||||
int get_color_index(int bucket, int index) {
|
||||
return buckets[bucket][index];
|
||||
}
|
||||
|
||||
void sort_and_cut_bucket(int current_bucket, int depth, int target) {
|
||||
if (num_buckets >= target) {
|
||||
// Con_Printf("quit due to too many buckets %d / %d\n", num_buckets, target);
|
||||
return;
|
||||
}
|
||||
|
||||
int count = bucket_counts[current_bucket];
|
||||
// find the highest range and count pixels in bucket
|
||||
byte minR = 255;
|
||||
byte maxR = 0;
|
||||
byte minG = 255;
|
||||
byte maxG = 0;
|
||||
byte minB = 255;
|
||||
byte maxB = 0;
|
||||
int pixelsInBucket = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
colentry_t * col = color_of_bucket(current_bucket, i);
|
||||
pixelsInBucket += col->pixcount;
|
||||
if (col->r < minR) minR = col->r;
|
||||
if (col->r > maxR) maxR = col->r;
|
||||
if (col->g < minG) minG = col->g;
|
||||
if (col->g > maxG) maxG = col->g;
|
||||
if (col->b < minB) minB = col->b;
|
||||
if (col->b > maxB) maxB = col->b;
|
||||
}
|
||||
int rangeR = maxR - minR;
|
||||
int rangeG = maxG - minG;
|
||||
int rangeB = maxB - minB;
|
||||
|
||||
// 0 = r, 1 = g, 2 = b
|
||||
int sortBy = 0;
|
||||
if (rangeG > rangeR && rangeG > rangeB) sortBy = 1;
|
||||
if (rangeB > rangeR && rangeB > rangeG) sortBy = 2;
|
||||
|
||||
// Con_Printf("%d: depth: %d, count: %d, pixels: %d, sortBy: %d\n", current_bucket, depth, count, pixelsInBucket, sortBy);
|
||||
// bubble sort, replace with comb sort or heap sort
|
||||
bool sorted = false;
|
||||
while (!sorted) {
|
||||
sorted = true; // this will change if a swap is done
|
||||
for (int i = 0; i < (count - 1); i++) {
|
||||
colentry_t * col1 = color_of_bucket(current_bucket, i);
|
||||
colentry_t * col2 = color_of_bucket(current_bucket, i + 1);
|
||||
bool swap = false;
|
||||
switch (sortBy) {
|
||||
case 0: swap = col1->r > col2->r; break;
|
||||
case 1: swap = col1->g > col2->g; break;
|
||||
case 2: swap = col1->b > col2->b; break;
|
||||
}
|
||||
if (swap) {
|
||||
byte prevval = buckets[current_bucket][i];
|
||||
byte nextval = buckets[current_bucket][i + 1];
|
||||
buckets[current_bucket][i] = nextval;
|
||||
buckets[current_bucket][i + 1] = prevval;
|
||||
sorted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// median cut
|
||||
int pixelsCounted = 0;
|
||||
int split = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (pixelsCounted > pixelsInBucket * 0.5) {
|
||||
split = i;
|
||||
break;
|
||||
}
|
||||
byte_to_halfbyte_map[get_color_index(current_bucket, i)] = current_bucket;
|
||||
colentry_t * col = color_of_bucket(current_bucket, i);
|
||||
pixelsCounted += col->pixcount;
|
||||
}
|
||||
|
||||
int next_bucket = get_new_bucket();
|
||||
for (int i = count-1; i >= split; i--) {
|
||||
byte colorindex = get_color_index(current_bucket, i);
|
||||
byte_to_halfbyte_map[colorindex] = next_bucket;
|
||||
add_to_bucket(next_bucket, colorindex);
|
||||
remove_last_from_bucket(current_bucket);
|
||||
}
|
||||
|
||||
if (depth >= 3) return;
|
||||
|
||||
sort_and_cut_bucket(current_bucket, depth + 1, target);
|
||||
sort_and_cut_bucket(next_bucket, depth + 1, target);
|
||||
}
|
||||
|
||||
int ConvertWad3ToClut4(miptex_t *tex)
|
||||
{
|
||||
// Check that texture has data
|
||||
if (!tex->offsets[0]) {
|
||||
Sys_Error("ConvertWad3ToRGBA: tex->offsets[0] == 0");
|
||||
}
|
||||
|
||||
// Get pointers to WAD3 data and palette
|
||||
byte* wadData = ((byte*)tex) + tex->offsets[0];
|
||||
byte* palette = ((byte*)tex) + tex->offsets[3] + (tex->width>>3)*(tex->height>>3) + 2;
|
||||
//byte* palette = wadData + tex->offsets[MIPLEVELS]; // Palette starts 2 bytes after the last mipmap
|
||||
|
||||
init_buckets();
|
||||
int current_bucket = get_new_bucket();
|
||||
|
||||
// Set bucket pixelcounts
|
||||
for (int pixi = 0; pixi < tex->width * tex->height; pixi++) {
|
||||
byte color = wadData[pixi];
|
||||
colentries[color].pixcount += 1;
|
||||
}
|
||||
|
||||
int MAX_COLORS = 256;
|
||||
bool has_transparency = false;
|
||||
byte transparent_index = 0;
|
||||
int colors_used = 0;
|
||||
// Set colors and fill first bucket
|
||||
for (int i = 0; i < MAX_COLORS; i++) {
|
||||
colentry_t * ce = &(colentries[i]);
|
||||
if (ce->pixcount == 0) {
|
||||
continue; // speed up by not processing unused colors
|
||||
}
|
||||
colors_used++;
|
||||
|
||||
ce->r = palette[i * 3 + 0];
|
||||
ce->g = palette[i * 3 + 1];
|
||||
ce->b = palette[i * 3 + 2];
|
||||
ce->a = 255;
|
||||
if (ce->r == 0 && ce->g == 0 && ce->b == 255) {
|
||||
ce->r = ce->g = ce->b = 128;
|
||||
ce->a = 255;
|
||||
has_transparency = true;
|
||||
transparent_index = i;
|
||||
continue; // don't add transparencies to buckets
|
||||
}
|
||||
add_to_bucket(current_bucket, i);
|
||||
}
|
||||
// Con_Printf("Initial bucket %d: %d colors\n", current_bucket, bucket_counts[current_bucket]);
|
||||
|
||||
// could check if bucket has 16 colors or less and early out
|
||||
|
||||
sort_and_cut_bucket(current_bucket, 0, has_transparency ? 15 : 16);
|
||||
|
||||
if (has_transparency) {
|
||||
int transparent_bucket = get_new_bucket();
|
||||
byte_to_halfbyte_map[transparent_index] = transparent_bucket; // last index
|
||||
add_to_bucket(transparent_bucket, transparent_index);
|
||||
}
|
||||
// Con_Printf("num buckets: %d, has transparency: %d, colors actually used: %d\n", num_buckets, has_transparency, colors_used);
|
||||
|
||||
/* hack to resample width/height if it's less than 32 */
|
||||
bool stretch_sideways = false;
|
||||
if (tex->width <= 16) {
|
||||
tex->width *= 2;
|
||||
stretch_sideways = true;
|
||||
}
|
||||
int imageSize = tex->width * tex->height * 0.5;
|
||||
byte* texData = (byte*)memalign(16, 16 * 4 + imageSize);
|
||||
byte* unswizzled = (byte*)memalign(16, imageSize);
|
||||
unsigned int * newPal = (unsigned int*)&(texData[imageSize]);
|
||||
|
||||
/*
|
||||
for (int y = 0; y < tex->height; y++) {
|
||||
for (int x = 0; x < tex->width; x++) {
|
||||
byte colorIndex = wadData[y * (tex->width / width_mult) / height_mult + x / width_mult * 2];
|
||||
unswizzled[y * tex->width + x] = byte_to_halfbyte_map[colorIndex];
|
||||
colorIndex = wadData[y * (tex->width / width_mult) / height_mult + x / width_mult * 2 + 1];
|
||||
unswizzled[y * tex->width + x] = byte_to_halfbyte_map[colorIndex] << 4;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (stretch_sideways) {
|
||||
for (int i = 0; i < imageSize; i++) {
|
||||
byte colorIndex = wadData[i];
|
||||
unswizzled[i] = byte_to_halfbyte_map[colorIndex] + (byte_to_halfbyte_map[colorIndex] << 4);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < imageSize; i++) {
|
||||
byte colorIndex = wadData[i*2];
|
||||
unswizzled[i] = byte_to_halfbyte_map[colorIndex];
|
||||
colorIndex = wadData[i*2 + 1];
|
||||
unswizzled[i] += byte_to_halfbyte_map[colorIndex] << 4;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_buckets; i++) {
|
||||
int r = 0;
|
||||
int g = 0;
|
||||
int b = 0;
|
||||
int alpha = 255;
|
||||
colentry_t * col;
|
||||
for (int j = 0; j < bucket_counts[i]; j++) {
|
||||
byte colindex = get_color_index(i, j);
|
||||
if (has_transparency && transparent_index == colindex) {
|
||||
r = 128;
|
||||
g = 128;
|
||||
b = 128;
|
||||
alpha = 0;
|
||||
break;
|
||||
}
|
||||
col = color_of_bucket(i, j);
|
||||
r += col->r;
|
||||
g += col->g;
|
||||
b += col->b;
|
||||
}
|
||||
r /= bucket_counts[i];
|
||||
g /= bucket_counts[i];
|
||||
b /= bucket_counts[i];
|
||||
|
||||
newPal[i] = (alpha << 24) + (b << 16) + (g << 8) + r;
|
||||
//Con_Printf("Bucket %d count: %d\n", i, bucket_counts[i]);
|
||||
//Con_Printf("Color %d: %d %d %d %x\n", i, r, g, b, newPal[i]);
|
||||
}
|
||||
//Con_Printf("_ _ _ \n");
|
||||
|
||||
swizzle_fast(texData, unswizzled, tex->width * 0.5, tex->height);
|
||||
// memcpy(texData, unswizzled, imageSize);
|
||||
int index = GL_LoadTexture4(tex->name, tex->width, tex->height, texData, GU_LINEAR, qtrue);
|
||||
|
||||
free(texData);
|
||||
free(unswizzled);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
int WAD3_LoadTexture(miptex_t *mt)
|
||||
{
|
||||
char texname[MAX_QPATH];
|
||||
|
@ -198,7 +468,7 @@ int WAD3_LoadTexture(miptex_t *mt)
|
|||
int index;
|
||||
|
||||
if (mt->offsets[0])
|
||||
return ConvertWad3ToRGBA(mt);
|
||||
return ConvertWad3ToClut4(mt); // ConvertWad3ToRGBA(mt);
|
||||
|
||||
texname[sizeof(texname) - 1] = 0;
|
||||
W_CleanupName (mt->name, texname);
|
||||
|
@ -242,7 +512,7 @@ int WAD3_LoadTexture(miptex_t *mt)
|
|||
for (j = 0;j < MIPLEVELS;j++)
|
||||
tex->offsets[j] = LittleLong(tex->offsets[j]);
|
||||
|
||||
index = ConvertWad3ToRGBA(tex);
|
||||
index = ConvertWad3ToClut4(mt); // ConvertWad3ToRGBA(tex);
|
||||
|
||||
Hunk_FreeToLowMark(lowmark);
|
||||
|
||||
|
|
Loading…
Reference in a new issue