diff --git a/CMakeLists.txt b/CMakeLists.txt index e416cdd..5b08891 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,6 +197,7 @@ gen_sample(nehe06_vq samples/nehe06_vq/main.c) gen_sample(nehe06_4444twid samples/nehe06_4444twid/main.c) gen_sample(nehe08 samples/nehe08/main.c samples/nehe08/pvr-texture.c) gen_sample(nehe10 samples/nehe10/main.c samples/loadbmp.c) +gen_sample(nehe16 samples/nehe16/main.c samples/nehe16/pvr-texture.c) gen_sample(nehe20 samples/nehe20/main.c samples/loadbmp.c) gen_sample(ortho2d samples/ortho2d/main.c) gen_sample(paletted samples/paletted/main.c) diff --git a/GL/fog.c b/GL/fog.c index e83d624..17f2ad3 100644 --- a/GL/fog.c +++ b/GL/fog.c @@ -1,73 +1,138 @@ +/* + TODO: glGetX() fog values +*/ + #include #include "private.h" -static GLfloat FOG_START = 0.0f; -static GLfloat FOG_END = 1.0f; -static GLfloat FOG_DENSITY = 1.0f; -static GLenum FOG_MODE = GL_EXP; -static GLfloat FOG_COLOR [] = {0.0f, 0.0f, 0.0f, 0.0f}; +static struct { + GLfloat START; + GLfloat END; + GLfloat DENSITY; + GLenum MODE; + GLfloat COLOR[4]; +} FOG = { + 0.0f, 1.0f, 1.0f, GL_EXP, { 0.0f, 0.0f, 0.0f, 0.0f } +}; -static void updatePVRFog() { - if(FOG_MODE == GL_LINEAR) { - GPUSetFogLinear(FOG_START, FOG_END); - } else if(FOG_MODE == GL_EXP) { - GPUSetFogExp(FOG_DENSITY); - } else if(FOG_MODE == GL_EXP2) { - GPUSetFogExp2(FOG_DENSITY); +static void updatePVRFog(void) { + switch(FOG.MODE) { + case GL_LINEAR: + GPUSetFogLinear(FOG.START, FOG.END); + break; + + case GL_EXP: + GPUSetFogExp(FOG.DENSITY); + break; + + case GL_EXP2: + GPUSetFogExp2(FOG.DENSITY); + break; } - GPUSetFogColor(FOG_COLOR[3], FOG_COLOR[0], FOG_COLOR[1], FOG_COLOR[2]); + + GPUSetFogColor(FOG.COLOR[3], FOG.COLOR[0], FOG.COLOR[1], FOG.COLOR[2]); } -void APIENTRY glFogf(GLenum pname, GLfloat param) { +void APIENTRY glFogf(GLenum pname, GLfloat param) { switch(pname) { - case GL_FOG_MODE: { - FOG_MODE = (GLenum) param; - updatePVRFog(); - } break; - case GL_FOG_DENSITY: { - FOG_DENSITY = param; - updatePVRFog(); - } break; - case GL_FOG_START: { - FOG_START = param; - updatePVRFog(); - } break; - case GL_FOG_END: { - FOG_END = param; - updatePVRFog(); - } break; - case GL_FOG_INDEX: - default: { - _glKosThrowError(GL_INVALID_ENUM, __func__); - } + case GL_FOG_DENSITY: + if(FOG.DENSITY != param) { + if(param < 0.0f) + _glKosThrowError(GL_INVALID_VALUE, __func__); + else { + FOG.DENSITY = param; + updatePVRFog(); + } + } + break; + + case GL_FOG_START: + if(FOG.START != param) { + FOG.START = param; + updatePVRFog(); + } + break; + + case GL_FOG_END: + if(FOG.END != param) { + FOG.END = param; + updatePVRFog(); + } + break; + + default: + _glKosThrowError(GL_INVALID_ENUM, __func__); } } -void APIENTRY glFogi(GLenum pname, GLint param) { - glFogf(pname, (GLfloat) param); -} +void APIENTRY glFogi(GLenum pname, GLint param) { + switch(pname) { + case GL_FOG_DENSITY: + case GL_FOG_START: + case GL_FOG_END: + glFogf(pname, (GLfloat)param); + break; -void APIENTRY glFogfv(GLenum pname, const GLfloat* params) { - if(pname == GL_FOG_COLOR) { - FOG_COLOR[0] = params[0]; - FOG_COLOR[1] = params[1]; - FOG_COLOR[2] = params[2]; - FOG_COLOR[3] = params[3]; - updatePVRFog(); - } else { - glFogf(pname, *params); + case GL_FOG_MODE: + if(FOG.MODE != param) { + switch(param) { + case GL_LINEAR: + case GL_EXP: + case GL_EXP2: + FOG.MODE = param; + updatePVRFog(); + break; + + default: + _glKosThrowError(GL_INVALID_ENUM, __func__); + } + } + break; + + case GL_FOG_INDEX: + default: + _glKosThrowError(GL_INVALID_ENUM, __func__); } } -void APIENTRY glFogiv(GLenum pname, const GLint* params) { - if(pname == GL_FOG_COLOR) { - FOG_COLOR[0] = ((GLfloat) params[0]) / (GLfloat) INT_MAX; - FOG_COLOR[1] = ((GLfloat) params[1]) / (GLfloat) INT_MAX; - FOG_COLOR[2] = ((GLfloat) params[2]) / (GLfloat) INT_MAX; - FOG_COLOR[3] = ((GLfloat) params[3]) / (GLfloat) INT_MAX; - updatePVRFog(); - } else { - glFogi(pname, *params); +void APIENTRY glFogfv(GLenum pname, const GLfloat* params) { + switch(pname) { + case GL_FOG_COLOR: { + GLfloat color[] = { + CLAMP(params[0], 0.0f, 1.0f), + CLAMP(params[1], 0.0f, 1.0f), + CLAMP(params[2], 0.0f, 1.0f), + CLAMP(params[3], 0.0f, 1.0f) + }; + + if(memcmp(color, FOG.COLOR, sizeof(float) * 4)) { + memcpy(FOG.COLOR, color, sizeof(float) * 4); + updatePVRFog(); + } + break; + } + + default: + glFogf(pname, *params); + } +} + +void APIENTRY glFogiv(GLenum pname, const GLint* params) { + switch(pname) { + case GL_FOG_COLOR: { + GLfloat color[] = { + (GLfloat)params[0] / (GLfloat)INT_MAX, + (GLfloat)params[1] / (GLfloat)INT_MAX, + (GLfloat)params[2] / (GLfloat)INT_MAX, + (GLfloat)params[3] / (GLfloat)INT_MAX, + }; + + glFogfv(pname, color); + break; + } + + default: + glFogi(pname, *params); } } diff --git a/samples/nehe16/main.c b/samples/nehe16/main.c new file mode 100644 index 0000000..c080a1a --- /dev/null +++ b/samples/nehe16/main.c @@ -0,0 +1,247 @@ +/* + KallistiOS 2.0.0 + + nehe16.c + (c)2025 Falco Girgis + (c)2014 Josh Pearson + (c)2001 Benoit Miller + (c)2000 Jeff Molofee +*/ + +#include +#include +#include +#include + +#ifdef __DREAMCAST__ +#include +extern uint8 romdisk[]; +KOS_INIT_ROMDISK(romdisk); +#define IMG_PATH "/rd/glass.pvr" +#else +#define IMG_PATH "../samples/nehe08/romdisk/glass.pvr" +#endif + +/* Simple GL example to demonstrate fog (PVR table fog). + + Essentially the same thing as NeHe's lesson16 code. + To learn more, go to http://nehe.gamedev.net/. + + DPAD controls the cube rotation, button A & B control the depth + of the cube, button X toggles fog on/off, and button Y toggles fog type. +*/ + +static GLfloat xrot; /* X Rotation */ +static GLfloat yrot; /* Y Rotation */ +static GLfloat xspeed; /* X Rotation Speed */ +static GLfloat yspeed; /* Y Rotation Speed */ +static GLfloat z = -5.0f; /* Depth Into The Screen */ + +static GLuint texture; /* Storage For Texture */ + +/* Storage For Three Types Of Fog */ +GLuint fogType = 0; /* use GL_EXP initially */ +GLuint fogMode[] = { GL_EXP, GL_EXP2, GL_LINEAR }; +char cfogMode[3][10] = {"GL_EXP ", "GL_EXP2 ", "GL_LINEAR" }; +GLfloat fogColor[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; /* Fog Color */ +int fog = GL_TRUE; + +/* Load a PVR texture - located in pvr-texture.c */ +extern GLuint glTextureLoadPVR(char *fname, unsigned char isMipMapped, unsigned char glMipMap); + +void draw_gl(void) { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + glTranslatef(0.0f, 0.0f, z); + + glRotatef(xrot, 1.0f, 0.0f, 0.0f); + glRotatef(yrot, 0.0f, 1.0f, 0.0f); + + glBindTexture(GL_TEXTURE_2D, texture); + + glBegin(GL_QUADS); + /* Front Face */ + glNormal3f(0.0f, 0.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); + glVertex3f(-1.0f, -1.0f, 1.0f); + glTexCoord2f(1.0f, 0.0f); + glVertex3f(1.0f, -1.0f, 1.0f); + glTexCoord2f(1.0f, 1.0f); + glVertex3f(1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(-1.0f, 1.0f, 1.0f); + /* Back Face */ + glNormal3f(0.0f, 0.0f, -1.0f); + glTexCoord2f(1.0f, 0.0f); + glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(1.0f, 1.0f); + glVertex3f(-1.0f, 1.0f, -1.0f); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(1.0f, 1.0f, -1.0f); + glTexCoord2f(0.0f, 0.0f); + glVertex3f(1.0f, -1.0f, -1.0f); + /* Top Face */ + glNormal3f(0.0f, 1.0f, 0.0f); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(-1.0f, 1.0f, -1.0f); + glTexCoord2f(0.0f, 0.0f); + glVertex3f(-1.0f, 1.0f, 1.0f); + glTexCoord2f(1.0f, 0.0f); + glVertex3f(1.0f, 1.0f, 1.0f); + glTexCoord2f(1.0f, 1.0f); + glVertex3f(1.0f, 1.0f, -1.0f); + /* Bottom Face */ + glNormal3f(0.0f, -1.0f, 0.0f); + glTexCoord2f(1.0f, 1.0f); + glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(1.0f, -1.0f, -1.0f); + glTexCoord2f(0.0f, 0.0f); + glVertex3f(1.0f, -1.0f, 1.0f); + glTexCoord2f(1.0f, 0.0f); + glVertex3f(-1.0f, -1.0f, 1.0f); + /* Right face */ + glNormal3f(1.0f, 0.0f, 0.0f); + glTexCoord2f(1.0f, 0.0f); + glVertex3f(1.0f, -1.0f, -1.0f); + glTexCoord2f(1.0f, 1.0f); + glVertex3f(1.0f, 1.0f, -1.0f); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); + glVertex3f(1.0f, -1.0f, 1.0f); + /* Left Face */ + glNormal3f(-1.0f, 0.0f, 0.0f); + glTexCoord2f(0.0f, 0.0f); + glVertex3f(-1.0f, -1.0f, -1.0f); + glTexCoord2f(1.0f, 0.0f); + glVertex3f(-1.0f, -1.0f, 1.0f); + glTexCoord2f(1.0f, 1.0f); + glVertex3f(-1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(-1.0f, 1.0f, -1.0f); + glEnd(); + + xrot += xspeed; + yrot += yspeed; +} + +int main(int argc, char **argv) { + GLboolean xp = GL_FALSE; + GLboolean yp = GL_FALSE; + + printf("nehe16 beginning\n"); + + /* Get basic stuff initialized */ + glKosInit(); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(45.0f, 640.0f / 480.0f, 0.1f, 100.0f); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glEnable(GL_TEXTURE_2D); + glShadeModel(GL_SMOOTH); + glClearColor(0.5f, 0.5f, 0.5f, 1.0f); + glClearDepth(1.0f); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + + glColor4f(1.0f, 1.0f, 1.0f, 0.5); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + + /* Enable Lighting and GL_LIGHT0 */ + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + + /* Set up the fog */ + glFogi(GL_FOG_MODE, fogMode[fogType]); /* Fog Mode */ + glFogfv(GL_FOG_COLOR, fogColor); /* Set Fog Color */ + glFogf(GL_FOG_DENSITY, 0.35f); /* How Dense The Fog is */ + glHint(GL_FOG_HINT, GL_DONT_CARE); /* Fog Hint Value */ + glFogf(GL_FOG_START, 0.0f); /* Fog Start Depth */ + glFogf(GL_FOG_END, 5.0f); /* Fog End Depth */ + glEnable(GL_FOG); /* Enables GL_FOG */ + + /* Set up the textures */ + texture = glTextureLoadPVR(IMG_PATH, 0, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + while(1) { +#ifdef __DREAMCAST__ + maple_device_t *cont; + cont_state_t *state; + + cont = maple_enum_type(0, MAPLE_FUNC_CONTROLLER); + + /* Check key status */ + state = (cont_state_t *)maple_dev_status(cont); + + if(!state) { + printf("Error reading controller\n"); + break; + } + + if(state->start) + break; + + if(state->a) { + if(z >= -15.0f) z -= 0.02f; + } + + if(state->b) { + if(z <= 0.0f) z += 0.02f; + } + + if(state->x && !xp) { + xp = GL_TRUE; + fogType = (fogType + 1) % 3; + glFogi(GL_FOG_MODE, fogMode[fogType]); + printf("%s\n", cfogMode[fogType]); + } + + if(!state->x) + xp = GL_FALSE; + + if(state->y && !yp) { + yp = GL_TRUE; + fog = !fog; + } + + if(!state->y) + yp = GL_FALSE; + + if(state->dpad_up) + xspeed -= 0.1f; + + if(state->dpad_down) + xspeed += 0.1f; + + if(state->dpad_left) + yspeed -= 0.1f; + + if(state->dpad_right) + yspeed += 0.1f; +#endif + + /* Switch fog off/on */ + if(fog) { + glEnable(GL_FOG); + } + else { + glDisable(GL_FOG); + } + + /* Draw the GL "scene" */ + draw_gl(); + + /* Finish the frame */ + glKosSwapBuffers(); + } + + return 0; +} + + diff --git a/samples/nehe16/pvr-texture.c b/samples/nehe16/pvr-texture.c new file mode 100644 index 0000000..c9d5f92 --- /dev/null +++ b/samples/nehe16/pvr-texture.c @@ -0,0 +1,176 @@ +/* + KallistiOS 2.0.0 + + pvr-texture.c + (c)2014 Josh PH3NOM Pearson + + Load A PVR Texture to the PVR using Open GL +*/ +#include +#include +#include + +#include "GL/gl.h" +#include "GL/glu.h" +#include "GL/glkos.h" +#include "GL/glext.h" + +#define PVR_HDR_SIZE 0x20 +#define MAX(x, y) ((x > y) ? x : y) + +static GLuint PVR_TextureHeight(unsigned char *HDR); +static GLuint PVR_TextureWidth(unsigned char *HDR); +static GLuint PVR_TextureFormat(unsigned char *HDR); + +static GLuint _glGetMipmapLevelCount(GLuint width, GLuint height) { + return 1 + floor(log2(MAX(width, height))); +} + +static GLuint _glGetMipmapDataSize(GLuint width, GLuint height) { + GLuint size = 0; + + GLuint i = 0; + + for(; i < _glGetMipmapLevelCount(width, height); ++i) { + size += (width * height * 2); + + if(width > 1) { + width /= 2; + } + + if(height > 1) { + height /= 2; + } + } + + return size; +} + +/* Load a PVR texture file into memory, and then bind the texture to Open GL. + fname is the name of the PVR texture file to be opened and read. + isMipMapped should be passed as 1 if the texture contains MipMap levels, 0 otherwise. + glMipMap should be passed as 1 if Open GL should calculate the Mipmap levels, 0 otherwise */ +GLuint glTextureLoadPVR(char *fname, unsigned char isMipMapped, unsigned char glMipMap) { + FILE *tex = NULL; + uint16_t *TEX0 = NULL; + uint8_t HDR[PVR_HDR_SIZE]; + GLuint texID, texSize, texW, texH, texFormat; + + /* Open the PVR texture file, and get its file size */ + tex = fopen(fname, "rb"); + + if(tex == NULL) { + printf("FILE READ ERROR: %s\n", fname); + + while(1); + } + + fseek(tex, 0, SEEK_END); + texSize = ftell(tex) - PVR_HDR_SIZE; + fseek(tex, 0, SEEK_SET); + + /* Read in the PVR texture file header */ + fread(HDR, 1, PVR_HDR_SIZE, tex); + + /* Extract some information from the PVR texture file header */ + texW = PVR_TextureWidth(HDR); + texH = PVR_TextureHeight(HDR); + texFormat = PVR_TextureFormat(HDR); + + /* Allocate Some Memory for the texture. If we are using Open GL to build the MipMap, + we need to allocate enough space to hold the MipMap texture levels. */ + if(!isMipMapped && glMipMap) + TEX0 = malloc(_glGetMipmapDataSize(texW, texH)); + else + TEX0 = malloc(texSize); + + fread(TEX0, 1, texSize, tex); /* Read in the PVR texture data */ + + /* Generate and bind a texture as normal for Open GL */ + glGenTextures(1, &texID); + glBindTexture(GL_TEXTURE_2D, texID); + + if(texFormat != GL_UNSIGNED_SHORT_5_6_5) + glCompressedTexImage2DARB(GL_TEXTURE_2D, + 0, + texFormat, + texW, + texH, + 0, + texSize, + TEX0); + else { + fprintf(stderr, "%x\n", texFormat); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGB, + texW, texH, + 0, + GL_RGB, + texFormat, + TEX0); + if(!isMipMapped && glMipMap) + glGenerateMipmapEXT(GL_TEXTURE_2D); + } + + free(TEX0); + + return texID; +} + +static GLuint PVR_TextureFormat(unsigned char *HDR) { + GLuint color = (GLuint)HDR[PVR_HDR_SIZE - 8]; + GLuint format = (GLuint)HDR[PVR_HDR_SIZE - 7]; + + GLboolean twiddled = format == 0x01; + GLboolean compressed = (format == 0x10 || format == 0x03); + + if(compressed) { + if(twiddled) { + switch(color) { + case 0x0: { + return GL_COMPRESSED_ARGB_1555_VQ_TWID_KOS; + } break; + case 0x01: { + return GL_COMPRESSED_RGB_565_VQ_TWID_KOS; + } break; + case 0x02: { + return GL_COMPRESSED_ARGB_4444_VQ_TWID_KOS; + } + break; + default: + fprintf(stderr, "Invalid texture format"); + return 0; + } + } else { + switch(color) { + case 0: { + return GL_COMPRESSED_ARGB_1555_VQ_KOS; + } break; + case 1: { + return GL_COMPRESSED_RGB_565_VQ_KOS; + } break; + case 2: { + return GL_COMPRESSED_ARGB_4444_VQ_KOS; + } + break; + default: + fprintf(stderr, "Invalid texture format"); + return 0; + } + } + } else { + if(color == 1) { + return GL_UNSIGNED_SHORT_5_6_5; + } + return 0; + } +} + +static GLuint PVR_TextureWidth(unsigned char *HDR) { + return (GLuint)HDR[PVR_HDR_SIZE - 4] | HDR[PVR_HDR_SIZE - 3] << 8; +} + +static GLuint PVR_TextureHeight(unsigned char *HDR) { + return (GLuint)HDR[PVR_HDR_SIZE - 2] | HDR[PVR_HDR_SIZE - 1] << 8; +} diff --git a/samples/nehe16/romdisk/glass.pvr b/samples/nehe16/romdisk/glass.pvr new file mode 100644 index 0000000..3f81ec0 Binary files /dev/null and b/samples/nehe16/romdisk/glass.pvr differ