SRB2/src/hardware/r_opengl/r_opengl.c
2021-05-22 20:08:11 -03:00

3516 lines
99 KiB
C

// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2021 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file r_opengl.c
/// \brief OpenGL API for Sonic Robo Blast 2
#if defined (_WIN32)
//#define WIN32_LEAN_AND_MEAN
#define RPC_NO_WINDOWS_H
#include <windows.h>
#endif
#undef GETTEXT
#ifdef __GNUC__
#include <unistd.h>
#endif
#include <stdarg.h>
#include <math.h>
#include "r_opengl.h"
#include "r_vbo.h"
#if defined (HWRENDER) && !defined (NOROPENGL)
struct GLRGBAFloat
{
GLfloat red;
GLfloat green;
GLfloat blue;
GLfloat alpha;
};
typedef struct GLRGBAFloat GLRGBAFloat;
static const GLubyte white[4] = { 255, 255, 255, 255 };
// ==========================================================================
// CONSTANTS
// ==========================================================================
// With OpenGL 1.1+, the first texture should be 1
static GLuint NOTEXTURE_NUM = 0;
#define N_PI_DEMI (M_PIl/2.0f) //(1.5707963268f)
#define ASPECT_RATIO (1.0f) //(320.0f/200.0f)
#define FAR_CLIPPING_PLANE 32768.0f // Draw further! Tails 01-21-2001
static float NEAR_CLIPPING_PLANE = NZCLIP_PLANE;
// **************************************************************************
// GLOBALS
// **************************************************************************
static GLuint tex_downloaded = 0;
static GLfloat fov = 90.0f;
static FBITFIELD CurrentPolyFlags;
// Linked list of all textures.
static FTextureInfo *TexCacheTail = NULL;
static FTextureInfo *TexCacheHead = NULL;
static RGBA_t *textureBuffer = NULL;
static size_t textureBufferSize = 0;
RGBA_t myPaletteData[256];
GLint screen_width = 0; // used by Draw2DLine()
GLint screen_height = 0;
GLbyte screen_depth = 0;
GLint textureformatGL = 0;
GLint maximumAnisotropy = 0;
static GLboolean MipMap = GL_FALSE;
static GLint min_filter = GL_LINEAR;
static GLint mag_filter = GL_LINEAR;
static GLint anisotropic_filter = 0;
static boolean model_lighting = false;
const GLubyte *gl_version = NULL;
const GLubyte *gl_renderer = NULL;
const GLubyte *gl_extensions = NULL;
//Hurdler: 04/10/2000: added for the kick ass coronas as Boris wanted;-)
static GLfloat modelMatrix[16];
static GLfloat projMatrix[16];
static GLint viewport[4];
// Sryder: NextTexAvail is broken for these because palette changes or changes to the texture filter or antialiasing
// flush all of the stored textures, leaving them unavailable at times such as between levels
// These need to start at 0 and be set to their number, and be reset to 0 when deleted so that intel GPUs
// can know when the textures aren't there, as textures are always considered resident in their virtual memory
static GLuint screentexture = 0;
static GLuint startScreenWipe = 0;
static GLuint endScreenWipe = 0;
static GLuint finalScreenTexture = 0;
// shortcut for ((float)1/i)
static const GLfloat byte2float[256] = {
0.000000f, 0.003922f, 0.007843f, 0.011765f, 0.015686f, 0.019608f, 0.023529f, 0.027451f,
0.031373f, 0.035294f, 0.039216f, 0.043137f, 0.047059f, 0.050980f, 0.054902f, 0.058824f,
0.062745f, 0.066667f, 0.070588f, 0.074510f, 0.078431f, 0.082353f, 0.086275f, 0.090196f,
0.094118f, 0.098039f, 0.101961f, 0.105882f, 0.109804f, 0.113725f, 0.117647f, 0.121569f,
0.125490f, 0.129412f, 0.133333f, 0.137255f, 0.141176f, 0.145098f, 0.149020f, 0.152941f,
0.156863f, 0.160784f, 0.164706f, 0.168627f, 0.172549f, 0.176471f, 0.180392f, 0.184314f,
0.188235f, 0.192157f, 0.196078f, 0.200000f, 0.203922f, 0.207843f, 0.211765f, 0.215686f,
0.219608f, 0.223529f, 0.227451f, 0.231373f, 0.235294f, 0.239216f, 0.243137f, 0.247059f,
0.250980f, 0.254902f, 0.258824f, 0.262745f, 0.266667f, 0.270588f, 0.274510f, 0.278431f,
0.282353f, 0.286275f, 0.290196f, 0.294118f, 0.298039f, 0.301961f, 0.305882f, 0.309804f,
0.313726f, 0.317647f, 0.321569f, 0.325490f, 0.329412f, 0.333333f, 0.337255f, 0.341176f,
0.345098f, 0.349020f, 0.352941f, 0.356863f, 0.360784f, 0.364706f, 0.368627f, 0.372549f,
0.376471f, 0.380392f, 0.384314f, 0.388235f, 0.392157f, 0.396078f, 0.400000f, 0.403922f,
0.407843f, 0.411765f, 0.415686f, 0.419608f, 0.423529f, 0.427451f, 0.431373f, 0.435294f,
0.439216f, 0.443137f, 0.447059f, 0.450980f, 0.454902f, 0.458824f, 0.462745f, 0.466667f,
0.470588f, 0.474510f, 0.478431f, 0.482353f, 0.486275f, 0.490196f, 0.494118f, 0.498039f,
0.501961f, 0.505882f, 0.509804f, 0.513726f, 0.517647f, 0.521569f, 0.525490f, 0.529412f,
0.533333f, 0.537255f, 0.541177f, 0.545098f, 0.549020f, 0.552941f, 0.556863f, 0.560784f,
0.564706f, 0.568627f, 0.572549f, 0.576471f, 0.580392f, 0.584314f, 0.588235f, 0.592157f,
0.596078f, 0.600000f, 0.603922f, 0.607843f, 0.611765f, 0.615686f, 0.619608f, 0.623529f,
0.627451f, 0.631373f, 0.635294f, 0.639216f, 0.643137f, 0.647059f, 0.650980f, 0.654902f,
0.658824f, 0.662745f, 0.666667f, 0.670588f, 0.674510f, 0.678431f, 0.682353f, 0.686275f,
0.690196f, 0.694118f, 0.698039f, 0.701961f, 0.705882f, 0.709804f, 0.713726f, 0.717647f,
0.721569f, 0.725490f, 0.729412f, 0.733333f, 0.737255f, 0.741177f, 0.745098f, 0.749020f,
0.752941f, 0.756863f, 0.760784f, 0.764706f, 0.768627f, 0.772549f, 0.776471f, 0.780392f,
0.784314f, 0.788235f, 0.792157f, 0.796078f, 0.800000f, 0.803922f, 0.807843f, 0.811765f,
0.815686f, 0.819608f, 0.823529f, 0.827451f, 0.831373f, 0.835294f, 0.839216f, 0.843137f,
0.847059f, 0.850980f, 0.854902f, 0.858824f, 0.862745f, 0.866667f, 0.870588f, 0.874510f,
0.878431f, 0.882353f, 0.886275f, 0.890196f, 0.894118f, 0.898039f, 0.901961f, 0.905882f,
0.909804f, 0.913726f, 0.917647f, 0.921569f, 0.925490f, 0.929412f, 0.933333f, 0.937255f,
0.941177f, 0.945098f, 0.949020f, 0.952941f, 0.956863f, 0.960784f, 0.964706f, 0.968628f,
0.972549f, 0.976471f, 0.980392f, 0.984314f, 0.988235f, 0.992157f, 0.996078f, 1.000000f
};
// -----------------+
// GL_DBG_Printf : Output debug messages to debug log if DEBUG_TO_FILE is defined,
// : else do nothing
// -----------------+
#ifdef DEBUG_TO_FILE
FILE *gllogstream;
#endif
FUNCPRINTF void GL_DBG_Printf(const char *format, ...)
{
#ifdef DEBUG_TO_FILE
char str[4096] = "";
va_list arglist;
if (!gllogstream)
gllogstream = fopen("ogllog.txt", "w");
va_start(arglist, format);
vsnprintf(str, 4096, format, arglist);
va_end(arglist);
fwrite(str, strlen(str), 1, gllogstream);
#else
(void)format;
#endif
}
// -----------------+
// GL_MSG_Warning : Raises a warning.
// -----------------+
static void GL_MSG_Warning(const char *format, ...)
{
char str[4096] = "";
va_list arglist;
va_start(arglist, format);
vsnprintf(str, 4096, format, arglist);
va_end(arglist);
#ifdef HAVE_SDL
CONS_Alert(CONS_WARNING, "%s", str);
#endif
#ifdef DEBUG_TO_FILE
if (!gllogstream)
gllogstream = fopen("ogllog.txt", "w");
fwrite(str, strlen(str), 1, gllogstream);
#endif
}
// -----------------+
// GL_MSG_Error : Raises an error.
// -----------------+
static void GL_MSG_Error(const char *format, ...)
{
char str[4096] = "";
va_list arglist;
va_start(arglist, format);
vsnprintf(str, 4096, format, arglist);
va_end(arglist);
#ifdef HAVE_SDL
CONS_Alert(CONS_ERROR, "%s", str);
#endif
#ifdef DEBUG_TO_FILE
if (!gllogstream)
gllogstream = fopen("ogllog.txt", "w");
fwrite(str, strlen(str), 1, gllogstream);
#endif
}
#ifdef STATIC_OPENGL
/* 1.0 functions */
/* Miscellaneous */
#define pglClearColor glClearColor
//glClear
#define pglColorMask glColorMask
#define pglAlphaFunc glAlphaFunc
#define pglBlendFunc glBlendFunc
#define pglCullFace glCullFace
#define pglPolygonOffset glPolygonOffset
#define pglScissor glScissor
#define pglEnable glEnable
#define pglDisable glDisable
#define pglGetFloatv glGetFloatv
//glGetIntegerv
//glGetString
#define pglHint glHint
/* Depth Buffer */
#define pglClearDepth glClearDepth
#define pglDepthFunc glDepthFunc
#define pglDepthMask glDepthMask
#define pglDepthRange glDepthRange
/* Transformation */
#define pglMatrixMode glMatrixMode
#define pglViewport glViewport
#define pglPushMatrix glPushMatrix
#define pglPopMatrix glPopMatrix
#define pglLoadIdentity glLoadIdentity
#define pglMultMatrixf glMultMatrixf
#define pglRotatef glRotatef
#define pglScalef glScalef
#define pglTranslatef glTranslatef
/* Drawing Functions */
#define pglColor4ubv glColor4ubv
#define pglVertexPointer glVertexPointer
#define pglNormalPointer glNormalPointer
#define pglTexCoordPointer glTexCoordPointer
#define pglColorPointer glColorPointer
#define pglDrawArrays glDrawArrays
#define pglDrawElements glDrawElements
#define pglEnableClientState glEnableClientState
#define pglDisableClientState glDisableClientState
/* Lighting */
#define pglShadeModel glShadeModel
#define pglLightfv glLightfv
#define pglLightModelfv glLightModelfv
#define pglMaterialfv glMaterialfv
#define pglMateriali glMateriali
/* Raster functions */
#define pglPixelStorei glPixelStorei
#define pglReadPixels glReadPixels
/* Texture mapping */
#define pglTexEnvi glTexEnvi
#define pglTexParameteri glTexParameteri
#define pglTexImage2D glTexImage2D
#define pglTexSubImage2D glTexSubImage2D
/* 1.1 functions */
/* texture objects */ //GL_EXT_texture_object
#define pglGenTextures glGenTextures
#define pglDeleteTextures glDeleteTextures
#define pglBindTexture glBindTexture
/* texture mapping */ //GL_EXT_copy_texture
#define pglCopyTexImage2D glCopyTexImage2D
#define pglCopyTexSubImage2D glCopyTexSubImage2D
#else //!STATIC_OPENGL
/* 1.0 functions */
/* Miscellaneous */
typedef void (APIENTRY * PFNglClearColor) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
static PFNglClearColor pglClearColor;
typedef void (APIENTRY * PFNglColorMask) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
static PFNglColorMask pglColorMask;
typedef void (APIENTRY * PFNglAlphaFunc) (GLenum func, GLclampf ref);
static PFNglAlphaFunc pglAlphaFunc;
typedef void (APIENTRY * PFNglBlendFunc) (GLenum sfactor, GLenum dfactor);
static PFNglBlendFunc pglBlendFunc;
typedef void (APIENTRY * PFNglCullFace) (GLenum mode);
static PFNglCullFace pglCullFace;
typedef void (APIENTRY * PFNglPolygonOffset) (GLfloat factor, GLfloat units);
static PFNglPolygonOffset pglPolygonOffset;
typedef void (APIENTRY * PFNglScissor) (GLint x, GLint y, GLsizei width, GLsizei height);
static PFNglScissor pglScissor;
typedef void (APIENTRY * PFNglEnable) (GLenum cap);
static PFNglEnable pglEnable;
typedef void (APIENTRY * PFNglDisable) (GLenum cap);
static PFNglDisable pglDisable;
typedef void (APIENTRY * PFNglGetFloatv) (GLenum pname, GLfloat *params);
static PFNglGetFloatv pglGetFloatv;
/* Depth Buffer */
typedef void (APIENTRY * PFNglClearDepth) (GLclampd depth);
static PFNglClearDepth pglClearDepth;
typedef void (APIENTRY * PFNglDepthFunc) (GLenum func);
static PFNglDepthFunc pglDepthFunc;
typedef void (APIENTRY * PFNglDepthMask) (GLboolean flag);
static PFNglDepthMask pglDepthMask;
typedef void (APIENTRY * PFNglDepthRange) (GLclampd near_val, GLclampd far_val);
static PFNglDepthRange pglDepthRange;
/* Transformation */
typedef void (APIENTRY * PFNglMatrixMode) (GLenum mode);
static PFNglMatrixMode pglMatrixMode;
typedef void (APIENTRY * PFNglViewport) (GLint x, GLint y, GLsizei width, GLsizei height);
static PFNglViewport pglViewport;
typedef void (APIENTRY * PFNglPushMatrix) (void);
static PFNglPushMatrix pglPushMatrix;
typedef void (APIENTRY * PFNglPopMatrix) (void);
static PFNglPopMatrix pglPopMatrix;
typedef void (APIENTRY * PFNglLoadIdentity) (void);
static PFNglLoadIdentity pglLoadIdentity;
typedef void (APIENTRY * PFNglMultMatrixf) (const GLfloat *m);
static PFNglMultMatrixf pglMultMatrixf;
typedef void (APIENTRY * PFNglRotatef) (GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
static PFNglRotatef pglRotatef;
typedef void (APIENTRY * PFNglScalef) (GLfloat x, GLfloat y, GLfloat z);
static PFNglScalef pglScalef;
typedef void (APIENTRY * PFNglTranslatef) (GLfloat x, GLfloat y, GLfloat z);
static PFNglTranslatef pglTranslatef;
/* Drawing Functions */
typedef void (APIENTRY * PFNglColor4ubv) (const GLubyte *v);
static PFNglColor4ubv pglColor4ubv;
typedef void (APIENTRY * PFNglVertexPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
static PFNglVertexPointer pglVertexPointer;
typedef void (APIENTRY * PFNglNormalPointer) (GLenum type, GLsizei stride, const GLvoid *pointer);
static PFNglNormalPointer pglNormalPointer;
typedef void (APIENTRY * PFNglTexCoordPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
static PFNglTexCoordPointer pglTexCoordPointer;
typedef void (APIENTRY * PFNglColorPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
static PFNglColorPointer pglColorPointer;
typedef void (APIENTRY * PFNglDrawArrays) (GLenum mode, GLint first, GLsizei count);
static PFNglDrawArrays pglDrawArrays;
typedef void (APIENTRY * PFNglDrawElements) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
static PFNglDrawElements pglDrawElements;
typedef void (APIENTRY * PFNglEnableClientState) (GLenum cap);
static PFNglEnableClientState pglEnableClientState;
typedef void (APIENTRY * PFNglDisableClientState) (GLenum cap);
static PFNglDisableClientState pglDisableClientState;
/* Lighting */
typedef void (APIENTRY * PFNglShadeModel) (GLenum mode);
static PFNglShadeModel pglShadeModel;
typedef void (APIENTRY * PFNglLightfv) (GLenum light, GLenum pname, GLfloat *params);
static PFNglLightfv pglLightfv;
typedef void (APIENTRY * PFNglLightModelfv) (GLenum pname, GLfloat *params);
static PFNglLightModelfv pglLightModelfv;
typedef void (APIENTRY * PFNglMaterialfv) (GLint face, GLenum pname, GLfloat *params);
static PFNglMaterialfv pglMaterialfv;
typedef void (APIENTRY * PFNglMateriali) (GLint face, GLenum pname, GLint param);
static PFNglMateriali pglMateriali;
/* Raster functions */
typedef void (APIENTRY * PFNglPixelStorei) (GLenum pname, GLint param);
static PFNglPixelStorei pglPixelStorei;
typedef void (APIENTRY * PFNglReadPixels) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);
static PFNglReadPixels pglReadPixels;
/* Texture mapping */
typedef void (APIENTRY * PFNglTexEnvi) (GLenum target, GLenum pname, GLint param);
static PFNglTexEnvi pglTexEnvi;
typedef void (APIENTRY * PFNglTexParameteri) (GLenum target, GLenum pname, GLint param);
static PFNglTexParameteri pglTexParameteri;
typedef void (APIENTRY * PFNglTexImage2D) (GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
static PFNglTexImage2D pglTexImage2D;
typedef void (APIENTRY * PFNglTexSubImage2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);
static PFNglTexSubImage2D pglTexSubImage2D;
/* 1.1 functions */
/* texture objects */ //GL_EXT_texture_object
typedef void (APIENTRY * PFNglGenTextures) (GLsizei n, const GLuint *textures);
static PFNglGenTextures pglGenTextures;
typedef void (APIENTRY * PFNglDeleteTextures) (GLsizei n, const GLuint *textures);
static PFNglDeleteTextures pglDeleteTextures;
typedef void (APIENTRY * PFNglBindTexture) (GLenum target, GLuint texture);
static PFNglBindTexture pglBindTexture;
/* texture mapping */ //GL_EXT_copy_texture
typedef void (APIENTRY * PFNglCopyTexImage2D) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
static PFNglCopyTexImage2D pglCopyTexImage2D;
typedef void (APIENTRY * PFNglCopyTexSubImage2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
static PFNglCopyTexSubImage2D pglCopyTexSubImage2D;
#endif
/* GLU functions */
typedef GLint (APIENTRY * PFNgluBuild2DMipmaps) (GLenum target, GLint internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *data);
static PFNgluBuild2DMipmaps pgluBuild2DMipmaps;
/* 1.3 functions for multitexturing */
typedef void (APIENTRY *PFNglActiveTexture) (GLenum);
static PFNglActiveTexture pglActiveTexture;
typedef void (APIENTRY *PFNglMultiTexCoord2f) (GLenum, GLfloat, GLfloat);
static PFNglMultiTexCoord2f pglMultiTexCoord2f;
typedef void (APIENTRY *PFNglMultiTexCoord2fv) (GLenum target, const GLfloat *v);
static PFNglMultiTexCoord2fv pglMultiTexCoord2fv;
typedef void (APIENTRY *PFNglClientActiveTexture) (GLenum);
static PFNglClientActiveTexture pglClientActiveTexture;
/* 1.5 functions for buffers */
typedef void (APIENTRY * PFNglGenBuffers) (GLsizei n, GLuint *buffers);
static PFNglGenBuffers pglGenBuffers;
typedef void (APIENTRY * PFNglBindBuffer) (GLenum target, GLuint buffer);
static PFNglBindBuffer pglBindBuffer;
typedef void (APIENTRY * PFNglBufferData) (GLenum target, GLsizei size, const GLvoid *data, GLenum usage);
static PFNglBufferData pglBufferData;
typedef void (APIENTRY * PFNglDeleteBuffers) (GLsizei n, const GLuint *buffers);
static PFNglDeleteBuffers pglDeleteBuffers;
/* 2.0 functions */
typedef void (APIENTRY * PFNglBlendEquation) (GLenum mode);
static PFNglBlendEquation pglBlendEquation;
/* 1.2 Parms */
/* GL_CLAMP_TO_EDGE_EXT */
#ifndef GL_CLAMP_TO_EDGE
#define GL_CLAMP_TO_EDGE 0x812F
#endif
#ifndef GL_TEXTURE_MIN_LOD
#define GL_TEXTURE_MIN_LOD 0x813A
#endif
#ifndef GL_TEXTURE_MAX_LOD
#define GL_TEXTURE_MAX_LOD 0x813B
#endif
/* 1.3 GL_TEXTUREi */
#ifndef GL_TEXTURE0
#define GL_TEXTURE0 0x84C0
#endif
#ifndef GL_TEXTURE1
#define GL_TEXTURE1 0x84C1
#endif
/* 1.5 Parms */
#ifndef GL_ARRAY_BUFFER
#define GL_ARRAY_BUFFER 0x8892
#endif
#ifndef GL_STATIC_DRAW
#define GL_STATIC_DRAW 0x88E4
#endif
boolean SetupGLfunc(void)
{
#ifndef STATIC_OPENGL
#define GETOPENGLFUNC(func, proc) \
func = GetGLFunc(#proc); \
if (!func) \
{ \
GL_MSG_Warning("failed to get OpenGL function: %s", #proc); \
} \
GETOPENGLFUNC(pglClearColor, glClearColor)
GETOPENGLFUNC(pglClear, glClear)
GETOPENGLFUNC(pglColorMask, glColorMask)
GETOPENGLFUNC(pglAlphaFunc, glAlphaFunc)
GETOPENGLFUNC(pglBlendFunc, glBlendFunc)
GETOPENGLFUNC(pglCullFace, glCullFace)
GETOPENGLFUNC(pglPolygonOffset, glPolygonOffset)
GETOPENGLFUNC(pglScissor, glScissor)
GETOPENGLFUNC(pglEnable, glEnable)
GETOPENGLFUNC(pglDisable, glDisable)
GETOPENGLFUNC(pglGetFloatv, glGetFloatv)
GETOPENGLFUNC(pglGetIntegerv, glGetIntegerv)
GETOPENGLFUNC(pglGetString, glGetString)
GETOPENGLFUNC(pglClearDepth, glClearDepth)
GETOPENGLFUNC(pglDepthFunc, glDepthFunc)
GETOPENGLFUNC(pglDepthMask, glDepthMask)
GETOPENGLFUNC(pglDepthRange, glDepthRange)
GETOPENGLFUNC(pglMatrixMode, glMatrixMode)
GETOPENGLFUNC(pglViewport, glViewport)
GETOPENGLFUNC(pglPushMatrix, glPushMatrix)
GETOPENGLFUNC(pglPopMatrix, glPopMatrix)
GETOPENGLFUNC(pglLoadIdentity, glLoadIdentity)
GETOPENGLFUNC(pglMultMatrixf, glMultMatrixf)
GETOPENGLFUNC(pglRotatef, glRotatef)
GETOPENGLFUNC(pglScalef, glScalef)
GETOPENGLFUNC(pglTranslatef, glTranslatef)
GETOPENGLFUNC(pglColor4ubv, glColor4ubv)
GETOPENGLFUNC(pglVertexPointer, glVertexPointer)
GETOPENGLFUNC(pglNormalPointer, glNormalPointer)
GETOPENGLFUNC(pglTexCoordPointer, glTexCoordPointer)
GETOPENGLFUNC(pglColorPointer, glColorPointer)
GETOPENGLFUNC(pglDrawArrays, glDrawArrays)
GETOPENGLFUNC(pglDrawElements, glDrawElements)
GETOPENGLFUNC(pglEnableClientState, glEnableClientState)
GETOPENGLFUNC(pglDisableClientState, glDisableClientState)
GETOPENGLFUNC(pglShadeModel, glShadeModel)
GETOPENGLFUNC(pglLightfv, glLightfv)
GETOPENGLFUNC(pglLightModelfv, glLightModelfv)
GETOPENGLFUNC(pglMaterialfv, glMaterialfv)
GETOPENGLFUNC(pglMateriali, glMateriali)
GETOPENGLFUNC(pglPixelStorei, glPixelStorei)
GETOPENGLFUNC(pglReadPixels, glReadPixels)
GETOPENGLFUNC(pglTexEnvi, glTexEnvi)
GETOPENGLFUNC(pglTexParameteri, glTexParameteri)
GETOPENGLFUNC(pglTexImage2D, glTexImage2D)
GETOPENGLFUNC(pglTexSubImage2D, glTexSubImage2D)
GETOPENGLFUNC(pglGenTextures, glGenTextures)
GETOPENGLFUNC(pglDeleteTextures, glDeleteTextures)
GETOPENGLFUNC(pglBindTexture, glBindTexture)
GETOPENGLFUNC(pglCopyTexImage2D, glCopyTexImage2D)
GETOPENGLFUNC(pglCopyTexSubImage2D, glCopyTexSubImage2D)
#undef GETOPENGLFUNC
#endif
return true;
}
static boolean gl_shadersenabled = false;
static hwdshaderoption_t gl_allowshaders = HWD_SHADEROPTION_OFF;
#ifdef GL_SHADERS
typedef GLuint (APIENTRY *PFNglCreateShader) (GLenum);
typedef void (APIENTRY *PFNglShaderSource) (GLuint, GLsizei, const GLchar**, GLint*);
typedef void (APIENTRY *PFNglCompileShader) (GLuint);
typedef void (APIENTRY *PFNglGetShaderiv) (GLuint, GLenum, GLint*);
typedef void (APIENTRY *PFNglGetShaderInfoLog) (GLuint, GLsizei, GLsizei*, GLchar*);
typedef void (APIENTRY *PFNglDeleteShader) (GLuint);
typedef GLuint (APIENTRY *PFNglCreateProgram) (void);
typedef void (APIENTRY *PFNglDeleteProgram) (GLuint);
typedef void (APIENTRY *PFNglAttachShader) (GLuint, GLuint);
typedef void (APIENTRY *PFNglLinkProgram) (GLuint);
typedef void (APIENTRY *PFNglGetProgramiv) (GLuint, GLenum, GLint*);
typedef void (APIENTRY *PFNglUseProgram) (GLuint);
typedef void (APIENTRY *PFNglUniform1i) (GLint, GLint);
typedef void (APIENTRY *PFNglUniform1f) (GLint, GLfloat);
typedef void (APIENTRY *PFNglUniform2f) (GLint, GLfloat, GLfloat);
typedef void (APIENTRY *PFNglUniform3f) (GLint, GLfloat, GLfloat, GLfloat);
typedef void (APIENTRY *PFNglUniform4f) (GLint, GLfloat, GLfloat, GLfloat, GLfloat);
typedef void (APIENTRY *PFNglUniform1fv) (GLint, GLsizei, const GLfloat*);
typedef void (APIENTRY *PFNglUniform2fv) (GLint, GLsizei, const GLfloat*);
typedef void (APIENTRY *PFNglUniform3fv) (GLint, GLsizei, const GLfloat*);
typedef GLint (APIENTRY *PFNglGetUniformLocation) (GLuint, const GLchar*);
static PFNglCreateShader pglCreateShader;
static PFNglShaderSource pglShaderSource;
static PFNglCompileShader pglCompileShader;
static PFNglGetShaderiv pglGetShaderiv;
static PFNglGetShaderInfoLog pglGetShaderInfoLog;
static PFNglDeleteShader pglDeleteShader;
static PFNglCreateProgram pglCreateProgram;
static PFNglDeleteProgram pglDeleteProgram;
static PFNglAttachShader pglAttachShader;
static PFNglLinkProgram pglLinkProgram;
static PFNglGetProgramiv pglGetProgramiv;
static PFNglUseProgram pglUseProgram;
static PFNglUniform1i pglUniform1i;
static PFNglUniform1f pglUniform1f;
static PFNglUniform2f pglUniform2f;
static PFNglUniform3f pglUniform3f;
static PFNglUniform4f pglUniform4f;
static PFNglUniform1fv pglUniform1fv;
static PFNglUniform2fv pglUniform2fv;
static PFNglUniform3fv pglUniform3fv;
static PFNglGetUniformLocation pglGetUniformLocation;
// 13062019
typedef enum
{
// lighting
gluniform_poly_color,
gluniform_tint_color,
gluniform_fade_color,
gluniform_lighting,
gluniform_fade_start,
gluniform_fade_end,
// misc. (custom shaders)
gluniform_leveltime,
gluniform_max,
} gluniform_t;
typedef struct gl_shader_s
{
GLuint program;
GLint uniforms[gluniform_max+1];
boolean custom;
} gl_shader_t;
static gl_shader_t gl_shaders[HWR_MAXSHADERS];
static gl_shader_t gl_usershaders[HWR_MAXSHADERS];
static shadersource_t gl_customshaders[HWR_MAXSHADERS];
// 09102020
typedef struct gl_shaderstate_s
{
gl_shader_t *current;
GLuint type;
GLuint program;
boolean changed;
} gl_shaderstate_t;
static gl_shaderstate_t gl_shaderstate;
// Shader info
static INT32 shader_leveltime = 0;
// Lactozilla: Shader functions
static boolean Shader_CompileProgram(gl_shader_t *shader, GLint i, const GLchar *vert_shader, const GLchar *frag_shader);
static void Shader_CompileError(const char *message, GLuint program, INT32 shadernum);
static void Shader_SetUniforms(FSurfaceInfo *Surface, GLRGBAFloat *poly, GLRGBAFloat *tint, GLRGBAFloat *fade);
static GLRGBAFloat shader_defaultcolor = {1.0f, 1.0f, 1.0f, 1.0f};
// ================
// Vertex shaders
// ================
//
// Generic vertex shader
//
#define GLSL_DEFAULT_VERTEX_SHADER \
"void main()\n" \
"{\n" \
"gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;\n" \
"gl_FrontColor = gl_Color;\n" \
"gl_TexCoord[0].xy = gl_MultiTexCoord0.xy;\n" \
"gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n" \
"}\0"
// replicates the way fixed function lighting is used by the model lighting option,
// stores the lighting result to gl_Color
// (ambient lighting of 0.75 and diffuse lighting from above)
#define GLSL_MODEL_LIGHTING_VERTEX_SHADER \
"void main()\n" \
"{\n" \
"float nDotVP = dot(gl_Normal, vec3(0, 1, 0));\n" \
"float light = 0.75 + max(nDotVP, 0.0);\n" \
"gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;\n" \
"gl_FrontColor = vec4(light, light, light, 1.0);\n" \
"gl_TexCoord[0].xy = gl_MultiTexCoord0.xy;\n" \
"gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n" \
"}\0"
// ==================
// Fragment shaders
// ==================
//
// Generic fragment shader
//
#define GLSL_DEFAULT_FRAGMENT_SHADER \
"uniform sampler2D tex;\n" \
"uniform vec4 poly_color;\n" \
"void main(void) {\n" \
"gl_FragColor = texture2D(tex, gl_TexCoord[0].st) * poly_color;\n" \
"}\0"
//
// Software fragment shader
//
#define GLSL_DOOM_COLORMAP \
"float R_DoomColormap(float light, float z)\n" \
"{\n" \
"float lightnum = clamp(light / 17.0, 0.0, 15.0);\n" \
"float lightz = clamp(z / 16.0, 0.0, 127.0);\n" \
"float startmap = (15.0 - lightnum) * 4.0;\n" \
"float scale = 160.0 / (lightz + 1.0);\n" \
"return startmap - scale * 0.5;\n" \
"}\n"
#define GLSL_DOOM_LIGHT_EQUATION \
"float R_DoomLightingEquation(float light)\n" \
"{\n" \
"float z = gl_FragCoord.z / gl_FragCoord.w;\n" \
"float colormap = floor(R_DoomColormap(light, z)) + 0.5;\n" \
"return clamp(colormap, 0.0, 31.0) / 32.0;\n" \
"}\n"
#define GLSL_SOFTWARE_TINT_EQUATION \
"if (tint_color.a > 0.0) {\n" \
"float color_bright = sqrt((base_color.r * base_color.r) + (base_color.g * base_color.g) + (base_color.b * base_color.b));\n" \
"float strength = sqrt(9.0 * tint_color.a);\n" \
"final_color.r = clamp((color_bright * (tint_color.r * strength)) + (base_color.r * (1.0 - strength)), 0.0, 1.0);\n" \
"final_color.g = clamp((color_bright * (tint_color.g * strength)) + (base_color.g * (1.0 - strength)), 0.0, 1.0);\n" \
"final_color.b = clamp((color_bright * (tint_color.b * strength)) + (base_color.b * (1.0 - strength)), 0.0, 1.0);\n" \
"}\n"
#define GLSL_SOFTWARE_FADE_EQUATION \
"float darkness = R_DoomLightingEquation(lighting);\n" \
"if (fade_start != 0.0 || fade_end != 31.0) {\n" \
"float fs = fade_start / 31.0;\n" \
"float fe = fade_end / 31.0;\n" \
"float fd = fe - fs;\n" \
"darkness = clamp((darkness - fs) * (1.0 / fd), 0.0, 1.0);\n" \
"}\n" \
"final_color = mix(final_color, fade_color, darkness);\n"
#define GLSL_SOFTWARE_FRAGMENT_SHADER \
"uniform sampler2D tex;\n" \
"uniform vec4 poly_color;\n" \
"uniform vec4 tint_color;\n" \
"uniform vec4 fade_color;\n" \
"uniform float lighting;\n" \
"uniform float fade_start;\n" \
"uniform float fade_end;\n" \
GLSL_DOOM_COLORMAP \
GLSL_DOOM_LIGHT_EQUATION \
"void main(void) {\n" \
"vec4 texel = texture2D(tex, gl_TexCoord[0].st);\n" \
"vec4 base_color = texel * poly_color;\n" \
"vec4 final_color = base_color;\n" \
GLSL_SOFTWARE_TINT_EQUATION \
GLSL_SOFTWARE_FADE_EQUATION \
"final_color.a = texel.a * poly_color.a;\n" \
"gl_FragColor = final_color;\n" \
"}\0"
// same as above but multiplies results with the lighting value from the
// accompanying vertex shader (stored in gl_Color)
#define GLSL_SOFTWARE_MODEL_LIGHTING_FRAGMENT_SHADER \
"uniform sampler2D tex;\n" \
"uniform vec4 poly_color;\n" \
"uniform vec4 tint_color;\n" \
"uniform vec4 fade_color;\n" \
"uniform float lighting;\n" \
"uniform float fade_start;\n" \
"uniform float fade_end;\n" \
GLSL_DOOM_COLORMAP \
GLSL_DOOM_LIGHT_EQUATION \
"void main(void) {\n" \
"vec4 texel = texture2D(tex, gl_TexCoord[0].st);\n" \
"vec4 base_color = texel * poly_color;\n" \
"vec4 final_color = base_color;\n" \
GLSL_SOFTWARE_TINT_EQUATION \
GLSL_SOFTWARE_FADE_EQUATION \
"final_color *= gl_Color;\n" \
"final_color.a = texel.a * poly_color.a;\n" \
"gl_FragColor = final_color;\n" \
"}\0"
//
// Water surface shader
//
// Mostly guesstimated, rather than the rest being built off Software science.
// Still needs to distort things underneath/around the water...
//
#define GLSL_WATER_FRAGMENT_SHADER \
"uniform sampler2D tex;\n" \
"uniform vec4 poly_color;\n" \
"uniform vec4 tint_color;\n" \
"uniform vec4 fade_color;\n" \
"uniform float lighting;\n" \
"uniform float fade_start;\n" \
"uniform float fade_end;\n" \
"uniform float leveltime;\n" \
"const float freq = 0.025;\n" \
"const float amp = 0.025;\n" \
"const float speed = 2.0;\n" \
"const float pi = 3.14159;\n" \
GLSL_DOOM_COLORMAP \
GLSL_DOOM_LIGHT_EQUATION \
"void main(void) {\n" \
"float z = (gl_FragCoord.z / gl_FragCoord.w) / 2.0;\n" \
"float a = -pi * (z * freq) + (leveltime * speed);\n" \
"float sdistort = sin(a) * amp;\n" \
"float cdistort = cos(a) * amp;\n" \
"vec4 texel = texture2D(tex, vec2(gl_TexCoord[0].s - sdistort, gl_TexCoord[0].t - cdistort));\n" \
"vec4 base_color = texel * poly_color;\n" \
"vec4 final_color = base_color;\n" \
GLSL_SOFTWARE_TINT_EQUATION \
GLSL_SOFTWARE_FADE_EQUATION \
"final_color.a = texel.a * poly_color.a;\n" \
"gl_FragColor = final_color;\n" \
"}\0"
//
// Fog block shader
//
// Alpha of the planes themselves are still slightly off -- see HWR_FogBlockAlpha
//
#define GLSL_FOG_FRAGMENT_SHADER \
"uniform vec4 tint_color;\n" \
"uniform vec4 fade_color;\n" \
"uniform float lighting;\n" \
"uniform float fade_start;\n" \
"uniform float fade_end;\n" \
GLSL_DOOM_COLORMAP \
GLSL_DOOM_LIGHT_EQUATION \
"void main(void) {\n" \
"vec4 base_color = gl_Color;\n" \
"vec4 final_color = base_color;\n" \
GLSL_SOFTWARE_TINT_EQUATION \
GLSL_SOFTWARE_FADE_EQUATION \
"gl_FragColor = final_color;\n" \
"}\0"
//
// Sky fragment shader
// Modulates poly_color with gl_Color
//
#define GLSL_SKY_FRAGMENT_SHADER \
"uniform sampler2D tex;\n" \
"uniform vec4 poly_color;\n" \
"void main(void) {\n" \
"gl_FragColor = texture2D(tex, gl_TexCoord[0].st) * gl_Color * poly_color;\n" \
"}\0"
// ================
// Shader sources
// ================
static struct {
const char *vertex;
const char *fragment;
} const gl_shadersources[] = {
// Default shader
{GLSL_DEFAULT_VERTEX_SHADER, GLSL_DEFAULT_FRAGMENT_SHADER},
// Floor shader
{GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER},
// Wall shader
{GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER},
// Sprite shader
{GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER},
// Model shader
{GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER},
// Model shader + diffuse lighting from above
{GLSL_MODEL_LIGHTING_VERTEX_SHADER, GLSL_SOFTWARE_MODEL_LIGHTING_FRAGMENT_SHADER},
// Water shader
{GLSL_DEFAULT_VERTEX_SHADER, GLSL_WATER_FRAGMENT_SHADER},
// Fog shader
{GLSL_DEFAULT_VERTEX_SHADER, GLSL_FOG_FRAGMENT_SHADER},
// Sky shader
{GLSL_DEFAULT_VERTEX_SHADER, GLSL_SKY_FRAGMENT_SHADER},
{NULL, NULL},
};
#endif // GL_SHADERS
void SetupGLFunc4(void)
{
pglActiveTexture = GetGLFunc("glActiveTexture");
pglMultiTexCoord2f = GetGLFunc("glMultiTexCoord2f");
pglClientActiveTexture = GetGLFunc("glClientActiveTexture");
pglMultiTexCoord2fv = GetGLFunc("glMultiTexCoord2fv");
/* 1.5 funcs */
pglGenBuffers = GetGLFunc("glGenBuffers");
pglBindBuffer = GetGLFunc("glBindBuffer");
pglBufferData = GetGLFunc("glBufferData");
pglDeleteBuffers = GetGLFunc("glDeleteBuffers");
/* 2.0 funcs */
pglBlendEquation = GetGLFunc("glBlendEquation");
#ifdef GL_SHADERS
pglCreateShader = GetGLFunc("glCreateShader");
pglShaderSource = GetGLFunc("glShaderSource");
pglCompileShader = GetGLFunc("glCompileShader");
pglGetShaderiv = GetGLFunc("glGetShaderiv");
pglGetShaderInfoLog = GetGLFunc("glGetShaderInfoLog");
pglDeleteShader = GetGLFunc("glDeleteShader");
pglCreateProgram = GetGLFunc("glCreateProgram");
pglDeleteProgram = GetGLFunc("glDeleteProgram");
pglAttachShader = GetGLFunc("glAttachShader");
pglLinkProgram = GetGLFunc("glLinkProgram");
pglGetProgramiv = GetGLFunc("glGetProgramiv");
pglUseProgram = GetGLFunc("glUseProgram");
pglUniform1i = GetGLFunc("glUniform1i");
pglUniform1f = GetGLFunc("glUniform1f");
pglUniform2f = GetGLFunc("glUniform2f");
pglUniform3f = GetGLFunc("glUniform3f");
pglUniform4f = GetGLFunc("glUniform4f");
pglUniform1fv = GetGLFunc("glUniform1fv");
pglUniform2fv = GetGLFunc("glUniform2fv");
pglUniform3fv = GetGLFunc("glUniform3fv");
pglGetUniformLocation = GetGLFunc("glGetUniformLocation");
#endif
// GLU
pgluBuild2DMipmaps = GetGLFunc("gluBuild2DMipmaps");
}
EXPORT boolean HWRAPI(CompileShaders) (void)
{
#ifdef GL_SHADERS
GLint i;
if (!pglUseProgram)
return false;
gl_customshaders[SHADER_DEFAULT].vertex = NULL;
gl_customshaders[SHADER_DEFAULT].fragment = NULL;
for (i = 0; gl_shadersources[i].vertex && gl_shadersources[i].fragment; i++)
{
gl_shader_t *shader, *usershader;
const GLchar *vert_shader = gl_shadersources[i].vertex;
const GLchar *frag_shader = gl_shadersources[i].fragment;
if (i >= HWR_MAXSHADERS)
break;
shader = &gl_shaders[i];
usershader = &gl_usershaders[i];
if (shader->program)
pglDeleteProgram(shader->program);
if (usershader->program)
pglDeleteProgram(usershader->program);
shader->program = 0;
usershader->program = 0;
if (!Shader_CompileProgram(shader, i, vert_shader, frag_shader))
shader->program = 0;
// Compile custom shader
if ((i == SHADER_DEFAULT) || !(gl_customshaders[i].vertex || gl_customshaders[i].fragment))
continue;
// 18032019
if (gl_customshaders[i].vertex)
vert_shader = gl_customshaders[i].vertex;
if (gl_customshaders[i].fragment)
frag_shader = gl_customshaders[i].fragment;
if (!Shader_CompileProgram(usershader, i, vert_shader, frag_shader))
{
GL_MSG_Warning("CompileShaders: Could not compile custom shader program for %s\n", HWR_GetShaderName(i));
usershader->program = 0;
}
}
return true;
#else
return false;
#endif
}
//
// Shader info
// Those are given to the uniforms.
//
EXPORT void HWRAPI(SetShaderInfo) (hwdshaderinfo_t info, INT32 value)
{
#ifdef GL_SHADERS
switch (info)
{
case HWD_SHADERINFO_LEVELTIME:
shader_leveltime = value;
break;
default:
break;
}
#else
(void)info;
(void)value;
#endif
}
//
// Custom shader loading
//
EXPORT void HWRAPI(LoadCustomShader) (int number, char *code, size_t size, boolean isfragment)
{
#ifdef GL_SHADERS
shadersource_t *shader;
if (!pglUseProgram)
return;
if (number < 1 || number > HWR_MAXSHADERS)
I_Error("LoadCustomShader: cannot load shader %d (min 1, max %d)", number, HWR_MAXSHADERS);
else if (code == NULL)
I_Error("LoadCustomShader: empty shader");
shader = &gl_customshaders[number];
#define COPYSHADER(source) { \
if (shader->source) \
free(shader->source); \
shader->source = malloc(size+1); \
strncpy(shader->source, code, size); \
shader->source[size] = 0; \
}
if (isfragment)
COPYSHADER(fragment)
else
COPYSHADER(vertex)
#else
(void)number;
(void)shader;
(void)size;
(void)fragment;
#endif
}
EXPORT void HWRAPI(SetShader) (int type)
{
#ifdef GL_SHADERS
if (gl_allowshaders != HWD_SHADEROPTION_OFF)
{
gl_shader_t *shader = gl_shaderstate.current;
// If using model lighting, set the appropriate shader.
// However don't override a custom shader.
if (type == SHADER_MODEL && model_lighting
&& !(gl_shaders[SHADER_MODEL].custom && !gl_shaders[SHADER_MODEL_LIGHTING].custom))
type = SHADER_MODEL_LIGHTING;
if ((shader == NULL) || (GLuint)type != gl_shaderstate.type)
{
gl_shader_t *baseshader = &gl_shaders[type];
gl_shader_t *usershader = &gl_usershaders[type];
if (usershader->program)
shader = (gl_allowshaders == HWD_SHADEROPTION_NOCUSTOM) ? baseshader : usershader;
else
shader = baseshader;
gl_shaderstate.current = shader;
gl_shaderstate.type = type;
gl_shaderstate.changed = true;
}
if (gl_shaderstate.program != shader->program)
{
gl_shaderstate.program = shader->program;
gl_shaderstate.changed = true;
}
gl_shadersenabled = (shader->program != 0);
return;
}
#else
(void)type;
#endif
gl_shadersenabled = false;
}
EXPORT void HWRAPI(UnSetShader) (void)
{
#ifdef GL_SHADERS
gl_shaderstate.current = NULL;
gl_shaderstate.type = 0;
gl_shaderstate.program = 0;
if (pglUseProgram)
pglUseProgram(0);
#endif
gl_shadersenabled = false;
}
EXPORT void HWRAPI(CleanShaders) (void)
{
INT32 i;
for (i = 1; i < HWR_MAXSHADERS; i++)
{
shadersource_t *shader = &gl_customshaders[i];
if (shader->vertex)
free(shader->vertex);
if (shader->fragment)
free(shader->fragment);
shader->vertex = NULL;
shader->fragment = NULL;
}
}
// -----------------+
// SetNoTexture : Disable texture
// -----------------+
static void SetNoTexture(void)
{
// Disable texture.
if (tex_downloaded != NOTEXTURE_NUM)
{
if (NOTEXTURE_NUM == 0)
pglGenTextures(1, &NOTEXTURE_NUM);
pglBindTexture(GL_TEXTURE_2D, NOTEXTURE_NUM);
tex_downloaded = NOTEXTURE_NUM;
}
}
static void GLPerspective(GLfloat fovy, GLfloat aspect)
{
GLfloat m[4][4] =
{
{ 1.0f, 0.0f, 0.0f, 0.0f},
{ 0.0f, 1.0f, 0.0f, 0.0f},
{ 0.0f, 0.0f, 1.0f,-1.0f},
{ 0.0f, 0.0f, 0.0f, 0.0f},
};
const GLfloat zNear = NEAR_CLIPPING_PLANE;
const GLfloat zFar = FAR_CLIPPING_PLANE;
const GLfloat radians = (GLfloat)(fovy / 2.0f * M_PIl / 180.0f);
const GLfloat sine = sin(radians);
const GLfloat deltaZ = zFar - zNear;
GLfloat cotangent;
if ((fabsf((float)deltaZ) < 1.0E-36f) || fpclassify(sine) == FP_ZERO || fpclassify(aspect) == FP_ZERO)
{
return;
}
cotangent = cosf(radians) / sine;
m[0][0] = cotangent / aspect;
m[1][1] = cotangent;
m[2][2] = -(zFar + zNear) / deltaZ;
m[3][2] = -2.0f * zNear * zFar / deltaZ;
pglMultMatrixf(&m[0][0]);
}
static void GLProject(GLfloat objX, GLfloat objY, GLfloat objZ,
GLfloat* winX, GLfloat* winY, GLfloat* winZ)
{
GLfloat in[4], out[4];
int i;
for (i=0; i<4; i++)
{
out[i] =
objX * modelMatrix[0*4+i] +
objY * modelMatrix[1*4+i] +
objZ * modelMatrix[2*4+i] +
modelMatrix[3*4+i];
}
for (i=0; i<4; i++)
{
in[i] =
out[0] * projMatrix[0*4+i] +
out[1] * projMatrix[1*4+i] +
out[2] * projMatrix[2*4+i] +
out[3] * projMatrix[3*4+i];
}
if (fpclassify(in[3]) == FP_ZERO) return;
in[0] /= in[3];
in[1] /= in[3];
in[2] /= in[3];
/* Map x, y and z to range 0-1 */
in[0] = in[0] * 0.5f + 0.5f;
in[1] = in[1] * 0.5f + 0.5f;
in[2] = in[2] * 0.5f + 0.5f;
/* Map x,y to viewport */
in[0] = in[0] * viewport[2] + viewport[0];
in[1] = in[1] * viewport[3] + viewport[1];
*winX=in[0];
*winY=in[1];
*winZ=in[2];
}
// -----------------+
// SetModelView :
// -----------------+
void SetModelView(GLint w, GLint h)
{
// GL_DBG_Printf("SetModelView(): %dx%d\n", (int)w, (int)h);
// The screen textures need to be flushed if the width or height change so that they be remade for the correct size
if (screen_width != w || screen_height != h)
FlushScreenTextures();
screen_width = w;
screen_height = h;
pglViewport(0, 0, w, h);
pglMatrixMode(GL_PROJECTION);
pglLoadIdentity();
pglMatrixMode(GL_MODELVIEW);
pglLoadIdentity();
GLPerspective(fov, ASPECT_RATIO);
//pglScalef(1.0f, 320.0f/200.0f, 1.0f); // gl_scalefrustum (ORIGINAL_ASPECT)
// added for new coronas' code (without depth buffer)
pglGetIntegerv(GL_VIEWPORT, viewport);
pglGetFloatv(GL_PROJECTION_MATRIX, projMatrix);
}
// -----------------+
// SetStates : Set permanent states
// -----------------+
void SetStates(void)
{
#ifdef GL_LIGHT_MODEL_AMBIENT
GLfloat LightDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
#endif
// GL_DBG_Printf("SetStates()\n");
// Hurdler: not necessary, is it?
pglShadeModel(GL_SMOOTH); // iterate vertice colors
//pglShadeModel(GL_FLAT);
pglEnable(GL_TEXTURE_2D); // two-dimensional texturing
pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
pglEnable(GL_ALPHA_TEST);
pglAlphaFunc(GL_NOTEQUAL, 0.0f);
//pglBlendFunc(GL_ONE, GL_ZERO); // copy pixel to frame buffer (opaque)
pglEnable(GL_BLEND); // enable color blending
pglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
//pglDisable(GL_DITHER); // faB: ??? (undocumented in OpenGL 1.1)
// Hurdler: yes, it is!
pglEnable(GL_DEPTH_TEST); // check the depth buffer
pglDepthMask(GL_TRUE); // enable writing to depth buffer
pglClearDepth(1.0f);
pglDepthRange(0.0f, 1.0f);
pglDepthFunc(GL_LEQUAL);
// this set CurrentPolyFlags to the actual configuration
CurrentPolyFlags = 0xffffffff;
SetBlend(0);
tex_downloaded = 0;
SetNoTexture();
pglPolygonOffset(-1.0f, -1.0f);
//pglEnable(GL_CULL_FACE);
//pglCullFace(GL_FRONT);
pglDisable(GL_FOG);
// Lighting for models
#ifdef GL_LIGHT_MODEL_AMBIENT
pglLightModelfv(GL_LIGHT_MODEL_AMBIENT, LightDiffuse);
pglEnable(GL_LIGHT0);
#endif
// bp : when no t&l :)
pglLoadIdentity();
pglScalef(1.0f, 1.0f, -1.0f);
pglGetFloatv(GL_MODELVIEW_MATRIX, modelMatrix); // added for new coronas' code (without depth buffer)
}
// -----------------+
// DeleteTexture : Deletes a texture from the GPU and frees its data
// -----------------+
EXPORT void HWRAPI(DeleteTexture) (GLMipmap_t *pTexInfo)
{
FTextureInfo *head = TexCacheHead;
if (!pTexInfo)
return;
else if (pTexInfo->downloaded)
pglDeleteTextures(1, (GLuint *)&pTexInfo->downloaded);
while (head)
{
if (head->downloaded == pTexInfo->downloaded)
{
if (head->next)
head->next->prev = head->prev;
else // no next -> tail is being deleted -> update TexCacheTail
TexCacheTail = head->prev;
if (head->prev)
head->prev->next = head->next;
else // no prev -> head is being deleted -> update TexCacheHead
TexCacheHead = head->next;
free(head);
break;
}
head = head->next;
}
pTexInfo->downloaded = 0;
}
// -----------------+
// Flush : flush OpenGL textures
// : Clear list of downloaded mipmaps
// -----------------+
void Flush(void)
{
//GL_DBG_Printf ("HWR_Flush()\n");
while (TexCacheHead)
{
FTextureInfo *pTexInfo = TexCacheHead;
GLMipmap_t *texture = pTexInfo->texture;
if (pTexInfo->downloaded)
{
pglDeleteTextures(1, (GLuint *)&pTexInfo->downloaded);
pTexInfo->downloaded = 0;
}
if (texture)
texture->downloaded = 0;
TexCacheHead = pTexInfo->next;
free(pTexInfo);
}
TexCacheTail = TexCacheHead = NULL; //Hurdler: well, TexCacheHead is already NULL
tex_downloaded = 0;
free(textureBuffer);
textureBuffer = NULL;
textureBufferSize = 0;
}
// -----------------+
// isExtAvailable : Look if an OpenGL extension is available
// Returns : true if extension available
// -----------------+
INT32 isExtAvailable(const char *extension, const GLubyte *start)
{
GLubyte *where, *terminator;
if (!extension || !start) return 0;
where = (GLubyte *) strchr(extension, ' ');
if (where || *extension == '\0')
return 0;
for (;;)
{
where = (GLubyte *) strstr((const char *) start, extension);
if (!where)
break;
terminator = where + strlen(extension);
if (where == start || *(where - 1) == ' ')
if (*terminator == ' ' || *terminator == '\0')
return 1;
start = terminator;
}
return 0;
}
// -----------------+
// Init : Initialise the OpenGL interface API
// -----------------+
EXPORT boolean HWRAPI(Init) (void)
{
return LoadGL();
}
// -----------------+
// ClearMipMapCache : Flush OpenGL textures from memory
// -----------------+
EXPORT void HWRAPI(ClearMipMapCache) (void)
{
// GL_DBG_Printf ("HWR_Flush(exe)\n");
Flush();
}
// -----------------+
// ReadRect : Read a rectangle region of the truecolor framebuffer
// : store pixels as 16bit 565 RGB
// Returns : 16bit 565 RGB pixel array stored in dst_data
// -----------------+
EXPORT void HWRAPI(ReadRect) (INT32 x, INT32 y, INT32 width, INT32 height,
INT32 dst_stride, UINT16 * dst_data)
{
INT32 i;
// GL_DBG_Printf ("ReadRect()\n");
if (dst_stride == width*3)
{
GLubyte*top = (GLvoid*)dst_data, *bottom = top + dst_stride * (height - 1);
GLubyte *row = malloc(dst_stride);
if (!row) return;
pglPixelStorei(GL_PACK_ALIGNMENT, 1);
pglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, dst_data);
pglPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for(i = 0; i < height/2; i++)
{
memcpy(row, top, dst_stride);
memcpy(top, bottom, dst_stride);
memcpy(bottom, row, dst_stride);
top += dst_stride;
bottom -= dst_stride;
}
free(row);
}
else
{
INT32 j;
GLubyte *image = malloc(width*height*3*sizeof (*image));
if (!image) return;
pglPixelStorei(GL_PACK_ALIGNMENT, 1);
pglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, image);
pglPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for (i = height-1; i >= 0; i--)
{
for (j = 0; j < width; j++)
{
dst_data[(height-1-i)*width+j] =
(UINT16)(
((image[(i*width+j)*3]>>3)<<11) |
((image[(i*width+j)*3+1]>>2)<<5) |
((image[(i*width+j)*3+2]>>3)));
}
}
free(image);
}
}
// -----------------+
// GClipRect : Defines the 2D hardware clipping window
// -----------------+
EXPORT void HWRAPI(GClipRect) (INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float nearclip)
{
// GL_DBG_Printf ("GClipRect(%d, %d, %d, %d)\n", minx, miny, maxx, maxy);
pglViewport(minx, screen_height-maxy, maxx-minx, maxy-miny);
NEAR_CLIPPING_PLANE = nearclip;
//pglScissor(minx, screen_height-maxy, maxx-minx, maxy-miny);
pglMatrixMode(GL_PROJECTION);
pglLoadIdentity();
GLPerspective(fov, ASPECT_RATIO);
pglMatrixMode(GL_MODELVIEW);
// added for new coronas' code (without depth buffer)
pglGetIntegerv(GL_VIEWPORT, viewport);
pglGetFloatv(GL_PROJECTION_MATRIX, projMatrix);
}
// -----------------+
// ClearBuffer : Clear the color/alpha/depth buffer(s)
// -----------------+
EXPORT void HWRAPI(ClearBuffer) (FBOOLEAN ColorMask,
FBOOLEAN DepthMask,
FRGBAFloat * ClearColor)
{
// GL_DBG_Printf ("ClearBuffer(%d)\n", alpha);
GLbitfield ClearMask = 0;
if (ColorMask)
{
if (ClearColor)
pglClearColor(ClearColor->red,
ClearColor->green,
ClearColor->blue,
ClearColor->alpha);
ClearMask |= GL_COLOR_BUFFER_BIT;
}
if (DepthMask)
{
pglClearDepth(1.0f); //Hurdler: all that are permanen states
pglDepthRange(0.0f, 1.0f);
pglDepthFunc(GL_LEQUAL);
ClearMask |= GL_DEPTH_BUFFER_BIT;
}
SetBlend(DepthMask ? PF_Occlude | CurrentPolyFlags : CurrentPolyFlags&~PF_Occlude);
pglClear(ClearMask);
pglEnableClientState(GL_VERTEX_ARRAY); // We always use this one
pglEnableClientState(GL_TEXTURE_COORD_ARRAY); // And mostly this one, too
}
// -----------------+
// HWRAPI Draw2DLine: Render a 2D line
// -----------------+
EXPORT void HWRAPI(Draw2DLine) (F2DCoord * v1,
F2DCoord * v2,
RGBA_t Color)
{
// GL_DBG_Printf ("DrawLine() (%f %f %f) %d\n", v1->x, -v1->y, -v1->z, v1->argb);
GLfloat p[12];
GLfloat dx, dy;
GLfloat angle;
// BP: we should reflect the new state in our variable
//SetBlend(PF_Modulated|PF_NoTexture);
pglDisable(GL_TEXTURE_2D);
// This is the preferred, 'modern' way of rendering lines -- creating a polygon.
if (fabsf(v2->x - v1->x) > FLT_EPSILON)
angle = (float)atan((v2->y-v1->y)/(v2->x-v1->x));
else
angle = (float)N_PI_DEMI;
dx = (float)sin(angle) / (float)screen_width;
dy = (float)cos(angle) / (float)screen_height;
p[0] = v1->x - dx; p[1] = -(v1->y + dy); p[2] = 1;
p[3] = v2->x - dx; p[4] = -(v2->y + dy); p[5] = 1;
p[6] = v2->x + dx; p[7] = -(v2->y - dy); p[8] = 1;
p[9] = v1->x + dx; p[10] = -(v1->y - dy); p[11] = 1;
pglDisableClientState(GL_TEXTURE_COORD_ARRAY);
pglColor4ubv((GLubyte*)&Color.s);
pglVertexPointer(3, GL_FLOAT, 0, p);
pglDrawArrays(GL_TRIANGLE_FAN, 0, 4);
pglEnableClientState(GL_TEXTURE_COORD_ARRAY);
pglEnable(GL_TEXTURE_2D);
}
// -----------------+
// SetBlend : Set render mode
// -----------------+
// PF_Masked - we could use an ALPHA_TEST of GL_EQUAL, and alpha ref of 0,
// is it faster when pixels are discarded ?
static void Clamp2D(GLenum pname)
{
pglTexParameteri(GL_TEXTURE_2D, pname, GL_CLAMP); // fallback clamp
pglTexParameteri(GL_TEXTURE_2D, pname, GL_CLAMP_TO_EDGE);
}
static void SetBlendEquation(GLenum mode)
{
if (pglBlendEquation)
pglBlendEquation(mode);
}
static void SetBlendMode(FBITFIELD flags)
{
// Set blending function
switch (flags)
{
case PF_Translucent & PF_Blending:
pglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // alpha = level of transparency
break;
case PF_Masked & PF_Blending:
// Hurdler: does that mean lighting is only made by alpha src?
// it sounds ok, but not for polygonsmooth
pglBlendFunc(GL_SRC_ALPHA, GL_ZERO); // 0 alpha = holes in texture
break;
case PF_Additive & PF_Blending:
case PF_Subtractive & PF_Blending:
case PF_ReverseSubtract & PF_Blending:
pglBlendFunc(GL_SRC_ALPHA, GL_ONE); // src * alpha + dest
break;
case PF_Environment & PF_Blending:
pglBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
break;
case PF_Multiplicative & PF_Blending:
pglBlendFunc(GL_DST_COLOR, GL_ZERO);
break;
case PF_Fog & PF_Fog:
// Sryder: Fog
// multiplies input colour by input alpha, and destination colour by input colour, then adds them
pglBlendFunc(GL_SRC_ALPHA, GL_SRC_COLOR);
break;
default: // must be 0, otherwise it's an error
// No blending
pglBlendFunc(GL_ONE, GL_ZERO); // the same as no blending
break;
}
// Set blending equation
switch (flags)
{
case PF_Subtractive & PF_Blending:
SetBlendEquation(GL_FUNC_SUBTRACT);
break;
case PF_ReverseSubtract & PF_Blending:
// good for shadow
// not really but what else ?
SetBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
break;
default:
SetBlendEquation(GL_FUNC_ADD);
break;
}
// Alpha test
switch (flags)
{
case PF_Masked & PF_Blending:
pglAlphaFunc(GL_GREATER, 0.5f);
break;
case PF_Translucent & PF_Blending:
case PF_Additive & PF_Blending:
case PF_Subtractive & PF_Blending:
case PF_ReverseSubtract & PF_Blending:
case PF_Environment & PF_Blending:
case PF_Multiplicative & PF_Blending:
pglAlphaFunc(GL_NOTEQUAL, 0.0f);
break;
case PF_Fog & PF_Fog:
pglAlphaFunc(GL_ALWAYS, 0.0f); // Don't discard zero alpha fragments
break;
default:
pglAlphaFunc(GL_GREATER, 0.5f);
break;
}
}
EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags)
{
FBITFIELD Xor;
Xor = CurrentPolyFlags^PolyFlags;
if (Xor & (PF_Blending|PF_RemoveYWrap|PF_ForceWrapX|PF_ForceWrapY|PF_Occlude|PF_NoTexture|PF_Modulated|PF_NoDepthTest|PF_Decal|PF_Invisible))
{
if (Xor & PF_Blending) // if blending mode must be changed
SetBlendMode(PolyFlags & PF_Blending);
if (Xor & PF_NoAlphaTest)
{
if (PolyFlags & PF_NoAlphaTest)
pglDisable(GL_ALPHA_TEST);
else
pglEnable(GL_ALPHA_TEST); // discard 0 alpha pixels (holes in texture)
}
if (Xor & PF_Decal)
{
if (PolyFlags & PF_Decal)
pglEnable(GL_POLYGON_OFFSET_FILL);
else
pglDisable(GL_POLYGON_OFFSET_FILL);
}
if (Xor & PF_NoDepthTest)
{
if (PolyFlags & PF_NoDepthTest)
pglDepthFunc(GL_ALWAYS); //pglDisable(GL_DEPTH_TEST);
else
pglDepthFunc(GL_LEQUAL); //pglEnable(GL_DEPTH_TEST);
}
if (Xor & PF_RemoveYWrap)
{
if (PolyFlags & PF_RemoveYWrap)
Clamp2D(GL_TEXTURE_WRAP_T);
}
if (Xor & PF_ForceWrapX)
{
if (PolyFlags & PF_ForceWrapX)
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
}
if (Xor & PF_ForceWrapY)
{
if (PolyFlags & PF_ForceWrapY)
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
if (Xor & PF_Modulated)
{
#if defined (__unix__) || defined (UNIXCOMMON)
if (oglflags & GLF_NOTEXENV)
{
if (!(PolyFlags & PF_Modulated))
pglColor4ubv(white);
}
else
#endif
if (PolyFlags & PF_Modulated)
{ // mix texture colour with Surface->PolyColor
pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
else
{ // colour from texture is unchanged before blending
pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
}
if (Xor & PF_Occlude) // depth test but (no) depth write
{
if (PolyFlags&PF_Occlude)
{
pglDepthMask(1);
}
else
pglDepthMask(0);
}
////Hurdler: not used if we don't define POLYSKY
if (Xor & PF_Invisible)
{
if (PolyFlags&PF_Invisible)
pglBlendFunc(GL_ZERO, GL_ONE); // transparent blending
else
{ // big hack: (TODO: manage that better)
// we test only for PF_Masked because PF_Invisible is only used
// (for now) with it (yeah, that's crappy, sorry)
if ((PolyFlags&PF_Blending)==PF_Masked)
pglBlendFunc(GL_SRC_ALPHA, GL_ZERO);
}
}
if (PolyFlags & PF_NoTexture)
{
SetNoTexture();
}
}
CurrentPolyFlags = PolyFlags;
}
static void AllocTextureBuffer(GLMipmap_t *pTexInfo)
{
size_t size = pTexInfo->width * pTexInfo->height;
if (size > textureBufferSize)
{
textureBuffer = realloc(textureBuffer, size * sizeof(RGBA_t));
if (textureBuffer == NULL)
I_Error("AllocTextureBuffer: out of memory allocating %s bytes", sizeu1(size * sizeof(RGBA_t)));
textureBufferSize = size;
}
}
// -----------------+
// UpdateTexture : Updates texture data.
// -----------------+
EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo)
{
// Upload a texture
GLuint num = pTexInfo->downloaded;
boolean update = true;
INT32 w = pTexInfo->width, h = pTexInfo->height;
INT32 i, j;
const GLubyte *pImgData = (const GLubyte *)pTexInfo->data;
const GLvoid *ptex = NULL;
RGBA_t *tex = NULL;
// Generate a new texture name.
if (!num)
{
pglGenTextures(1, &num);
pTexInfo->downloaded = num;
update = false;
}
//GL_DBG_Printf("UpdateTexture %d %x\n", (INT32)num, pImgData);
if ((pTexInfo->format == GL_TEXFMT_P_8) || (pTexInfo->format == GL_TEXFMT_AP_88))
{
AllocTextureBuffer(pTexInfo);
ptex = tex = textureBuffer;
for (j = 0; j < h; j++)
{
for (i = 0; i < w; i++)
{
if ((*pImgData == HWR_PATCHES_CHROMAKEY_COLORINDEX) &&
(pTexInfo->flags & TF_CHROMAKEYED))
{
tex[w*j+i].s.red = 0;
tex[w*j+i].s.green = 0;
tex[w*j+i].s.blue = 0;
tex[w*j+i].s.alpha = 0;
pTexInfo->flags |= TF_TRANSPARENT; // there is a hole in it
}
else
{
tex[w*j+i].s.red = myPaletteData[*pImgData].s.red;
tex[w*j+i].s.green = myPaletteData[*pImgData].s.green;
tex[w*j+i].s.blue = myPaletteData[*pImgData].s.blue;
tex[w*j+i].s.alpha = myPaletteData[*pImgData].s.alpha;
}
pImgData++;
if (pTexInfo->format == GL_TEXFMT_AP_88)
{
if (!(pTexInfo->flags & TF_CHROMAKEYED))
tex[w*j+i].s.alpha = *pImgData;
pImgData++;
}
}
}
}
else if (pTexInfo->format == GL_TEXFMT_RGBA)
{
// Directly upload the texture data without any kind of conversion.
ptex = pImgData;
}
else if (pTexInfo->format == GL_TEXFMT_ALPHA_INTENSITY_88)
{
AllocTextureBuffer(pTexInfo);
ptex = tex = textureBuffer;
for (j = 0; j < h; j++)
{
for (i = 0; i < w; i++)
{
tex[w*j+i].s.red = *pImgData;
tex[w*j+i].s.green = *pImgData;
tex[w*j+i].s.blue = *pImgData;
pImgData++;
tex[w*j+i].s.alpha = *pImgData;
pImgData++;
}
}
}
else if (pTexInfo->format == GL_TEXFMT_ALPHA_8) // Used for fade masks
{
AllocTextureBuffer(pTexInfo);
ptex = tex = textureBuffer;
for (j = 0; j < h; j++)
{
for (i = 0; i < w; i++)
{
tex[w*j+i].s.red = 255; // 255 because the fade mask is modulated with the screen texture, so alpha affects it while the colours don't
tex[w*j+i].s.green = 255;
tex[w*j+i].s.blue = 255;
tex[w*j+i].s.alpha = *pImgData;
pImgData++;
}
}
}
else
GL_MSG_Warning("UpdateTexture: bad format %d\n", pTexInfo->format);
pglBindTexture(GL_TEXTURE_2D, num);
tex_downloaded = num;
// disable texture filtering on any texture that has holes so there's no dumb borders or blending issues
if (pTexInfo->flags & TF_TRANSPARENT)
{
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
else
{
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
}
if (pTexInfo->format == GL_TEXFMT_ALPHA_INTENSITY_88)
{
//pglTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
if (MipMap)
{
pgluBuild2DMipmaps(GL_TEXTURE_2D, GL_LUMINANCE_ALPHA, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0);
if (pTexInfo->flags & TF_TRANSPARENT)
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 0); // No mippmaps on transparent stuff
else
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 4);
//pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_LINEAR_MIPMAP_LINEAR);
}
else
{
if (update)
pglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
else
pglTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
}
}
else if (pTexInfo->format == GL_TEXFMT_ALPHA_8)
{
//pglTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
if (MipMap)
{
pgluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0);
if (pTexInfo->flags & TF_TRANSPARENT)
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 0); // No mippmaps on transparent stuff
else
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 4);
//pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_LINEAR_MIPMAP_LINEAR);
}
else
{
if (update)
pglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
else
pglTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
}
}
else
{
if (MipMap)
{
pgluBuild2DMipmaps(GL_TEXTURE_2D, textureformatGL, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
// Control the mipmap level of detail
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); // the lower the number, the higer the detail
if (pTexInfo->flags & TF_TRANSPARENT)
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 0); // No mippmaps on transparent stuff
else
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 5);
}
else
{
if (update)
pglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
else
pglTexImage2D(GL_TEXTURE_2D, 0, textureformatGL, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex);
}
}
if (pTexInfo->flags & TF_WRAPX)
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
else
Clamp2D(GL_TEXTURE_WRAP_S);
if (pTexInfo->flags & TF_WRAPY)
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
else
Clamp2D(GL_TEXTURE_WRAP_T);
if (maximumAnisotropy)
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropic_filter);
}
// -----------------+
// SetTexture : The mipmap becomes the current texture source
// -----------------+
EXPORT void HWRAPI(SetTexture) (GLMipmap_t *pTexInfo)
{
if (!pTexInfo)
{
SetNoTexture();
return;
}
else if (pTexInfo->downloaded)
{
if (pTexInfo->downloaded != tex_downloaded)
{
pglBindTexture(GL_TEXTURE_2D, pTexInfo->downloaded);
tex_downloaded = pTexInfo->downloaded;
}
}
else
{
FTextureInfo *newTex = calloc(1, sizeof (*newTex));
UpdateTexture(pTexInfo);
newTex->texture = pTexInfo;
newTex->downloaded = (UINT32)pTexInfo->downloaded;
newTex->width = (UINT32)pTexInfo->width;
newTex->height = (UINT32)pTexInfo->height;
newTex->format = (UINT32)pTexInfo->format;
// insertion at the tail
if (TexCacheTail)
{
newTex->prev = TexCacheTail;
TexCacheTail->next = newTex;
TexCacheTail = newTex;
}
else // initialization of the linked list
TexCacheTail = TexCacheHead = newTex;
}
}
static void Shader_SetUniforms(FSurfaceInfo *Surface, GLRGBAFloat *poly, GLRGBAFloat *tint, GLRGBAFloat *fade)
{
#ifdef GL_SHADERS
gl_shader_t *shader = gl_shaderstate.current;
if (gl_shadersenabled && (shader != NULL) && pglUseProgram)
{
if (!shader->program)
{
pglUseProgram(0);
return;
}
if (gl_shaderstate.changed)
{
pglUseProgram(shader->program);
gl_shaderstate.changed = false;
}
// Color uniforms can be left NULL and will be set to white (1.0f, 1.0f, 1.0f, 1.0f)
if (poly == NULL)
poly = &shader_defaultcolor;
if (tint == NULL)
tint = &shader_defaultcolor;
if (fade == NULL)
fade = &shader_defaultcolor;
#define UNIFORM_1(uniform, a, function) \
if (uniform != -1) \
function (uniform, a);
#define UNIFORM_2(uniform, a, b, function) \
if (uniform != -1) \
function (uniform, a, b);
#define UNIFORM_3(uniform, a, b, c, function) \
if (uniform != -1) \
function (uniform, a, b, c);
#define UNIFORM_4(uniform, a, b, c, d, function) \
if (uniform != -1) \
function (uniform, a, b, c, d);
// polygon
UNIFORM_4(shader->uniforms[gluniform_poly_color], poly->red, poly->green, poly->blue, poly->alpha, pglUniform4f);
UNIFORM_4(shader->uniforms[gluniform_tint_color], tint->red, tint->green, tint->blue, tint->alpha, pglUniform4f);
UNIFORM_4(shader->uniforms[gluniform_fade_color], fade->red, fade->green, fade->blue, fade->alpha, pglUniform4f);
if (Surface != NULL)
{
UNIFORM_1(shader->uniforms[gluniform_lighting], Surface->LightInfo.light_level, pglUniform1f);
UNIFORM_1(shader->uniforms[gluniform_fade_start], Surface->LightInfo.fade_start, pglUniform1f);
UNIFORM_1(shader->uniforms[gluniform_fade_end], Surface->LightInfo.fade_end, pglUniform1f);
}
UNIFORM_1(shader->uniforms[gluniform_leveltime], ((float)shader_leveltime) / TICRATE, pglUniform1f);
#undef UNIFORM_1
#undef UNIFORM_2
#undef UNIFORM_3
#undef UNIFORM_4
}
#else
(void)Surface;
(void)poly;
(void)tint;
(void)fade;
#endif
}
static boolean Shader_CompileProgram(gl_shader_t *shader, GLint i, const GLchar *vert_shader, const GLchar *frag_shader)
{
GLuint gl_vertShader, gl_fragShader;
GLint result;
//
// Load and compile vertex shader
//
gl_vertShader = pglCreateShader(GL_VERTEX_SHADER);
if (!gl_vertShader)
{
GL_MSG_Error("Shader_CompileProgram: Error creating vertex shader %s\n", HWR_GetShaderName(i));
return false;
}
pglShaderSource(gl_vertShader, 1, &vert_shader, NULL);
pglCompileShader(gl_vertShader);
// check for compile errors
pglGetShaderiv(gl_vertShader, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE)
{
Shader_CompileError("Error compiling vertex shader", gl_vertShader, i);
pglDeleteShader(gl_vertShader);
return false;
}
//
// Load and compile fragment shader
//
gl_fragShader = pglCreateShader(GL_FRAGMENT_SHADER);
if (!gl_fragShader)
{
GL_MSG_Error("Shader_CompileProgram: Error creating fragment shader %s\n", HWR_GetShaderName(i));
pglDeleteShader(gl_vertShader);
pglDeleteShader(gl_fragShader);
return false;
}
pglShaderSource(gl_fragShader, 1, &frag_shader, NULL);
pglCompileShader(gl_fragShader);
// check for compile errors
pglGetShaderiv(gl_fragShader, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE)
{
Shader_CompileError("Error compiling fragment shader", gl_fragShader, i);
pglDeleteShader(gl_vertShader);
pglDeleteShader(gl_fragShader);
return false;
}
shader->program = pglCreateProgram();
pglAttachShader(shader->program, gl_vertShader);
pglAttachShader(shader->program, gl_fragShader);
pglLinkProgram(shader->program);
// check link status
pglGetProgramiv(shader->program, GL_LINK_STATUS, &result);
// delete the shader objects
pglDeleteShader(gl_vertShader);
pglDeleteShader(gl_fragShader);
// couldn't link?
if (result != GL_TRUE)
{
GL_MSG_Error("Shader_CompileProgram: Error linking shader program %s\n", HWR_GetShaderName(i));
pglDeleteProgram(shader->program);
return false;
}
// 13062019
#define GETUNI(uniform) pglGetUniformLocation(shader->program, uniform);
// lighting
shader->uniforms[gluniform_poly_color] = GETUNI("poly_color");
shader->uniforms[gluniform_tint_color] = GETUNI("tint_color");
shader->uniforms[gluniform_fade_color] = GETUNI("fade_color");
shader->uniforms[gluniform_lighting] = GETUNI("lighting");
shader->uniforms[gluniform_fade_start] = GETUNI("fade_start");
shader->uniforms[gluniform_fade_end] = GETUNI("fade_end");
// misc. (custom shaders)
shader->uniforms[gluniform_leveltime] = GETUNI("leveltime");
#undef GETUNI
return true;
}
static void Shader_CompileError(const char *message, GLuint program, INT32 shadernum)
{
GLchar *infoLog = NULL;
GLint logLength;
pglGetShaderiv(program, GL_INFO_LOG_LENGTH, &logLength);
if (logLength)
{
infoLog = malloc(logLength);
pglGetShaderInfoLog(program, logLength, NULL, infoLog);
}
GL_MSG_Error("Shader_CompileProgram: %s (%s)\n%s", message, HWR_GetShaderName(shadernum), (infoLog ? infoLog : ""));
if (infoLog)
free(infoLog);
}
// code that is common between DrawPolygon and DrawIndexedTriangles
// the corona thing is there too, i have no idea if that stuff works with DrawIndexedTriangles and batching
static void PreparePolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, FBITFIELD PolyFlags)
{
static GLRGBAFloat poly = {0,0,0,0};
static GLRGBAFloat tint = {0,0,0,0};
static GLRGBAFloat fade = {0,0,0,0};
if ((PolyFlags & PF_Corona) && (oglflags & GLF_NOZBUFREAD))
PolyFlags &= ~(PF_NoDepthTest|PF_Corona);
SetBlend(PolyFlags); //TODO: inline (#pragma..)
if (pSurf)
{
// If modulated, mix the surface colour to the texture
if (CurrentPolyFlags & PF_Modulated)
pglColor4ubv((GLubyte*)&pSurf->PolyColor.s);
// If the surface is either modulated or colormapped, or both
if (CurrentPolyFlags & (PF_Modulated | PF_ColorMapped))
{
poly.red = byte2float[pSurf->PolyColor.s.red];
poly.green = byte2float[pSurf->PolyColor.s.green];
poly.blue = byte2float[pSurf->PolyColor.s.blue];
poly.alpha = byte2float[pSurf->PolyColor.s.alpha];
}
// Only if the surface is colormapped
if (CurrentPolyFlags & PF_ColorMapped)
{
tint.red = byte2float[pSurf->TintColor.s.red];
tint.green = byte2float[pSurf->TintColor.s.green];
tint.blue = byte2float[pSurf->TintColor.s.blue];
tint.alpha = byte2float[pSurf->TintColor.s.alpha];
fade.red = byte2float[pSurf->FadeColor.s.red];
fade.green = byte2float[pSurf->FadeColor.s.green];
fade.blue = byte2float[pSurf->FadeColor.s.blue];
fade.alpha = byte2float[pSurf->FadeColor.s.alpha];
}
}
// this test is added for new coronas' code (without depth buffer)
// I think I should do a separate function for drawing coronas, so it will be a little faster
if (PolyFlags & PF_Corona) // check to see if we need to draw the corona
{
FUINT i;
FUINT j;
//rem: all 8 (or 8.0f) values are hard coded: it can be changed to a higher value
GLfloat buf[8][8];
GLfloat cx, cy, cz;
GLfloat px = 0.0f, py = 0.0f, pz = -1.0f;
GLfloat scalef = 0.0f;
GLubyte c[4];
float alpha;
cx = (pOutVerts[0].x + pOutVerts[2].x) / 2.0f; // we should change the coronas' ...
cy = (pOutVerts[0].y + pOutVerts[2].y) / 2.0f; // ... code so its only done once.
cz = pOutVerts[0].z;
// I dont know if this is slow or not
GLProject(cx, cy, cz, &px, &py, &pz);
//GL_DBG_Printf("Projection: (%f, %f, %f)\n", px, py, pz);
if ((pz < 0.0l) ||
(px < -8.0l) ||
(py < viewport[1]-8.0l) ||
(px > viewport[2]+8.0l) ||
(py > viewport[1]+viewport[3]+8.0l))
return;
// the damned slow glReadPixels functions :(
pglReadPixels((INT32)px-4, (INT32)py, 8, 8, GL_DEPTH_COMPONENT, GL_FLOAT, buf);
//GL_DBG_Printf("DepthBuffer: %f %f\n", buf[0][0], buf[3][3]);
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
scalef += (pz > buf[i][j]+0.00005f) ? 0 : 1;
// quick test for screen border (not 100% correct, but looks ok)
if (px < 4) scalef -= (GLfloat)(8*(4-px));
if (py < viewport[1]+4) scalef -= (GLfloat)(8*(viewport[1]+4-py));
if (px > viewport[2]-4) scalef -= (GLfloat)(8*(4-(viewport[2]-px)));
if (py > viewport[1]+viewport[3]-4) scalef -= (GLfloat)(8*(4-(viewport[1]+viewport[3]-py)));
scalef /= 64;
//GL_DBG_Printf("Scale factor: %f\n", scalef);
if (scalef < 0.05f)
return;
// GLubyte c[4];
c[0] = pSurf->PolyColor.s.red;
c[1] = pSurf->PolyColor.s.green;
c[2] = pSurf->PolyColor.s.blue;
alpha = byte2float[pSurf->PolyColor.s.alpha];
alpha *= scalef; // change the alpha value (it seems better than changing the size of the corona)
c[3] = (unsigned char)(alpha * 255);
pglColor4ubv(c);
}
Shader_SetUniforms(pSurf, &poly, &tint, &fade);
}
// -----------------+
// DrawPolygon : Render a polygon, set the texture, set render mode
// -----------------+
EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, FBITFIELD PolyFlags)
{
PreparePolygon(pSurf, pOutVerts, PolyFlags);
pglVertexPointer(3, GL_FLOAT, sizeof(FOutVector), &pOutVerts[0].x);
pglTexCoordPointer(2, GL_FLOAT, sizeof(FOutVector), &pOutVerts[0].s);
pglDrawArrays(GL_TRIANGLE_FAN, 0, iNumPts);
if (PolyFlags & PF_RemoveYWrap)
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
if (PolyFlags & PF_ForceWrapX)
Clamp2D(GL_TEXTURE_WRAP_S);
if (PolyFlags & PF_ForceWrapY)
Clamp2D(GL_TEXTURE_WRAP_T);
}
EXPORT void HWRAPI(DrawIndexedTriangles) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, FBITFIELD PolyFlags, UINT32 *IndexArray)
{
PreparePolygon(pSurf, pOutVerts, PolyFlags);
pglVertexPointer(3, GL_FLOAT, sizeof(FOutVector), &pOutVerts[0].x);
pglTexCoordPointer(2, GL_FLOAT, sizeof(FOutVector), &pOutVerts[0].s);
pglDrawElements(GL_TRIANGLES, iNumPts, GL_UNSIGNED_INT, IndexArray);
// the DrawPolygon variant of this has some code about polyflags and wrapping here but havent noticed any problems from omitting it?
}
static const boolean gl_ext_arb_vertex_buffer_object = true;
#define NULL_VBO_VERTEX ((gl_skyvertex_t*)NULL)
#define sky_vbo_x (gl_ext_arb_vertex_buffer_object ? &NULL_VBO_VERTEX->x : &sky->data[0].x)
#define sky_vbo_u (gl_ext_arb_vertex_buffer_object ? &NULL_VBO_VERTEX->u : &sky->data[0].u)
#define sky_vbo_r (gl_ext_arb_vertex_buffer_object ? &NULL_VBO_VERTEX->r : &sky->data[0].r)
EXPORT void HWRAPI(RenderSkyDome) (gl_sky_t *sky)
{
int i, j;
Shader_SetUniforms(NULL, NULL, NULL, NULL);
// Build the sky dome! Yes!
if (sky->rebuild)
{
// delete VBO when already exists
if (gl_ext_arb_vertex_buffer_object)
{
if (sky->vbo)
pglDeleteBuffers(1, &sky->vbo);
}
if (gl_ext_arb_vertex_buffer_object)
{
// generate a new VBO and get the associated ID
pglGenBuffers(1, &sky->vbo);
// bind VBO in order to use
pglBindBuffer(GL_ARRAY_BUFFER, sky->vbo);
// upload data to VBO
pglBufferData(GL_ARRAY_BUFFER, sky->vertex_count * sizeof(sky->data[0]), sky->data, GL_STATIC_DRAW);
}
sky->rebuild = false;
}
// bind VBO in order to use
if (gl_ext_arb_vertex_buffer_object)
pglBindBuffer(GL_ARRAY_BUFFER, sky->vbo);
// activate and specify pointers to arrays
pglVertexPointer(3, GL_FLOAT, sizeof(sky->data[0]), sky_vbo_x);
pglTexCoordPointer(2, GL_FLOAT, sizeof(sky->data[0]), sky_vbo_u);
pglColorPointer(4, GL_UNSIGNED_BYTE, sizeof(sky->data[0]), sky_vbo_r);
// activate color arrays
pglEnableClientState(GL_COLOR_ARRAY);
// set transforms
pglScalef(1.0f, (float)sky->height / 200.0f, 1.0f);
pglRotatef(270.0f, 0.0f, 1.0f, 0.0f);
for (j = 0; j < 2; j++)
{
for (i = 0; i < sky->loopcount; i++)
{
gl_skyloopdef_t *loop = &sky->loops[i];
unsigned int mode = 0;
if (j == 0 ? loop->use_texture : !loop->use_texture)
continue;
switch (loop->mode)
{
case HWD_SKYLOOP_FAN:
mode = GL_TRIANGLE_FAN;
break;
case HWD_SKYLOOP_STRIP:
mode = GL_TRIANGLE_STRIP;
break;
default:
continue;
}
pglDrawArrays(mode, loop->vertexindex, loop->vertexcount);
}
}
pglScalef(1.0f, 1.0f, 1.0f);
pglColor4ubv(white);
// bind with 0, so, switch back to normal pointer operation
if (gl_ext_arb_vertex_buffer_object)
pglBindBuffer(GL_ARRAY_BUFFER, 0);
// deactivate color array
pglDisableClientState(GL_COLOR_ARRAY);
}
// ==========================================================================
//
// ==========================================================================
EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value)
{
switch (IdState)
{
case HWD_SET_MODEL_LIGHTING:
model_lighting = Value;
break;
case HWD_SET_SHADERS:
gl_allowshaders = (hwdshaderoption_t)Value;
break;
case HWD_SET_TEXTUREFILTERMODE:
switch (Value)
{
case HWD_SET_TEXTUREFILTER_TRILINEAR:
min_filter = GL_LINEAR_MIPMAP_LINEAR;
mag_filter = GL_LINEAR;
MipMap = GL_TRUE;
break;
case HWD_SET_TEXTUREFILTER_BILINEAR:
min_filter = mag_filter = GL_LINEAR;
MipMap = GL_FALSE;
break;
case HWD_SET_TEXTUREFILTER_POINTSAMPLED:
min_filter = mag_filter = GL_NEAREST;
MipMap = GL_FALSE;
break;
case HWD_SET_TEXTUREFILTER_MIXED1:
min_filter = GL_NEAREST;
mag_filter = GL_LINEAR;
MipMap = GL_FALSE;
break;
case HWD_SET_TEXTUREFILTER_MIXED2:
min_filter = GL_LINEAR;
mag_filter = GL_NEAREST;
MipMap = GL_FALSE;
break;
case HWD_SET_TEXTUREFILTER_MIXED3:
min_filter = GL_LINEAR_MIPMAP_LINEAR;
mag_filter = GL_NEAREST;
MipMap = GL_TRUE;
break;
default:
mag_filter = GL_LINEAR;
min_filter = GL_NEAREST;
}
if (!pgluBuild2DMipmaps)
{
MipMap = GL_FALSE;
min_filter = GL_LINEAR;
}
Flush(); //??? if we want to change filter mode by texture, remove this
break;
case HWD_SET_TEXTUREANISOTROPICMODE:
anisotropic_filter = min(Value,maximumAnisotropy);
if (maximumAnisotropy)
Flush(); //??? if we want to change filter mode by texture, remove this
break;
default:
break;
}
}
static float *vertBuffer = NULL;
static float *normBuffer = NULL;
static size_t lerpBufferSize = 0;
static short *vertTinyBuffer = NULL;
static char *normTinyBuffer = NULL;
static size_t lerpTinyBufferSize = 0;
// Static temporary buffer for doing frame interpolation
// 'size' is the vertex size
static void AllocLerpBuffer(size_t size)
{
if (lerpBufferSize >= size)
return;
if (vertBuffer != NULL)
free(vertBuffer);
if (normBuffer != NULL)
free(normBuffer);
lerpBufferSize = size;
vertBuffer = malloc(lerpBufferSize);
normBuffer = malloc(lerpBufferSize);
}
// Static temporary buffer for doing frame interpolation
// 'size' is the vertex size
static void AllocLerpTinyBuffer(size_t size)
{
if (lerpTinyBufferSize >= size)
return;
if (vertTinyBuffer != NULL)
free(vertTinyBuffer);
if (normTinyBuffer != NULL)
free(normTinyBuffer);
lerpTinyBufferSize = size;
vertTinyBuffer = malloc(lerpTinyBufferSize);
normTinyBuffer = malloc(lerpTinyBufferSize / 2);
}
#ifndef GL_STATIC_DRAW
#define GL_STATIC_DRAW 0x88E4
#endif
#ifndef GL_ARRAY_BUFFER
#define GL_ARRAY_BUFFER 0x8892
#endif
static void CreateModelVBO(mesh_t *mesh, mdlframe_t *frame)
{
int bufferSize = sizeof(vbo64_t)*mesh->numTriangles * 3;
vbo64_t *buffer = (vbo64_t*)malloc(bufferSize);
vbo64_t *bufPtr = buffer;
float *vertPtr = frame->vertices;
float *normPtr = frame->normals;
float *tanPtr = frame->tangents;
float *uvPtr = mesh->uvs;
float *lightPtr = mesh->lightuvs;
char *colorPtr = frame->colors;
int i;
for (i = 0; i < mesh->numTriangles * 3; i++)
{
bufPtr->x = *vertPtr++;
bufPtr->y = *vertPtr++;
bufPtr->z = *vertPtr++;
bufPtr->nx = *normPtr++;
bufPtr->ny = *normPtr++;
bufPtr->nz = *normPtr++;
bufPtr->s0 = *uvPtr++;
bufPtr->t0 = *uvPtr++;
if (tanPtr != NULL)
{
bufPtr->tan0 = *tanPtr++;
bufPtr->tan1 = *tanPtr++;
bufPtr->tan2 = *tanPtr++;
}
if (lightPtr != NULL)
{
bufPtr->s1 = *lightPtr++;
bufPtr->t1 = *lightPtr++;
}
if (colorPtr)
{
bufPtr->r = *colorPtr++;
bufPtr->g = *colorPtr++;
bufPtr->b = *colorPtr++;
bufPtr->a = *colorPtr++;
}
else
{
bufPtr->r = 255;
bufPtr->g = 255;
bufPtr->b = 255;
bufPtr->a = 255;
}
bufPtr++;
}
pglGenBuffers(1, &frame->vboID);
pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID);
pglBufferData(GL_ARRAY_BUFFER, bufferSize, buffer, GL_STATIC_DRAW);
free(buffer);
// Don't leave the array buffer bound to the model,
// since this is called mid-frame
pglBindBuffer(GL_ARRAY_BUFFER, 0);
}
static void CreateModelVBOTiny(mesh_t *mesh, tinyframe_t *frame)
{
int bufferSize = sizeof(vbotiny_t)*mesh->numTriangles * 3;
vbotiny_t *buffer = (vbotiny_t*)malloc(bufferSize);
vbotiny_t *bufPtr = buffer;
short *vertPtr = frame->vertices;
char *normPtr = frame->normals;
float *uvPtr = mesh->uvs;
char *tanPtr = frame->tangents;
int i;
for (i = 0; i < mesh->numVertices; i++)
{
bufPtr->x = *vertPtr++;
bufPtr->y = *vertPtr++;
bufPtr->z = *vertPtr++;
bufPtr->nx = *normPtr++;
bufPtr->ny = *normPtr++;
bufPtr->nz = *normPtr++;
bufPtr->s0 = *uvPtr++;
bufPtr->t0 = *uvPtr++;
if (tanPtr)
{
bufPtr->tanx = *tanPtr++;
bufPtr->tany = *tanPtr++;
bufPtr->tanz = *tanPtr++;
}
bufPtr++;
}
pglGenBuffers(1, &frame->vboID);
pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID);
pglBufferData(GL_ARRAY_BUFFER, bufferSize, buffer, GL_STATIC_DRAW);
free(buffer);
// Don't leave the array buffer bound to the model,
// since this is called mid-frame
pglBindBuffer(GL_ARRAY_BUFFER, 0);
}
EXPORT void HWRAPI(CreateModelVBOs) (model_t *model)
{
int i;
for (i = 0; i < model->numMeshes; i++)
{
mesh_t *mesh = &model->meshes[i];
if (mesh->frames)
{
int j;
for (j = 0; j < model->meshes[i].numFrames; j++)
{
mdlframe_t *frame = &mesh->frames[j];
if (frame->vboID)
pglDeleteBuffers(1, &frame->vboID);
frame->vboID = 0;
CreateModelVBO(mesh, frame);
}
}
else if (mesh->tinyframes)
{
int j;
for (j = 0; j < model->meshes[i].numFrames; j++)
{
tinyframe_t *frame = &mesh->tinyframes[j];
if (frame->vboID)
pglDeleteBuffers(1, &frame->vboID);
frame->vboID = 0;
CreateModelVBOTiny(mesh, frame);
}
}
}
}
#define BUFFER_OFFSET(i) ((void*)(i))
static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface)
{
static GLRGBAFloat poly = {0,0,0,0};
static GLRGBAFloat tint = {0,0,0,0};
static GLRGBAFloat fade = {0,0,0,0};
float pol = 0.0f;
float scalex, scaley, scalez;
boolean useTinyFrames;
boolean useVBO = true;
FBITFIELD flags;
int i;
// Because otherwise, scaling the screen negatively vertically breaks the lighting
GLfloat LightPos[] = {0.0f, 1.0f, 0.0f, 0.0f};
#ifdef GL_LIGHT_MODEL_AMBIENT
GLfloat ambient[4];
GLfloat diffuse[4];
#endif
// Affect input model scaling
scale *= 0.5f;
scalex = scale;
scaley = scale;
scalez = scale;
if (duration != 0 && duration != -1 && tics != -1) // don't interpolate if instantaneous or infinite in length
{
UINT32 newtime = (duration - tics); // + 1;
pol = (newtime)/(float)duration;
if (pol > 1.0f)
pol = 1.0f;
if (pol < 0.0f)
pol = 0.0f;
}
poly.red = byte2float[Surface->PolyColor.s.red];
poly.green = byte2float[Surface->PolyColor.s.green];
poly.blue = byte2float[Surface->PolyColor.s.blue];
poly.alpha = byte2float[Surface->PolyColor.s.alpha];
#ifdef GL_LIGHT_MODEL_AMBIENT
if (model_lighting)
{
if (!gl_shadersenabled)
{
ambient[0] = poly.red;
ambient[1] = poly.green;
ambient[2] = poly.blue;
ambient[3] = poly.alpha;
diffuse[0] = poly.red;
diffuse[1] = poly.green;
diffuse[2] = poly.blue;
diffuse[3] = poly.alpha;
if (ambient[0] > 0.75f)
ambient[0] = 0.75f;
if (ambient[1] > 0.75f)
ambient[1] = 0.75f;
if (ambient[2] > 0.75f)
ambient[2] = 0.75f;
pglLightfv(GL_LIGHT0, GL_POSITION, LightPos);
pglEnable(GL_LIGHTING);
pglMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient);
pglMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
}
pglShadeModel(GL_SMOOTH);
}
#endif
else
pglColor4ubv((GLubyte*)&Surface->PolyColor.s);
tint.red = byte2float[Surface->TintColor.s.red];
tint.green = byte2float[Surface->TintColor.s.green];
tint.blue = byte2float[Surface->TintColor.s.blue];
tint.alpha = byte2float[Surface->TintColor.s.alpha];
fade.red = byte2float[Surface->FadeColor.s.red];
fade.green = byte2float[Surface->FadeColor.s.green];
fade.blue = byte2float[Surface->FadeColor.s.blue];
fade.alpha = byte2float[Surface->FadeColor.s.alpha];
flags = (Surface->PolyFlags | PF_Modulated);
if (Surface->PolyFlags & (PF_Additive|PF_Subtractive|PF_ReverseSubtract|PF_Multiplicative))
flags |= PF_Occlude;
else if (Surface->PolyColor.s.alpha == 0xFF)
flags |= (PF_Occlude | PF_Masked);
SetBlend(flags);
Shader_SetUniforms(Surface, &poly, &tint, &fade);
pglEnable(GL_CULL_FACE);
pglEnable(GL_NORMALIZE);
#ifdef USE_FTRANSFORM_MIRROR
// flipped is if the object is vertically flipped
// hflipped is if the object is horizontally flipped
// pos->flip is if the screen is flipped vertically
// pos->mirror is if the screen is flipped horizontally
// XOR all the flips together to figure out what culling to use!
{
boolean reversecull = (flipped ^ hflipped ^ pos->flip ^ pos->mirror);
if (reversecull)
pglCullFace(GL_FRONT);
else
pglCullFace(GL_BACK);
}
#else
// pos->flip is if the screen is flipped too
if (flipped ^ hflipped ^ pos->flip) // If one or three of these are active, but not two, invert the model's culling
{
pglCullFace(GL_FRONT);
}
else
{
pglCullFace(GL_BACK);
}
#endif
pglPushMatrix(); // should be the same as glLoadIdentity
//Hurdler: now it seems to work
pglTranslatef(pos->x, pos->z, pos->y);
if (flipped)
scaley = -scaley;
if (hflipped)
scalez = -scalez;
#ifdef USE_FTRANSFORM_ANGLEZ
pglRotatef(pos->anglez, 0.0f, 0.0f, -1.0f); // rotate by slope from Kart
#endif
pglRotatef(pos->angley, 0.0f, -1.0f, 0.0f);
pglRotatef(pos->anglex, 1.0f, 0.0f, 0.0f);
if (pos->roll)
{
float roll = (1.0f * pos->rollflip);
pglTranslatef(pos->centerx, pos->centery, 0);
if (pos->rotaxis == 2) // Z
pglRotatef(pos->rollangle, 0.0f, 0.0f, roll);
else if (pos->rotaxis == 1) // Y
pglRotatef(pos->rollangle, 0.0f, roll, 0.0f);
else // X
pglRotatef(pos->rollangle, roll, 0.0f, 0.0f);
pglTranslatef(-pos->centerx, -pos->centery, 0);
}
pglScalef(scalex, scaley, scalez);
useTinyFrames = model->meshes[0].tinyframes != NULL;
if (useTinyFrames)
pglScalef(1 / 64.0f, 1 / 64.0f, 1 / 64.0f);
// Don't use the VBO if it does not have the correct texture coordinates.
// (Can happen when model uses a sprite as a texture and the sprite changes)
// Comparing floats with the != operator here should be okay because they
// are just copies of glpatches' max_s and max_t values.
// Instead of the != operator, memcmp is used to avoid a compiler warning.
if (memcmp(&(model->vbo_max_s), &(model->max_s), sizeof(model->max_s)) != 0 ||
memcmp(&(model->vbo_max_t), &(model->max_t), sizeof(model->max_t)) != 0)
useVBO = false;
pglEnableClientState(GL_NORMAL_ARRAY);
for (i = 0; i < model->numMeshes; i++)
{
mesh_t *mesh = &model->meshes[i];
if (useTinyFrames)
{
tinyframe_t *frame = &mesh->tinyframes[frameIndex % mesh->numFrames];
tinyframe_t *nextframe = NULL;
if (nextFrameIndex != -1)
nextframe = &mesh->tinyframes[nextFrameIndex % mesh->numFrames];
if (!nextframe || fpclassify(pol) == FP_ZERO)
{
if (useVBO)
{
pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID);
pglVertexPointer(3, GL_SHORT, sizeof(vbotiny_t), BUFFER_OFFSET(0));
pglNormalPointer(GL_BYTE, sizeof(vbotiny_t), BUFFER_OFFSET(sizeof(short)*3));
pglTexCoordPointer(2, GL_FLOAT, sizeof(vbotiny_t), BUFFER_OFFSET(sizeof(short) * 3 + sizeof(char) * 6));
pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices);
pglBindBuffer(GL_ARRAY_BUFFER, 0);
}
else
{
pglVertexPointer(3, GL_SHORT, 0, frame->vertices);
pglNormalPointer(GL_BYTE, 0, frame->normals);
pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs);
pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices);
}
}
else
{
short *vertPtr;
char *normPtr;
int j = 0;
// Dangit, I soooo want to do this in a GLSL shader...
AllocLerpTinyBuffer(mesh->numVertices * sizeof(short) * 3);
vertPtr = vertTinyBuffer;
normPtr = normTinyBuffer;
for (j = 0; j < mesh->numVertices * 3; j++)
{
// Interpolate
*vertPtr++ = (short)(frame->vertices[j] + (pol * (nextframe->vertices[j] - frame->vertices[j])));
*normPtr++ = (char)(frame->normals[j] + (pol * (nextframe->normals[j] - frame->normals[j])));
}
pglVertexPointer(3, GL_SHORT, 0, vertTinyBuffer);
pglNormalPointer(GL_BYTE, 0, normTinyBuffer);
pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs);
pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices);
}
}
else
{
mdlframe_t *frame = &mesh->frames[frameIndex % mesh->numFrames];
mdlframe_t *nextframe = NULL;
if (nextFrameIndex != -1)
nextframe = &mesh->frames[nextFrameIndex % mesh->numFrames];
if (!nextframe || fpclassify(pol) == FP_ZERO)
{
if (useVBO)
{
pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID);
pglVertexPointer(3, GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(0));
pglNormalPointer(GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(sizeof(float) * 3));
pglTexCoordPointer(2, GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(sizeof(float) * 6));
pglDrawArrays(GL_TRIANGLES, 0, mesh->numTriangles * 3);
// No tinyframes, no mesh indices
//pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices);
pglBindBuffer(GL_ARRAY_BUFFER, 0);
}
else
{
pglVertexPointer(3, GL_FLOAT, 0, frame->vertices);
pglNormalPointer(GL_FLOAT, 0, frame->normals);
pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs);
pglDrawArrays(GL_TRIANGLES, 0, mesh->numTriangles * 3);
}
}
else
{
float *vertPtr;
float *normPtr;
int j = 0;
// Dangit, I soooo want to do this in a GLSL shader...
AllocLerpBuffer(mesh->numVertices * sizeof(float) * 3);
vertPtr = vertBuffer;
normPtr = normBuffer;
//int j = 0;
for (j = 0; j < mesh->numVertices * 3; j++)
{
// Interpolate
*vertPtr++ = frame->vertices[j] + (pol * (nextframe->vertices[j] - frame->vertices[j]));
*normPtr++ = frame->normals[j] + (pol * (nextframe->normals[j] - frame->normals[j]));
}
pglVertexPointer(3, GL_FLOAT, 0, vertBuffer);
pglNormalPointer(GL_FLOAT, 0, normBuffer);
pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs);
pglDrawArrays(GL_TRIANGLES, 0, mesh->numVertices);
}
}
}
pglDisableClientState(GL_NORMAL_ARRAY);
pglPopMatrix(); // should be the same as glLoadIdentity
pglDisable(GL_CULL_FACE);
pglDisable(GL_NORMALIZE);
#ifdef GL_LIGHT_MODEL_AMBIENT
if (model_lighting)
{
if (!gl_shadersenabled)
pglDisable(GL_LIGHTING);
pglShadeModel(GL_FLAT);
}
#endif
}
// -----------------+
// HWRAPI DrawModel : Draw a model
// -----------------+
EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface)
{
DrawModelEx(model, frameIndex, duration, tics, nextFrameIndex, pos, scale, flipped, hflipped, Surface);
}
// -----------------+
// SetTransform :
// -----------------+
EXPORT void HWRAPI(SetTransform) (FTransform *stransform)
{
static boolean special_splitscreen;
boolean shearing = false;
float used_fov;
pglLoadIdentity();
if (stransform)
{
used_fov = stransform->fovxangle;
#ifdef USE_FTRANSFORM_MIRROR
// mirroring from Kart
if (stransform->mirror)
pglScalef(-stransform->scalex, stransform->scaley, -stransform->scalez);
else
#endif
if (stransform->flip)
pglScalef(stransform->scalex, -stransform->scaley, -stransform->scalez);
else
pglScalef(stransform->scalex, stransform->scaley, -stransform->scalez);
if (stransform->roll)
pglRotatef(stransform->rollangle, 0.0f, 0.0f, 1.0f);
pglRotatef(stransform->anglex , 1.0f, 0.0f, 0.0f);
pglRotatef(stransform->angley+270.0f, 0.0f, 1.0f, 0.0f);
pglTranslatef(-stransform->x, -stransform->z, -stransform->y);
special_splitscreen = stransform->splitscreen;
shearing = stransform->shearing;
}
else
{
used_fov = fov;
pglScalef(1.0f, 1.0f, -1.0f);
}
pglMatrixMode(GL_PROJECTION);
pglLoadIdentity();
// Simulate Software's y-shearing
// https://zdoom.org/wiki/Y-shearing
if (shearing)
{
float fdy = stransform->viewaiming * 2;
if (stransform->flip)
fdy *= -1.0f;
pglTranslatef(0.0f, -fdy/BASEVIDHEIGHT, 0.0f);
}
if (special_splitscreen)
{
used_fov = atan(tan(used_fov*M_PI/360)*0.8)*360/M_PI;
GLPerspective(used_fov, 2*ASPECT_RATIO);
}
else
GLPerspective(used_fov, ASPECT_RATIO);
pglGetFloatv(GL_PROJECTION_MATRIX, projMatrix); // added for new coronas' code (without depth buffer)
pglMatrixMode(GL_MODELVIEW);
pglGetFloatv(GL_MODELVIEW_MATRIX, modelMatrix); // added for new coronas' code (without depth buffer)
}
EXPORT INT32 HWRAPI(GetTextureUsed) (void)
{
FTextureInfo *tmp = TexCacheHead;
INT32 res = 0;
while (tmp)
{
// Figure out the correct bytes-per-pixel for this texture
// I don't know which one the game actually _uses_ but this
// follows format2bpp in hw_cache.c
int bpp = 1;
int format = tmp->format;
if (format == GL_TEXFMT_RGBA)
bpp = 4;
else if (format == GL_TEXFMT_ALPHA_INTENSITY_88 || format == GL_TEXFMT_AP_88)
bpp = 2;
// Add it up!
res += tmp->height*tmp->width*bpp;
tmp = tmp->next;
}
return res;
}
EXPORT void HWRAPI(PostImgRedraw) (float points[SCREENVERTS][SCREENVERTS][2])
{
INT32 x, y;
float float_x, float_y, float_nextx, float_nexty;
float xfix, yfix;
INT32 texsize = 2048;
const float blackBack[16] =
{
-16.0f, -16.0f, 6.0f,
-16.0f, 16.0f, 6.0f,
16.0f, 16.0f, 6.0f,
16.0f, -16.0f, 6.0f
};
// Use a power of two texture, dammit
if(screen_width <= 1024)
texsize = 1024;
if(screen_width <= 512)
texsize = 512;
// X/Y stretch fix for all resolutions(!)
xfix = (float)(texsize)/((float)((screen_width)/(float)(SCREENVERTS-1)));
yfix = (float)(texsize)/((float)((screen_height)/(float)(SCREENVERTS-1)));
pglDisable(GL_DEPTH_TEST);
pglDisable(GL_BLEND);
// const float blackBack[16]
// Draw a black square behind the screen texture,
// so nothing shows through the edges
pglColor4ubv(white);
pglVertexPointer(3, GL_FLOAT, 0, blackBack);
pglDrawArrays(GL_TRIANGLE_FAN, 0, 4);
pglEnableClientState(GL_TEXTURE_COORD_ARRAY);
for(x=0;x<SCREENVERTS-1;x++)
{
for(y=0;y<SCREENVERTS-1;y++)
{
float stCoords[8];
float vertCoords[12];
// Used for texture coordinates
// Annoying magic numbers to scale the square texture to
// a non-square screen..
float_x = (float)(x/(xfix));
float_y = (float)(y/(yfix));
float_nextx = (float)(x+1)/(xfix);
float_nexty = (float)(y+1)/(yfix);
// float stCoords[8];
stCoords[0] = float_x;
stCoords[1] = float_y;
stCoords[2] = float_x;
stCoords[3] = float_nexty;
stCoords[4] = float_nextx;
stCoords[5] = float_nexty;
stCoords[6] = float_nextx;
stCoords[7] = float_y;
pglTexCoordPointer(2, GL_FLOAT, 0, stCoords);
// float vertCoords[12];
vertCoords[0] = points[x][y][0];
vertCoords[1] = points[x][y][1];
vertCoords[2] = 4.4f;
vertCoords[3] = points[x][y + 1][0];
vertCoords[4] = points[x][y + 1][1];
vertCoords[5] = 4.4f;
vertCoords[6] = points[x + 1][y + 1][0];
vertCoords[7] = points[x + 1][y + 1][1];
vertCoords[8] = 4.4f;
vertCoords[9] = points[x + 1][y][0];
vertCoords[10] = points[x + 1][y][1];
vertCoords[11] = 4.4f;
pglVertexPointer(3, GL_FLOAT, 0, vertCoords);
pglDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
}
pglEnable(GL_DEPTH_TEST);
pglEnable(GL_BLEND);
}
// Sryder: This needs to be called whenever the screen changes resolution in order to reset the screen textures to use
// a new size
EXPORT void HWRAPI(FlushScreenTextures) (void)
{
pglDeleteTextures(1, &screentexture);
pglDeleteTextures(1, &startScreenWipe);
pglDeleteTextures(1, &endScreenWipe);
pglDeleteTextures(1, &finalScreenTexture);
screentexture = 0;
startScreenWipe = 0;
endScreenWipe = 0;
finalScreenTexture = 0;
}
// Create Screen to fade from
EXPORT void HWRAPI(StartScreenWipe) (void)
{
INT32 texsize = 2048;
boolean firstTime = (startScreenWipe == 0);
// Use a power of two texture, dammit
if(screen_width <= 512)
texsize = 512;
else if(screen_width <= 1024)
texsize = 1024;
// Create screen texture
if (firstTime)
pglGenTextures(1, &startScreenWipe);
pglBindTexture(GL_TEXTURE_2D, startScreenWipe);
if (firstTime)
{
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
Clamp2D(GL_TEXTURE_WRAP_S);
Clamp2D(GL_TEXTURE_WRAP_T);
pglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, texsize, texsize, 0);
}
else
pglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, texsize, texsize);
tex_downloaded = startScreenWipe;
}
// Create Screen to fade to
EXPORT void HWRAPI(EndScreenWipe)(void)
{
INT32 texsize = 2048;
boolean firstTime = (endScreenWipe == 0);
// Use a power of two texture, dammit
if(screen_width <= 512)
texsize = 512;
else if(screen_width <= 1024)
texsize = 1024;
// Create screen texture
if (firstTime)
pglGenTextures(1, &endScreenWipe);
pglBindTexture(GL_TEXTURE_2D, endScreenWipe);
if (firstTime)
{
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
Clamp2D(GL_TEXTURE_WRAP_S);
Clamp2D(GL_TEXTURE_WRAP_T);
pglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, texsize, texsize, 0);
}
else
pglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, texsize, texsize);
tex_downloaded = endScreenWipe;
}
// Draw the last scene under the intermission
EXPORT void HWRAPI(DrawIntermissionBG)(void)
{
float xfix, yfix;
INT32 texsize = 2048;
const float screenVerts[12] =
{
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f
};
float fix[8];
if(screen_width <= 1024)
texsize = 1024;
if(screen_width <= 512)
texsize = 512;
xfix = 1/((float)(texsize)/((float)((screen_width))));
yfix = 1/((float)(texsize)/((float)((screen_height))));
// const float screenVerts[12]
// float fix[8];
fix[0] = 0.0f;
fix[1] = 0.0f;
fix[2] = 0.0f;
fix[3] = yfix;
fix[4] = xfix;
fix[5] = yfix;
fix[6] = xfix;
fix[7] = 0.0f;
pglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
pglBindTexture(GL_TEXTURE_2D, screentexture);
pglColor4ubv(white);
pglTexCoordPointer(2, GL_FLOAT, 0, fix);
pglVertexPointer(3, GL_FLOAT, 0, screenVerts);
pglDrawArrays(GL_TRIANGLE_FAN, 0, 4);
tex_downloaded = screentexture;
}
// Do screen fades!
EXPORT void HWRAPI(DoScreenWipe)(void)
{
INT32 texsize = 2048;
float xfix, yfix;
INT32 fademaskdownloaded = tex_downloaded; // the fade mask that has been set
const float screenVerts[12] =
{
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f
};
float fix[8];
const float defaultST[8] =
{
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f
};
// Use a power of two texture, dammit
if(screen_width <= 1024)
texsize = 1024;
if(screen_width <= 512)
texsize = 512;
xfix = 1/((float)(texsize)/((float)((screen_width))));
yfix = 1/((float)(texsize)/((float)((screen_height))));
// const float screenVerts[12]
// float fix[8];
fix[0] = 0.0f;
fix[1] = 0.0f;
fix[2] = 0.0f;
fix[3] = yfix;
fix[4] = xfix;
fix[5] = yfix;
fix[6] = xfix;
fix[7] = 0.0f;
pglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
SetBlend(PF_Modulated|PF_NoDepthTest);
pglEnable(GL_TEXTURE_2D);
// Draw the original screen
pglBindTexture(GL_TEXTURE_2D, startScreenWipe);
pglColor4ubv(white);
pglTexCoordPointer(2, GL_FLOAT, 0, fix);
pglVertexPointer(3, GL_FLOAT, 0, screenVerts);
pglDrawArrays(GL_TRIANGLE_FAN, 0, 4);
SetBlend(PF_Modulated|PF_Translucent|PF_NoDepthTest);
// Draw the end screen that fades in
pglActiveTexture(GL_TEXTURE0);
pglEnable(GL_TEXTURE_2D);
pglBindTexture(GL_TEXTURE_2D, endScreenWipe);
pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
pglActiveTexture(GL_TEXTURE1);
pglEnable(GL_TEXTURE_2D);
pglBindTexture(GL_TEXTURE_2D, fademaskdownloaded);
pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
// const float defaultST[8]
pglClientActiveTexture(GL_TEXTURE0);
pglTexCoordPointer(2, GL_FLOAT, 0, fix);
pglVertexPointer(3, GL_FLOAT, 0, screenVerts);
pglClientActiveTexture(GL_TEXTURE1);
pglEnableClientState(GL_TEXTURE_COORD_ARRAY);
pglTexCoordPointer(2, GL_FLOAT, 0, defaultST);
pglDrawArrays(GL_TRIANGLE_FAN, 0, 4);
pglDisable(GL_TEXTURE_2D); // disable the texture in the 2nd texture unit
pglDisableClientState(GL_TEXTURE_COORD_ARRAY);
pglActiveTexture(GL_TEXTURE0);
pglClientActiveTexture(GL_TEXTURE0);
tex_downloaded = endScreenWipe;
}
// Create a texture from the screen.
EXPORT void HWRAPI(MakeScreenTexture) (void)
{
INT32 texsize = 2048;
boolean firstTime = (screentexture == 0);
// Use a power of two texture, dammit
if(screen_width <= 512)
texsize = 512;
else if(screen_width <= 1024)
texsize = 1024;
// Create screen texture
if (firstTime)
pglGenTextures(1, &screentexture);
pglBindTexture(GL_TEXTURE_2D, screentexture);
if (firstTime)
{
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
Clamp2D(GL_TEXTURE_WRAP_S);
Clamp2D(GL_TEXTURE_WRAP_T);
pglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, texsize, texsize, 0);
}
else
pglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, texsize, texsize);
tex_downloaded = screentexture;
}
EXPORT void HWRAPI(MakeScreenFinalTexture) (void)
{
INT32 texsize = 2048;
boolean firstTime = (finalScreenTexture == 0);
// Use a power of two texture, dammit
if(screen_width <= 512)
texsize = 512;
else if(screen_width <= 1024)
texsize = 1024;
// Create screen texture
if (firstTime)
pglGenTextures(1, &finalScreenTexture);
pglBindTexture(GL_TEXTURE_2D, finalScreenTexture);
if (firstTime)
{
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
Clamp2D(GL_TEXTURE_WRAP_S);
Clamp2D(GL_TEXTURE_WRAP_T);
pglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, texsize, texsize, 0);
}
else
pglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, texsize, texsize);
tex_downloaded = finalScreenTexture;
}
EXPORT void HWRAPI(DrawScreenFinalTexture)(int width, int height)
{
float xfix, yfix;
float origaspect, newaspect;
float xoff = 1, yoff = 1; // xoffset and yoffset for the polygon to have black bars around the screen
FRGBAFloat clearColour;
INT32 texsize = 2048;
float off[12];
float fix[8];
if(screen_width <= 1024)
texsize = 1024;
if(screen_width <= 512)
texsize = 512;
xfix = 1/((float)(texsize)/((float)((screen_width))));
yfix = 1/((float)(texsize)/((float)((screen_height))));
origaspect = (float)screen_width / screen_height;
newaspect = (float)width / height;
if (origaspect < newaspect)
{
xoff = origaspect / newaspect;
yoff = 1;
}
else if (origaspect > newaspect)
{
xoff = 1;
yoff = newaspect / origaspect;
}
// float off[12];
off[0] = -xoff;
off[1] = -yoff;
off[2] = 1.0f;
off[3] = -xoff;
off[4] = yoff;
off[5] = 1.0f;
off[6] = xoff;
off[7] = yoff;
off[8] = 1.0f;
off[9] = xoff;
off[10] = -yoff;
off[11] = 1.0f;
// float fix[8];
fix[0] = 0.0f;
fix[1] = 0.0f;
fix[2] = 0.0f;
fix[3] = yfix;
fix[4] = xfix;
fix[5] = yfix;
fix[6] = xfix;
fix[7] = 0.0f;
pglViewport(0, 0, width, height);
clearColour.red = clearColour.green = clearColour.blue = 0;
clearColour.alpha = 1;
ClearBuffer(true, false, &clearColour);
pglBindTexture(GL_TEXTURE_2D, finalScreenTexture);
pglColor4ubv(white);
pglTexCoordPointer(2, GL_FLOAT, 0, fix);
pglVertexPointer(3, GL_FLOAT, 0, off);
pglDrawArrays(GL_TRIANGLE_FAN, 0, 4);
tex_downloaded = finalScreenTexture;
}
#endif //HWRENDER