diff --git a/GL/private.h b/GL/private.h index 7ebd01b..e0b9eef 100644 --- a/GL/private.h +++ b/GL/private.h @@ -41,6 +41,14 @@ typedef struct { AlignedVector vector; } PolyList; +typedef struct { + /* Palette data is always stored in RAM as RGBA8888 and packed as ARGB8888 + * when uploaded to the PVR */ + GLubyte* data; + GLushort width; + GLenum format; +} TexturePalette; + typedef struct { GLushort width; GLushort height; @@ -57,6 +65,9 @@ typedef struct { GLenum magFilter; GLboolean isCompressed; + GLboolean isPaletted; + + TexturePalette* palette; } TextureObject; typedef struct { diff --git a/GL/state.c b/GL/state.c index 802068f..7ce4863 100644 --- a/GL/state.c +++ b/GL/state.c @@ -601,7 +601,7 @@ const GLbyte *glGetString(GLenum name) { return "GLdc 1.x"; case GL_EXTENSIONS: - return "GL_ARB_framebuffer_object, GL_ARB_multitexture, GL_ARB_texture_rg"; + return "GL_ARB_framebuffer_object, GL_ARB_multitexture, GL_ARB_texture_rg, GL_EXT_paletted_texture"; } return "GL_KOS_ERROR: ENUM Unsupported\n"; diff --git a/GL/texture.c b/GL/texture.c index f77bd12..cd028d3 100644 --- a/GL/texture.c +++ b/GL/texture.c @@ -2,6 +2,8 @@ #include #include +#include +#include #include "../include/glext.h" #include "../include/glkos.h" @@ -17,6 +19,28 @@ static GLubyte ACTIVE_TEXTURE = 0; static GLuint _determinePVRFormat(GLint internalFormat, GLenum type); +#define PACK_ARGB8888(a,r,g,b) ( ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF) ) + +static void _glApplyColorTable() { + TextureObject* active = getBoundTexture(); + + if(!active) { + return; //? Unload the palette? Make White? + } + + if(!active->palette || !active->palette->data) { + return; + } + + pvr_set_pal_format(PVR_PAL_ARGB8888); + + GLushort i = 0; + for(; i < active->palette->width; ++i) { + GLubyte* entry = &active->palette->data[i * 4]; + pvr_set_pal_entry(i, PACK_ARGB8888(entry[3], entry[1], entry[2], entry[0])); + } +} + GLubyte _glGetActiveTexture() { return ACTIVE_TEXTURE; } @@ -141,6 +165,9 @@ void APIENTRY glGenTextures(GLsizei n, GLuint *textures) { txr->mipmapCount = 0; txr->minFilter = GL_NEAREST; txr->magFilter = GL_NEAREST; + txr->palette = NULL; + txr->isCompressed = GL_FALSE; + txr->isPaletted = GL_FALSE; *textures = id; @@ -166,6 +193,16 @@ void APIENTRY glDeleteTextures(GLsizei n, GLuint *textures) { txr->data = NULL; } + if(txr->palette && txr->palette->data) { + free(txr->palette->data); + txr->palette->data = NULL; + } + + if(txr->palette) { + free(txr->palette); + txr->palette = NULL; + } + named_array_release(&TEXTURE_OBJECTS, *textures++); } } @@ -181,6 +218,9 @@ void APIENTRY glBindTexture(GLenum target, GLuint texture) { if(texture) { TEXTURE_UNITS[ACTIVE_TEXTURE] = (TextureObject*) named_array_get(&TEXTURE_OBJECTS, texture); + + // If this is a paletted texture, we need to reapply the color table + _glApplyColorTable(); } else { TEXTURE_UNITS[ACTIVE_TEXTURE] = NULL; } @@ -245,13 +285,13 @@ void APIENTRY glCompressedTexImage2DARB(GLenum target, GLint w = width; if(w < 8 || (w & -w) != w) { /* Width is not a power of two. Must be!*/ - _glKosThrowError(GL_INVALID_VALUE, "glTexImage2D"); + _glKosThrowError(GL_INVALID_VALUE, __func__); } GLint h = height; if(h < 8 || (h & -h) != h) { /* Height is not a power of two. Must be!*/ - _glKosThrowError(GL_INVALID_VALUE, "glTexImage2D"); + _glKosThrowError(GL_INVALID_VALUE, __func__); } if(level || border) { @@ -332,6 +372,8 @@ void APIENTRY glCompressedTexImage2DARB(GLenum target, static GLint _cleanInternalFormat(GLint internalFormat) { switch (internalFormat) { + case GL_COLOR_INDEX8_EXT: + return GL_COLOR_INDEX8_EXT; case GL_ALPHA: /* case GL_ALPHA4: case GL_ALPHA8: @@ -363,24 +405,24 @@ static GLint _cleanInternalFormat(GLint internalFormat) { case 3: return GL_RGB; case GL_RGB: -/* case GL_R3_G3_B2: + /* case GL_R3_G3_B2: */ case GL_RGB4: case GL_RGB5: case GL_RGB8: case GL_RGB10: case GL_RGB12: - case GL_RGB16: */ + case GL_RGB16: return GL_RGB; case 4: return GL_RGBA; case GL_RGBA: -/* case GL_RGBA2: + case GL_RGBA2: case GL_RGBA4: case GL_RGB5_A1: case GL_RGBA8: case GL_RGB10_A2: case GL_RGBA12: - case GL_RGBA16: */ + case GL_RGBA16: return GL_RGBA; /* Support ARB_texture_rg */ @@ -450,33 +492,69 @@ static GLuint _determinePVRFormat(GLint internalFormat, GLenum type) { case GL_COMPRESSED_ARGB_1555_VQ_TWID_KOS: case GL_COMPRESSED_ARGB_1555_VQ_MIPMAP_TWID_KOS: return PVR_TXRFMT_ARGB1555 | PVR_TXRFMT_TWIDDLED | PVR_TXRFMT_VQ_ENABLE; + case GL_COLOR_INDEX8_EXT: + return PVR_TXRFMT_PAL8BPP; default: return 0; } } -typedef void (*TextureConversionFunc)(const GLubyte*, GLushort*); +typedef void (*TextureConversionFunc)(const GLubyte*, GLubyte*); -static void _rgba8888_to_argb4444(const GLubyte* source, GLushort* dest) { - *dest = (source[3] & 0xF0) << 8 | (source[0] & 0xF0) << 4 | (source[1] & 0xF0) | (source[2] & 0xF0) >> 4; +static inline void _rgba8888_to_argb4444(const GLubyte* source, GLubyte* dest) { + *((GLushort*) dest) = (source[3] & 0xF0) << 8 | (source[0] & 0xF0) << 4 | (source[1] & 0xF0) | (source[2] & 0xF0) >> 4; } -static void _rgb888_to_rgb565(const GLubyte* source, GLushort* dest) { - *dest = ((source[0] & 0b11111000) << 8) | ((source[1] & 0b11111100) << 3) | (source[2] >> 3); +static inline void _rgba8888_to_rgba8888(const GLubyte* source, GLubyte* dest) { + /* Noop */ + GLubyte* dst = (GLubyte*) dest; + dst[0] = source[0]; + dst[1] = source[1]; + dst[2] = source[2]; + dst[3] = source[3]; } -static void _rgba8888_to_a000(const GLubyte* source, GLushort* dest) { - *dest = ((source[3] & 0b11111000) << 8); +static inline void _rgb888_to_rgba8888(const GLubyte* source, GLubyte* dest) { + /* Noop */ + GLubyte* dst = (GLubyte*) dest; + dst[0] = source[0]; + dst[1] = source[1]; + dst[2] = source[2]; + dst[3] = 255; } -static void _r8_to_rgb565(const GLubyte* source, GLushort* dest) { - *dest = (source[0] & 0b11111000) << 8; +static inline void _rgb888_to_rgb565(const GLubyte* source, GLubyte* dest) { + *((GLushort*) dest) = ((source[0] & 0b11111000) << 8) | ((source[1] & 0b11111100) << 3) | (source[2] >> 3); } -static void _rgba4444_to_argb4444(const GLubyte* source, GLushort* dest) { +static inline void _rgba8888_to_a000(const GLubyte* source, GLubyte* dest) { + *((GLushort*) dest) = ((source[3] & 0b11111000) << 8); +} + +static inline void _r8_to_rgb565(const GLubyte* source, GLubyte* dest) { + *((GLushort*) dest) = (source[0] & 0b11111000) << 8; +} + +static inline void _rgba4444_to_argb4444(const GLubyte* source, GLubyte* dest) { GLushort* src = (GLushort*) source; - *dest = ((*src & 0x000F) << 12) | *src >> 4; + *((GLushort*) dest) = ((*src & 0x000F) << 12) | *src >> 4; +} + +static inline void _rgba4444_to_rgba8888(const GLubyte* source, GLubyte* dest) { + GLushort src = *((GLushort*) source); + GLubyte* dst = (GLubyte*) dest; + + dst[0] = ((src & 0xF000) >> 12) * 2; + dst[1] = ((src & 0x0F00) >> 8) * 2; + dst[2] = ((src & 0x00F0) >> 4) * 2; + dst[3] = ((src & 0x000F)) * 2; +} + +static inline void _i8_to_i8(const GLubyte* source, GLubyte* dest) { + /* For indexes */ + GLubyte* dst = (GLubyte*) dest; + *dst = *source; } static TextureConversionFunc _determineConversion(GLint internalFormat, GLenum format, GLenum type) { @@ -512,6 +590,30 @@ static TextureConversionFunc _determineConversion(GLint internalFormat, GLenum f return _rgba4444_to_argb4444; } } break; + case GL_RGBA8: { + if(type == GL_UNSIGNED_BYTE && format == GL_RGBA) { + return _rgba8888_to_rgba8888; + } else if (type == GL_BYTE && format == GL_RGBA) { + return _rgba8888_to_rgba8888; + } else if(type == GL_UNSIGNED_BYTE && format == GL_RGB) { + return _rgb888_to_rgba8888; + } else if (type == GL_BYTE && format == GL_RGB) { + return _rgb888_to_rgba8888; + } else if(type == GL_UNSIGNED_SHORT_4_4_4_4 && format == GL_RGBA) { + return _rgba4444_to_rgba8888; + } + } break; + case GL_COLOR_INDEX8_EXT: + if(format == GL_COLOR_INDEX) { + switch(type) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + return _i8_to_i8; + default: + break; + } + } + break; default: fprintf(stderr, "Unsupported conversion: %x -> %x, %x\n", internalFormat, format, type); break; @@ -525,6 +627,7 @@ static GLboolean _isSupportedFormat(GLenum format) { case GL_RGB: case GL_RGBA: case GL_BGRA: + case GL_COLOR_INDEX: return GL_TRUE; default: return GL_FALSE; @@ -556,18 +659,24 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, _glKosThrowError(GL_INVALID_ENUM, "glTexImage2D"); } - if(!_isSupportedFormat(format)) { - _glKosThrowError(GL_INVALID_ENUM, "glTexImage2D"); - } + if(format != GL_COLOR_INDEX) { + if(!_isSupportedFormat(format)) { + _glKosThrowError(GL_INVALID_ENUM, "glTexImage2D"); + } - /* Abuse determineStride to see if type is valid */ - if(_determineStride(GL_RGBA, type) == -1) { - _glKosThrowError(GL_INVALID_ENUM, "glTexImage2D"); - } + /* Abuse determineStride to see if type is valid */ + if(_determineStride(GL_RGBA, type) == -1) { + _glKosThrowError(GL_INVALID_ENUM, "glTexImage2D"); + } - internalFormat = _cleanInternalFormat(internalFormat); - if(internalFormat == -1) { - _glKosThrowError(GL_INVALID_VALUE, "glTexImage2D"); + internalFormat = _cleanInternalFormat(internalFormat); + if(internalFormat == -1) { + _glKosThrowError(GL_INVALID_VALUE, "glTexImage2D"); + } + } else { + if(internalFormat != GL_COLOR_INDEX8_EXT) { + _glKosThrowError(GL_INVALID_ENUM, __func__); + } } GLint w = width; @@ -594,6 +703,13 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, _glKosThrowError(GL_INVALID_OPERATION, "glTexImage2D"); } + GLboolean isPaletted = (internalFormat == GL_COLOR_INDEX8_EXT) ? GL_TRUE : GL_FALSE; + + if(isPaletted && level > 0) { + /* Paletted textures can't have mipmaps */ + _glKosThrowError(GL_INVALID_OPERATION, __func__); + } + if(_glKosHasError()) { _glKosPrintError(); return; @@ -618,20 +734,34 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, } } - GLuint bytes = (width * height * sizeof(GLushort)); + /* All colour formats are represented as shorts internally. Paletted textures + * are represented by byte indexes (which look up into a color table) + */ + GLint destStride = isPaletted ? 1 : 2; + GLuint bytes = (width * height * destStride); if(!active->data) { + assert(active); + assert(width); + assert(height); + assert(destStride); + /* need texture memory */ active->width = width; active->height = height; active->color = pvr_format; /* Set the required mipmap count */ active->mipmapCount = _glGetMipmapLevelCount(active); - active->dataStride = sizeof(GLshort); + active->dataStride = destStride; GLuint size = _glGetMipmapDataSize(active); + assert(size); + active->data = pvr_mem_malloc(size); + assert(active->data); + active->isCompressed = GL_FALSE; + active->isPaletted = isPaletted; } /* Mark this level as set in the mipmap bitmask */ @@ -644,7 +774,10 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, * These are the only formats where the source format passed in matches the pvr format. * Note the REV formats + GL_BGRA will reverse to ARGB which is what the PVR supports */ - if(format == GL_BGRA && type == GL_UNSIGNED_SHORT_4_4_4_4_REV && internalFormat == GL_RGBA) { + if(format == GL_COLOR_INDEX) { + /* Don't convert color indexes */ + needsConversion = GL_FALSE; + } else if(format == GL_BGRA && type == GL_UNSIGNED_SHORT_4_4_4_4_REV && internalFormat == GL_RGBA) { needsConversion = GL_FALSE; } else if(format == GL_BGRA && type == GL_UNSIGNED_SHORT_1_5_5_5_REV && internalFormat == GL_RGBA) { needsConversion = GL_FALSE; @@ -659,6 +792,7 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, } GLubyte* targetData = _glGetMipmapLocation(active, level); + assert(targetData); if(!data) { /* No data? Do nothing! */ @@ -679,7 +813,7 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, return; } - GLushort* dest = (GLushort*) targetData; + GLubyte* dest = (GLubyte*) targetData; const GLubyte* source = data; GLint stride = _determineStride(format, type); @@ -691,10 +825,10 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, /* Perform the conversion */ GLuint i; - for(i = 0; i < bytes; i += 2) { + for(i = 0; i < bytes; i += destStride) { convert(source, dest); - dest++; + dest += destStride; source += stride; } } @@ -769,3 +903,95 @@ void APIENTRY glTexParameteri(GLenum target, GLenum pname, GLint param) { } } } + +GLAPI void APIENTRY glColorTableEXT(GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const GLvoid *data) { + GLenum validTargets[] = {GL_TEXTURE_2D, 0}; + GLenum validInternalFormats[] = {GL_RGB8, GL_RGBA8, 0}; + GLenum validFormats[] = {GL_RGB, GL_RGBA, 0}; + GLenum validTypes[] = {GL_UNSIGNED_BYTE, GL_BYTE, GL_UNSIGNED_SHORT, GL_SHORT, 0}; + + if(_glCheckValidEnum(target, validTargets, __func__) != 0) { + return; + } + + if(_glCheckValidEnum(internalFormat, validInternalFormats, __func__) != 0) { + return; + } + + if(_glCheckValidEnum(format, validFormats, __func__) != 0) { + return; + } + + if(_glCheckValidEnum(type, validTypes, __func__) != 0) { + return; + } + + /* Only allow up to 256 colours in a palette */ + if(width > 256 || width == 0) { + _glKosThrowError(GL_INVALID_VALUE, __func__); + _glKosPrintError(); + return; + } + + GLuint sourceStride = _determineStride(format, type); + + TextureConversionFunc convert = _determineConversion( + GL_RGBA8, /* We always store palettes in this format */ + format, + type + ); + + if(!convert) { + _glKosThrowError(GL_INVALID_OPERATION, __func__); + _glKosPrintError(); + return; + } + + TextureObject* active = getBoundTexture(); + if(active->palette) { + free(active->palette->data); + active->palette->data = NULL; + } else { + active->palette = (TexturePalette*) malloc(sizeof(TexturePalette)); + } + + active->palette->data = (GLubyte*) malloc(width * 4); + active->palette->format = format; + active->palette->width = width; + + GLubyte* src = (GLubyte*) data; + GLubyte* dst = (GLubyte*) active->palette->data; + + /* Transform and copy the source palette to the texture */ + GLushort i = 0; + for(; i < width; ++i) { + convert(src, dst); + + src += sourceStride; + dst += 4; + } + + /* Apply the palette immediately, we'll also do this when binding the texture */ + _glApplyColorTable(); +} + +GLAPI void APIENTRY glColorSubTableEXT(GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const GLvoid *data) { + _glKosThrowError(GL_INVALID_OPERATION, __func__); + _glKosPrintError(); +} + +GLAPI void APIENTRY glGetColorTableEXT(GLenum target, GLenum format, GLenum type, GLvoid *data) { + _glKosThrowError(GL_INVALID_OPERATION, __func__); + _glKosPrintError(); +} + +GLAPI void APIENTRY glGetColorTableParameterivEXT(GLenum target, GLenum pname, GLint *params) { + _glKosThrowError(GL_INVALID_OPERATION, __func__); + _glKosPrintError(); +} + +GLAPI void APIENTRY glGetColorTableParameterfvEXT(GLenum target, GLenum pname, GLfloat *params) { + _glKosThrowError(GL_INVALID_OPERATION, __func__); + _glKosPrintError(); +} + diff --git a/include/gl.h b/include/gl.h index 1b94cdc..2487f37 100644 --- a/include/gl.h +++ b/include/gl.h @@ -339,6 +339,7 @@ __BEGIN_DECLS #define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 #define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_COLOR_INDEX 0x1900 #define GL_RED 0x1903 #define GL_GREEN 0x1904 #define GL_BLUE 0x1905 @@ -349,6 +350,20 @@ __BEGIN_DECLS #define GL_LUMINANCE_ALPHA 0x190A #define GL_BGRA 0x80E1 +#define GL_RGB4 0x804F +#define GL_RGB5 0x8050 +#define GL_RGB8 0x8051 +#define GL_RGB10 0x8052 +#define GL_RGB12 0x8053 +#define GL_RGB16 0x8054 +#define GL_RGBA2 0x8055 +#define GL_RGBA4 0x8056 +#define GL_RGB5_A1 0x8057 +#define GL_RGBA8 0x8058 +#define GL_RGB10_A2 0x8059 +#define GL_RGBA12 0x805A +#define GL_RGBA16 0x805B + #define GLbyte char #define GLshort short #define GLint int diff --git a/include/glext.h b/include/glext.h index 67b39ca..739f333 100644 --- a/include/glext.h +++ b/include/glext.h @@ -134,6 +134,31 @@ GLAPI void APIENTRY glGenerateMipmapEXT(GLenum target); GLAPI GLenum APIENTRY glCheckFramebufferStatusEXT(GLenum target); GLAPI GLboolean APIENTRY glIsFramebufferEXT(GLuint framebuffer); +/* ext_paletted_texture */ +#define GL_COLOR_INDEX1_EXT 0x80E2 +#define GL_COLOR_INDEX2_EXT 0x80E3 +#define GL_COLOR_INDEX4_EXT 0x80E4 +#define GL_COLOR_INDEX8_EXT 0x80E5 +#define GL_COLOR_INDEX12_EXT 0x80E6 +#define GL_COLOR_INDEX16_EXT 0x80E7 + +#define GL_COLOR_TABLE_FORMAT_EXT 0x80D8 +#define GL_COLOR_TABLE_WIDTH_EXT 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE_EXT 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE_EXT 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE_EXT 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE_EXT 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE_EXT 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE_EXT 0x80DF + +#define GL_TEXTURE_INDEX_SIZE_EXT 0x80ED + +GLAPI void APIENTRY glColorTableEXT(GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const GLvoid *data); +GLAPI void APIENTRY glColorSubTableEXT(GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const GLvoid *data); +GLAPI void APIENTRY glGetColorTableEXT(GLenum target, GLenum format, GLenum type, GLvoid *data); +GLAPI void APIENTRY glGetColorTableParameterivEXT(GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetColorTableParameterfvEXT(GLenum target, GLenum pname, GLfloat *params); + /* Loads VQ compressed texture from SH4 RAM into PVR VRAM */ /* internalformat must be one of the following constants: GL_UNSIGNED_SHORT_5_6_5_VQ diff --git a/samples/Makefile b/samples/Makefile index de30006..f1ae938 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -22,3 +22,4 @@ all: $(KOS_MAKE) -C quadmark all $(KOS_MAKE) -C trimark all $(KOS_MAKE) -C multitexture_arrays all + $(KOS_MAKE) -C paletted all diff --git a/samples/paletted/Makefile b/samples/paletted/Makefile new file mode 100644 index 0000000..04e0bfd --- /dev/null +++ b/samples/paletted/Makefile @@ -0,0 +1,29 @@ +TARGET = paletted.elf +OBJS = main.o + +all: rm-elf $(TARGET) + +include $(KOS_BASE)/Makefile.rules + +clean: + -rm -f $(TARGET) $(OBJS) romdisk.* + +rm-elf: + -rm -f $(TARGET) romdisk.* + +$(TARGET): $(OBJS) romdisk.o + $(KOS_CC) $(KOS_CFLAGS) $(KOS_LDFLAGS) -o $(TARGET) $(KOS_START) \ + $(OBJS) romdisk.o $(OBJEXTRA) -lm -lkosutils $(KOS_LIBS) + +romdisk.img: + $(KOS_GENROMFS) -f romdisk.img -d romdisk -v + +romdisk.o: romdisk.img + $(KOS_BASE)/utils/bin2o/bin2o romdisk.img romdisk romdisk.o + +run: $(TARGET) + $(KOS_LOADER) $(TARGET) + +dist: + rm -f $(OBJS) romdisk.o romdisk.img + $(KOS_STRIP) $(TARGET) diff --git a/samples/paletted/main.c b/samples/paletted/main.c new file mode 100644 index 0000000..534d99e --- /dev/null +++ b/samples/paletted/main.c @@ -0,0 +1,236 @@ +#include +#include +#include + +#include "gl.h" +#include "glext.h" +#include "glu.h" +#include "glkos.h" + +extern uint8 romdisk[]; +KOS_INIT_ROMDISK(romdisk); + +/* floats for x rotation, y rotation, z rotation */ +float xrot, yrot, zrot; +/* storage for one texture */ +int texture[1]; + +typedef struct { + unsigned int height; + unsigned int width; + unsigned int palette_width; + char* palette; + char* data; +} Image; + +int LoadPalettedTex(const char* filename, Image* image) { + struct { + char id[4]; // 'DTEX' + short width; + short height; + int type; + int size; + } header; + + FILE* filein = NULL; + filein = fopen(filename, "rb"); + if(!filein) { + printf("Unable to open file\n"); + return 0; + } + + fread(&header, sizeof(header), 1, filein); + + if((header.type & (7 << 27)) >> 27 != 6) { + printf("Not PAL8BPP format\n"); + return 0; + } + + image->width = header.width; + image->height = header.height; + image->data = (char*) malloc(sizeof(char) * header.size); + + fread(image->data, header.size, sizeof(char), filein); + fclose(filein); + + char palette_filename[100]; + + strcpy(palette_filename, filename); + strcat(palette_filename, ".pal"); + + filein = fopen(palette_filename, "rb"); + if(!filename) { + printf("Unable to open the palette file\n"); + return 0; + } + + struct { + char id[4]; + int numcolors; + } palette_header; + fread(&palette_header, sizeof(palette_header), 1, filein); + + image->palette = (unsigned int*) malloc(sizeof(unsigned int) * palette_header.numcolors); + image->palette_width = palette_header.numcolors; + + fread(image->palette, sizeof(unsigned int), palette_header.numcolors, filein); + + unsigned int i = 0; + for(; i < palette_header.numcolors; ++i) { + unsigned int* p = (unsigned int*) image->palette; + + // Swap the colours around from ARGB to RGBA + char r = (p[i] & 0x00FF0000) >> 16; + char g = (p[i] & 0x0000FF00) >> 8; + char b = (p[i] & 0x000000FF); + char a = (p[i] & 0xFF000000) >> 24; + + image->palette[i * 4] = r; + image->palette[i * 4 + 1] = g; + image->palette[i * 4 + 2] = b; + image->palette[i * 4 + 3] = a; + } + + return 1; +} + +// Load Bitmaps And Convert To Textures +void LoadGLTextures() { + // Load Texture + Image *image1; + + // allocate space for texture + image1 = (Image *) malloc(sizeof(Image)); + if (image1 == NULL) { + printf("Error allocating space for image"); + exit(0); + } + + if (!LoadPalettedTex("/rd/NeHe.tex", image1)) { + exit(1); + } + + // Create Texture + glGenTextures(1, &texture[0]); + glBindTexture(GL_TEXTURE_2D, texture[0]); // 2d texture (x and y size) + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR); // scale linearly when image bigger than texture + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR); // scale linearly when image smalled than texture + + glColorTableEXT(GL_TEXTURE_2D, GL_RGBA8, image1->palette_width, GL_RGBA, GL_UNSIGNED_BYTE, image1->palette); + + // 2d texture, level of detail 0 (normal), 3 components (red, green, blue), x size from image, y size from image, + // border 0 (normal), rgb color data, unsigned byte data, and finally the data itself. + glTexImage2D(GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, image1->width, image1->height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, image1->data); +}; + +/* A general OpenGL initialization function. Sets all of the initial parameters. */ +void InitGL(int Width, int Height) // We call this right after our OpenGL window is created. +{ + LoadGLTextures(); + glEnable(GL_TEXTURE_2D); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // This Will Clear The Background Color To Black + glClearDepth(1.0); // Enables Clearing Of The Depth Buffer + glDepthFunc(GL_LESS); // The Type Of Depth Test To Do + glEnable(GL_DEPTH_TEST); // Enables Depth Testing + glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); // Reset The Projection Matrix + + gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f); // Calculate The Aspect Ratio Of The Window + + glMatrixMode(GL_MODELVIEW); +} + +/* The function called when our window is resized (which shouldn't happen, because we're fullscreen) */ +void ReSizeGLScene(int Width, int Height) +{ + if (Height == 0) // Prevent A Divide By Zero If The Window Is Too Small + Height = 1; + + glViewport(0, 0, Width, Height); // Reset The Current Viewport And Perspective Transformation + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f); + glMatrixMode(GL_MODELVIEW); +} + + +/* The main drawing function. */ +void DrawGLScene() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer + glLoadIdentity(); // Reset The View + + glTranslatef(0.0f,0.0f,-5.0f); // move 5 units into the screen. + + glRotatef(xrot,1.0f,0.0f,0.0f); // Rotate On The X Axis + glRotatef(yrot,0.0f,1.0f,0.0f); // Rotate On The Y Axis + glRotatef(zrot,0.0f,0.0f,1.0f); // Rotate On The Z Axis + + glBindTexture(GL_TEXTURE_2D, texture[0]); // choose the texture to use. + + glBegin(GL_QUADS); // begin drawing a cube + + // Front Face (note that the texture's corners have to match the quad's corners) + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Top Left Of The Texture and Quad + + // Back Face + glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Bottom Left Of The Texture and Quad + + // Top Face + glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Top Right Of The Texture and Quad + + // Bottom Face + glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Right Of The Texture and Quad + + // Right face + glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Bottom Left Of The Texture and Quad + + // Left Face + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // Top Left Of The Texture and Quad + + glEnd(); // done with the polygon. + + xrot+=1.5f; // X Axis Rotation + yrot+=1.5f; // Y Axis Rotation + zrot+=1.5f; // Z Axis Rotation + // + // swap buffers to display, since we're double buffered. + glKosSwapBuffers(); +} + +int main(int argc, char **argv) +{ + glKosInit(); + + InitGL(640, 480); + ReSizeGLScene(640, 480); + + while(1) { + DrawGLScene(); + } + + return 0; +} diff --git a/samples/paletted/romdisk/NeHe.bmp b/samples/paletted/romdisk/NeHe.bmp new file mode 100644 index 0000000..6b3db10 Binary files /dev/null and b/samples/paletted/romdisk/NeHe.bmp differ diff --git a/samples/paletted/romdisk/NeHe.tex b/samples/paletted/romdisk/NeHe.tex new file mode 100644 index 0000000..26ee1b4 Binary files /dev/null and b/samples/paletted/romdisk/NeHe.tex differ diff --git a/samples/paletted/romdisk/NeHe.tex.pal b/samples/paletted/romdisk/NeHe.tex.pal new file mode 100644 index 0000000..f1159f0 Binary files /dev/null and b/samples/paletted/romdisk/NeHe.tex.pal differ diff --git a/samples/paletted/romdisk/PLACEHOLDER b/samples/paletted/romdisk/PLACEHOLDER new file mode 100644 index 0000000..e69de29