Merge branch 'fog_fix' into 'master'

Fixed OpenGL Fog implementation + Added New Sample

See merge request simulant/GLdc!155
This commit is contained in:
Luke Benstead 2025-02-22 08:23:16 +00:00
commit 6bd42b87b9
5 changed files with 544 additions and 55 deletions

View File

@ -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)

175
GL/fog.c
View File

@ -1,73 +1,138 @@
/*
TODO: glGetX() fog values
*/
#include <limits.h>
#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);
}
}

247
samples/nehe16/main.c Normal file
View File

@ -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 <stdio.h>
#include <GL/glkos.h>
#include <GL/gl.h>
#include <GL/glu.h>
#ifdef __DREAMCAST__
#include <kos.h>
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;
}

View File

@ -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;
}

Binary file not shown.