diff --git a/CMakeLists.txt b/CMakeLists.txt index 8578f94..5b04ceb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,6 +132,7 @@ gen_sample(nehe05 samples/nehe05/main.c) gen_sample(nehe06 samples/nehe06/main.c) 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(ortho2d samples/ortho2d/main.c) gen_sample(paletted samples/paletted/main.c) gen_sample(paletted_pcx samples/paletted_pcx/main.c) diff --git a/samples/nehe08/main.c b/samples/nehe08/main.c new file mode 100644 index 0000000..fb7ff6f --- /dev/null +++ b/samples/nehe08/main.c @@ -0,0 +1,239 @@ +/* + KallistiOS 2.0.0 + + nehe08.c + (c)2021 Luke Benstead + (c)2014 Josh Pearson + (c)2001 Benoit Miller + (c)2000 Jeff Molofee +*/ + +#ifdef __DREAMCAST__ +#include <kos.h> +#endif + +#include <GL/gl.h> +#include <GL/glu.h> +#include <GL/glkos.h> + +/* Simple OpenGL example to demonstrate blending and lighting. + + Essentially the same thing as NeHe's lesson08 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 filtering, and button Y toggles alpha + blending. +*/ + +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 filter; /* Which Filter To Use */ +static GLuint texture[2]; /* Storage For Two Textures */ + +/* 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[filter]); + + 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; +} + +#ifdef __DREAMCAST__ +extern uint8 romdisk[]; +KOS_INIT_ROMDISK(romdisk); +#endif + +int main(int argc, char **argv) { +#ifdef __DREAMCAST__ + maple_device_t *cont; + cont_state_t *state; +#endif + + GLboolean xp = GL_FALSE; + GLboolean yp = GL_FALSE; + GLboolean blend = GL_FALSE; + + printf("nehe08 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.0f, 0.0f, 0.0f, 0.5f); + 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 textures */ + texture[0] = glTextureLoadPVR("/rd/glass.pvr", 0, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + texture[1] = glTextureLoadPVR("/rd/glass.pvr", 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__ + 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->buttons & CONT_START) + break; + + if(state->buttons & CONT_A) + z -= 0.02f; + + if(state->buttons & CONT_B) + z += 0.02f; + + if((state->buttons & CONT_X) && !xp) { + xp = GL_TRUE; + filter += 1; + + if(filter > 1) + filter = 0; + } + + if(!(state->buttons & CONT_X)) + xp = GL_FALSE; + + if((state->buttons & CONT_Y) && !yp) { + yp = GL_TRUE; + blend = !blend; + } + + if(!(state->buttons & CONT_Y)) + yp = GL_FALSE; + + if(state->buttons & CONT_DPAD_UP) + xspeed -= 0.01f; + + if(state->buttons & CONT_DPAD_DOWN) + xspeed += 0.01f; + + if(state->buttons & CONT_DPAD_LEFT) + yspeed -= 0.01f; + + if(state->buttons & CONT_DPAD_RIGHT) + yspeed += 0.01f; +#endif + + /* Switch to the blended polygon list if needed */ + if(blend) { + glEnable(GL_BLEND); + glDepthMask(0); + } + else { + glDisable(GL_BLEND); + glDepthMask(1); + } + + /* Draw the GL "scene" */ + draw_gl(); + + /* Finish the frame */ + glKosSwapBuffers(); + } + + return 0; +} + diff --git a/samples/nehe08/pvr-texture.c b/samples/nehe08/pvr-texture.c new file mode 100644 index 0000000..c9d5f92 --- /dev/null +++ b/samples/nehe08/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 <stdlib.h> +#include <stdio.h> +#include <stdint.h> + +#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/nehe08/romdisk/glass.pvr b/samples/nehe08/romdisk/glass.pvr new file mode 100644 index 0000000..3f81ec0 Binary files /dev/null and b/samples/nehe08/romdisk/glass.pvr differ