mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-26 15:45:41 +00:00
1588 lines
47 KiB
C
1588 lines
47 KiB
C
|
||
//#define WIREFRAME
|
||
// TODO: measure stats in VROM models (size of changing models, size of chunks, etc.)
|
||
// TODO: eventually use VBOs for model cache (update glext.h to OpenGL 1.5)
|
||
// TODO: cache dlists for per-model state changes
|
||
// TODO: align model cache structures to 4 bytes (use stride param) and profile
|
||
// TODO: move all config (i.e. USE_MODEL_CACHE, etc.) to makefile
|
||
// TODO: add specular lighting
|
||
// TODO: add light emission
|
||
// TODO: glFlush() ?
|
||
|
||
/*
|
||
* Sega Model 3 Emulator
|
||
* Copyright (C) 2003 Bart Trzynadlowski, Ville Linde, Stefano Teso
|
||
*
|
||
* This program is free software; you can redistribute it and/or modify it
|
||
* under the terms of the GNU General Public License Version 2 as published
|
||
* by the Free Software Foundation.
|
||
*
|
||
* This program is distributed in the hope that it will be useful, but
|
||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||
* for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License along
|
||
* with this program (license.txt); if not, write to the Free Software
|
||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
*/
|
||
|
||
/*
|
||
* osd_common/glrender.c
|
||
*
|
||
* OpenGL renderer backend. All OS-independent GL code is located here.
|
||
* osd_renderer_blit() is the only function not defined here.
|
||
*/
|
||
|
||
#include "model3.h"
|
||
#include <GL/gl.h>
|
||
#include <GL/glu.h>
|
||
#include <GL/glext.h> // this one can be obtained freely from SGI
|
||
|
||
|
||
/******************************************************************/
|
||
/* Configuration */
|
||
/******************************************************************/
|
||
|
||
// debug
|
||
//#define ANALYZE_VROM_MODELS
|
||
|
||
//#define USE_MODEL_CACHE // controls VROM model cache
|
||
#define CACHE_POLYS_AS_QUADS // QUADS should be a little faster
|
||
|
||
#define ENABLE_LIGHTING
|
||
|
||
/******************************************************************/
|
||
/* Private Data */
|
||
/******************************************************************/
|
||
|
||
/*
|
||
* Memory Regions
|
||
*
|
||
* These will be passed to us before rendering begins.
|
||
*/
|
||
|
||
static UINT8 *polygon_ram; // pointer to Real3D polygon RAM
|
||
static UINT8 *texture_ram; // pointer to Real3D texture RAM
|
||
static UINT8 *vrom; // pointer to VROM
|
||
|
||
/*
|
||
* Texture Mapping
|
||
*
|
||
* The smallest Model 3 textures are 32x32 and the total VRAM texture sheet
|
||
* is 2048x2048. Dividings this by 32 gives us 64x64. Each element contains an
|
||
* OpenGL ID for a texture. Because of 4bpp textures, 4 entries per texture
|
||
* must be maintained.
|
||
*/
|
||
|
||
static GLint texture_grid[64*64*4]; // 0 indicates unused
|
||
static GLbyte texture_buffer[512*512*4]; // for 1 texture
|
||
|
||
/*
|
||
* Mipmapping
|
||
*
|
||
* The bottom right of each 2048x1024 texture page contains the smallest
|
||
* mipmaps and the top-left has full-sized textures. These tables are used
|
||
* to determine where in a texture page a given mipmap is.
|
||
*/
|
||
|
||
static UINT mip_x[12] = { 0, 1024, 1536, 1792, 1920, 1984,
|
||
2016, 2032, 2040, 2044, 2046, 2047
|
||
};
|
||
static UINT mip_y[12] = { 0, 512, 768, 896, 960, 992,
|
||
1008, 1016, 1020, 1022, 1023, 0
|
||
};
|
||
static UINT mip_scale[7] = { 1, 2, 4, 8, 16, 32, 64 };
|
||
|
||
/*
|
||
* Layers
|
||
*
|
||
* Each layer is a 512x512 RGBA texture.
|
||
*/
|
||
|
||
static GLubyte * layer[4];
|
||
static GLuint layer_texture[4]; // IDs for the 4 layer textures
|
||
|
||
/*
|
||
* Resolution and Ratios
|
||
*
|
||
* The ratio of the OpenGL physical resolution to the Model 3 resolution is
|
||
* pre-calculated and passed to us via osd_gl_set_mode().
|
||
*/
|
||
|
||
static UINT xres, yres;
|
||
static float xres_ratio, yres_ratio;
|
||
|
||
/*
|
||
* Vertex and Texture Coordinate Configuration
|
||
*
|
||
* Vertices are in a 17.15 format on Step 1.0 and 13.19 on everything else.
|
||
* Texture coordinates can be configured on a per-polygon basis (13.3, 16.0.)
|
||
*/
|
||
|
||
static float vertex_divisor, texcoord_divisor;
|
||
|
||
typedef struct
|
||
{
|
||
GLfloat x, y, z; // vertices
|
||
GLfloat nx, ny, nz; // normals
|
||
GLfloat r, g, b, a; // colors
|
||
UINT32 uv; // texture coordinates
|
||
GLfloat u, v;
|
||
|
||
} VERTEX;
|
||
|
||
/*
|
||
* Model Cache
|
||
*/
|
||
|
||
#ifdef USE_MODEL_CACHE
|
||
|
||
typedef struct { GLfloat _0, _1; } VEC2;
|
||
typedef struct { GLfloat _0, _1, _2; } VEC3;
|
||
typedef struct { GLfloat _0, _1, _2, _3; } VEC4;
|
||
|
||
typedef struct
|
||
{
|
||
UINT ref_count; // number of references
|
||
UINT32 addr; // original address
|
||
UINT vbo_id; // vertex buffer object ID
|
||
UINT index; // index in the vertex array
|
||
UINT num_verts; // number of vertices
|
||
|
||
} HASH_ENTRY;
|
||
|
||
#define MODEL_CACHE_SIZE (256*1024) // ~150K seems to be enough
|
||
#define HASH_SIZE (2048*1024) // must be a power of two
|
||
#define HASH_FUNC(addr) ((addr >> 2) & (HASH_SIZE - 1))
|
||
|
||
static HASH_ENTRY * model_cache;
|
||
static UINT cur_model_index;
|
||
|
||
static VEC3 * model_vertex_array;
|
||
static VEC3 * model_normal_array;
|
||
static VEC4 * model_color_array;
|
||
static VEC2 * model_texcoord_array;
|
||
|
||
PFNGLBINDBUFFERARBPROC glBindBufferARB = NULL;
|
||
PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = NULL;
|
||
PFNGLGENBUFFERSARBPROC glGenBuffersARB = NULL;
|
||
PFNGLISBUFFERARBPROC glIsBufferARB = NULL;
|
||
PFNGLBUFFERDATAARBPROC glBufferDataARB = NULL;
|
||
PFNGLBUFFERSUBDATAARBPROC glBufferSubDataARB = NULL;
|
||
PFNGLMAPBUFFERARBPROC glMapBufferARB = NULL;
|
||
PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB = NULL;
|
||
PFNGLGETBUFFERPARAMETERIVARBPROC glGetBufferParameterivARB = NULL;
|
||
PFNGLGETBUFFERPOINTERVARBPROC glGetBufferPointervARB = NULL;
|
||
|
||
#endif
|
||
|
||
/******************************************************************/
|
||
/* Texture Management */
|
||
/******************************************************************/
|
||
|
||
/*
|
||
* void osd_renderer_invalidate_textures(UINT x, UINT y, UINT w, UINT h);
|
||
*
|
||
* Invalidates all textures that are within the rectangle in texture memory
|
||
* defined by the parameters.
|
||
*
|
||
* Parameters:
|
||
* x = Texture pixel X coordinate in texture memory.
|
||
* y = Y coordinate.
|
||
* w = Width of rectangle in pixels.
|
||
* h = Height.
|
||
*/
|
||
|
||
void osd_renderer_invalidate_textures(UINT x, UINT y, int u, int v, UINT w, UINT h, UINT8 *texture, int miplevel)
|
||
{
|
||
UINT yi;
|
||
|
||
x /= 32;
|
||
y /= 32;
|
||
w /= 32;
|
||
h /= 32;
|
||
|
||
//TODO: do we need to use w*4 here to take care of planes?
|
||
for (yi = 0; yi < h; yi++)
|
||
{
|
||
glDeleteTextures(w, &texture_grid[(yi + y) * (64*4) + x]);
|
||
memset((UINT8 *) &texture_grid[(yi + y) * (64*4) + x], 0, w * sizeof(GLint));
|
||
}
|
||
}
|
||
|
||
/*
|
||
* decode_texture():
|
||
*
|
||
* Decodes a single texture into texture_buffer[].
|
||
*/
|
||
|
||
static void decode_texture(UINT x, UINT y, UINT w, UINT h, UINT format)
|
||
{
|
||
UINT xi, yi;
|
||
UINT16 rgb16;
|
||
UINT8 gray8;
|
||
|
||
/*
|
||
* Formats:
|
||
*
|
||
* 0 = 16-bit A1RGB5
|
||
* 1 = 4-bit grayscale, field 0x000F
|
||
* 2 = 4-bit grayscale, field 0x00F0
|
||
* 3 = 4-bit grayscale, field 0x0F00
|
||
* 4 = 8-bit A4L4
|
||
* 5 = 8-bit grayscale
|
||
* 6 = 4-bit grayscale, field 0xF000 (?)
|
||
* 7 = RGBA4
|
||
*/
|
||
|
||
switch (format)
|
||
{
|
||
case 0: // 16-bit, A1RGB5
|
||
for (yi = 0; yi < h; yi++)
|
||
{
|
||
for (xi = 0; xi < w; xi++)
|
||
{
|
||
rgb16 = *(UINT16 *) &texture_ram[((y + yi) * 2048 + (x + xi)) * 2];
|
||
texture_buffer[((yi * w) + xi) * 4 + 0] = ((rgb16 >> 10) & 0x1F) << 3;
|
||
texture_buffer[((yi * w) + xi) * 4 + 1] = ((rgb16 >> 5) & 0x1F) << 3;
|
||
texture_buffer[((yi * w) + xi) * 4 + 2] = ((rgb16 >> 0) & 0x1F) << 3;
|
||
texture_buffer[((yi * w) + xi) * 4 + 3] = (rgb16 & 0x8000) ? 0 : 0xFF;
|
||
}
|
||
}
|
||
break;
|
||
case 1: // 4-bit grayscale
|
||
for (yi = 0; yi < h; yi++)
|
||
{
|
||
for (xi = 0; xi < w; xi++)
|
||
{
|
||
rgb16 = *(UINT16 *) &texture_ram[((y + yi) * 2048 + (x + xi)) * 2];
|
||
texture_buffer[((yi * w) + xi) * 4 + 0] = ((rgb16 >> 0) & 0xF) << 4;
|
||
texture_buffer[((yi * w) + xi) * 4 + 1] = ((rgb16 >> 0) & 0xF) << 4;
|
||
texture_buffer[((yi * w) + xi) * 4 + 2] = ((rgb16 >> 0) & 0xF) << 4;
|
||
texture_buffer[((yi * w) + xi) * 4 + 3] = (GLbyte) 0xFF;
|
||
}
|
||
}
|
||
break;
|
||
case 2: // 4-bit grayscale
|
||
for (yi = 0; yi < h; yi++)
|
||
{
|
||
for (xi = 0; xi < w; xi++)
|
||
{
|
||
rgb16 = *(UINT16 *) &texture_ram[((y + yi) * 2048 + (x + xi)) * 2];
|
||
texture_buffer[((yi * w) + xi) * 4 + 0] = ((rgb16 >> 4) & 0xF) << 4;
|
||
texture_buffer[((yi * w) + xi) * 4 + 1] = ((rgb16 >> 4) & 0xF) << 4;
|
||
texture_buffer[((yi * w) + xi) * 4 + 2] = ((rgb16 >> 4) & 0xF) << 4;
|
||
texture_buffer[((yi * w) + xi) * 4 + 3] = (GLbyte) 0xFF;
|
||
}
|
||
}
|
||
break;
|
||
case 3: // 4-bit grayscale
|
||
for (yi = 0; yi < h; yi++)
|
||
{
|
||
for (xi = 0; xi < w; xi++)
|
||
{
|
||
rgb16 = *(UINT16 *) &texture_ram[((y + yi) * 2048 + (x + xi)) * 2];
|
||
texture_buffer[((yi * w) + xi) * 4 + 0] = ((rgb16 >> 8) & 0xF) << 4;
|
||
texture_buffer[((yi * w) + xi) * 4 + 1] = ((rgb16 >> 8) & 0xF) << 4;
|
||
texture_buffer[((yi * w) + xi) * 4 + 2] = ((rgb16 >> 8) & 0xF) << 4;
|
||
texture_buffer[((yi * w) + xi) * 4 + 3] = (GLbyte) 0xFF;
|
||
}
|
||
}
|
||
break;
|
||
case 4: // 8-bit, A4L4
|
||
for (yi = 0; yi < h; yi++)
|
||
{
|
||
for (xi = 0; xi < w; xi++)
|
||
{
|
||
rgb16 = *(UINT16 *) &texture_ram[((y + yi) * 2048 + (x + xi / 2)) * 2];
|
||
gray8 = (rgb16 >> (!(xi & 1) * 8)) & 0xFF;
|
||
texture_buffer[((yi * w) + (xi ^ 1)) * 4 + 0] = ~(gray8 & 0x0F) << 4;
|
||
texture_buffer[((yi * w) + (xi ^ 1)) * 4 + 1] = ~(gray8 & 0x0F) << 4;
|
||
texture_buffer[((yi * w) + (xi ^ 1)) * 4 + 2] = ~(gray8 & 0x0F) << 4;
|
||
texture_buffer[((yi * w) + (xi ^ 1)) * 4 + 3] = (GLbyte) (gray8 & 0xF0); // fixme
|
||
}
|
||
}
|
||
break;
|
||
case 5: // 8-bit grayscale
|
||
for (yi = 0; yi < h; yi++)
|
||
{
|
||
for (xi = 0; xi < w; xi++)
|
||
{
|
||
rgb16 = *(UINT16 *) &texture_ram[((y + yi) * 2048 + (x + xi / 2)) * 2];
|
||
gray8 = (rgb16 >> (!(xi & 1) * 8)) & 0xFF;
|
||
texture_buffer[((yi * w) + (xi ^ 1)) * 4 + 0] = gray8;
|
||
texture_buffer[((yi * w) + (xi ^ 1)) * 4 + 1] = gray8;
|
||
texture_buffer[((yi * w) + (xi ^ 1)) * 4 + 2] = gray8;
|
||
texture_buffer[((yi * w) + (xi ^ 1)) * 4 + 3] = (GLbyte) 0xFF;
|
||
}
|
||
}
|
||
break;
|
||
case 6: // 4-bit grayscale
|
||
for (yi = 0; yi < h; yi++)
|
||
{
|
||
for (xi = 0; xi < w; xi++)
|
||
{
|
||
rgb16 = *(UINT16 *) &texture_ram[((y + yi) * 2048 + (x + xi)) * 2];
|
||
texture_buffer[((yi * w) + xi) * 4 + 0] = ((rgb16 >> 12) & 0xF) << 4;
|
||
texture_buffer[((yi * w) + xi) * 4 + 1] = ((rgb16 >> 12) & 0xF) << 4;
|
||
texture_buffer[((yi * w) + xi) * 4 + 2] = ((rgb16 >> 12) & 0xF) << 4;
|
||
texture_buffer[((yi * w) + xi) * 4 + 3] = (GLbyte) 0xFF;
|
||
}
|
||
}
|
||
break;
|
||
case 7: // 16-bit, RGBA4
|
||
for (yi = 0; yi < h; yi++)
|
||
{
|
||
for (xi = 0; xi < w; xi++)
|
||
{
|
||
rgb16 = *(UINT16 *) &texture_ram[((y + yi) * 2048 + (x + xi)) * 2];
|
||
texture_buffer[((yi * w) + xi) * 4 + 0] = ((rgb16 >> 12) & 0xF) << 4;
|
||
texture_buffer[((yi * w) + xi) * 4 + 1] = ((rgb16 >> 8) & 0xF) << 4;
|
||
texture_buffer[((yi * w) + xi) * 4 + 2] = ((rgb16 >> 4) & 0xF) << 4;
|
||
texture_buffer[((yi * w) + xi) * 4 + 3] = ((rgb16 >> 0) & 0xF) << 4;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* bind_texture():
|
||
*
|
||
* Given the coordinates of a texture and its size within the texture sheet,
|
||
* an OpenGL texture is created (along with its mipmaps) and uploaded. The
|
||
* texture will also be selected so that the caller may use it.
|
||
*
|
||
* The format is just bits 9 and 8 of polygon header word 7. The repeat mode
|
||
* is bits 0 and 1 of word 2. The texture Y coordinate includes the page.
|
||
*/
|
||
|
||
static void bind_texture(UINT x, UINT y, UINT w, UINT h, UINT format, UINT rep_mode)
|
||
{
|
||
UINT plane, page, num_mips, i, mx, my, mwidth, mheight;
|
||
GLint tex_id;
|
||
|
||
if (w > 512 || h > 512) // error!
|
||
{
|
||
LOG("texture.log", "%d,%d %dx%d\n", x, y, w, h);
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* Although we treat texture memory as a 2048x2048 buffer, it's actually
|
||
* split into 2 2048x1024 pages. We must extract this from the Y
|
||
* coordinate.
|
||
*/
|
||
|
||
page = y & 0x400; // page = 1024 or 0, can be added to Y coordinate
|
||
y &= 0x3FF; // Y coordinate within page
|
||
|
||
/*
|
||
* The lesser of the 2 dimensions determines the number of mipmaps that
|
||
* are defined. A dimension (width, height) divided by a mip_scale[]
|
||
* element yields the dimension of the mipmap.
|
||
*/
|
||
|
||
switch (min(w, h))
|
||
{
|
||
case 32: num_mips = 3; break;
|
||
case 64: num_mips = 4; break;
|
||
case 128: num_mips = 5; break;
|
||
case 256: num_mips = 6; break;
|
||
default: num_mips = 7; break;
|
||
}
|
||
|
||
/*
|
||
* The texture grid is a 64x64 array where each element corresponds to
|
||
* a 32x32 texture in texture RAM (2048x2048 pixels total.) Because 4-bit
|
||
* textures are stored like 16-bit textures with 4 different fields,
|
||
* we need 4 "planes" per element in the texture grid, because 1 32x32
|
||
* texture slot may actually contain 4 different 4-bit textures.
|
||
*/
|
||
|
||
switch (format) // determine which plane for 4bpp textures
|
||
{
|
||
case 1: plane = 3; break;
|
||
case 2: plane = 2; break;
|
||
case 3: plane = 1; break;
|
||
default: plane = 0; break;
|
||
}
|
||
|
||
/*
|
||
* If the texture is already cached, bind it and we're done
|
||
*/
|
||
|
||
tex_id = texture_grid[((y + page) / 32) * (64*4) + (x / 32) + plane];
|
||
if (tex_id != 0) // already exists, bind and exit
|
||
{
|
||
glBindTexture(GL_TEXTURE_2D, tex_id);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (rep_mode & 2) ? GL_MIRRORED_REPEAT_ARB : GL_REPEAT);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (rep_mode & 1) ? GL_MIRRORED_REPEAT_ARB : GL_REPEAT);
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* Decode the texture (all mipmaps), bind it, upload it, and mark it in
|
||
* the grid as used
|
||
*/
|
||
|
||
glGenTextures(1, &tex_id);
|
||
glBindTexture(GL_TEXTURE_2D, tex_id);
|
||
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (rep_mode & 2) ? GL_MIRRORED_REPEAT_ARB : GL_REPEAT);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (rep_mode & 1) ? GL_MIRRORED_REPEAT_ARB : GL_REPEAT);
|
||
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, num_mips - 1);
|
||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, (GLfloat) num_mips - 1);
|
||
|
||
for (i = 0; i < num_mips; i++)
|
||
{
|
||
mx = mip_x[i] + x / mip_scale[i];
|
||
my = mip_y[i] + y / mip_scale[i];
|
||
mwidth = w / mip_scale[i];
|
||
mheight = h / mip_scale[i];
|
||
|
||
decode_texture(mx, my + page, mwidth, mheight, format);
|
||
glTexImage2D(GL_TEXTURE_2D, i, GL_RGBA8, mwidth, mheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_buffer);
|
||
}
|
||
|
||
texture_grid[((y + page) / 32) * (64*4) + (x / 32) + plane] = tex_id; // mark texture as used
|
||
}
|
||
|
||
/******************************************************************/
|
||
/* Model Caching and Drawing */
|
||
/******************************************************************/
|
||
|
||
#define get_word(a) (*a)
|
||
|
||
/*
|
||
* Useful Polygon Header Macros
|
||
*
|
||
* The parameter is a 7-element array of 32-bit words (the 7-word header.)
|
||
*/
|
||
|
||
#define STOP_BIT(h) (h[1] & 0x04)
|
||
#define IS_QUAD(h) (h[0] & 0x40)
|
||
#define GET_LINK_DATA(h) (h[0] & 0x0F)
|
||
#define IS_UV_16(h) (h[1] & 0x40)
|
||
#define IS_TEXTURE_ENABLED(h) (h[6] & 0x04000000)
|
||
#define IS_TEXTURE_TRANSPARENT(h) (h[6] & 0x80000000)
|
||
#define IS_LIGHTING_DISABLED(h) (h[6] & 0x10000)
|
||
#define IS_OPAQUE(h) (h[6] & 0x00800000)
|
||
#define COLOR_RED(h) ((h[4] >> 24) & 0xFF)
|
||
#define COLOR_GREEN(h) ((h[4] >> 16) & 0xFF)
|
||
#define COLOR_BLUE(h) ((h[4] >> 8) & 0xFF)
|
||
#define GET_TEXTURE_X(h) ((((h[4] & 0x1F) << 1) | ((h[5] & 0x80) >> 7)) * 32)
|
||
#define GET_TEXTURE_Y(h) (((h[5] & 0x1F) | ((h[4] & 0x40) >> 1)) * 32)
|
||
#define GET_TEXTURE_WIDTH(h) (32 << ((h[3] >> 3) & 7))
|
||
#define GET_TEXTURE_HEIGHT(h) (32 << ((h[3] >> 0) & 7))
|
||
#define GET_TEXTURE_FORMAT(h) ((h[6] >> 7) & 7)
|
||
#define GET_TEXTURE_REPEAT(h) (h[2] & 3)
|
||
#define GET_TRANSLUCENCY(h) ((h[6] >> 18) & 0x1F)
|
||
|
||
/*
|
||
* convert_vertex_to_float():
|
||
*
|
||
* Converts from a fixed-point vertex to a float. Accepts a signed INT32.
|
||
*/
|
||
|
||
static float convert_vertex_to_float(INT32 num)
|
||
{
|
||
return (float) num / vertex_divisor;
|
||
}
|
||
|
||
/*
|
||
* convert_texcoord_to_float():
|
||
*
|
||
* Converts a texture coordinate into a floating point number.
|
||
*/
|
||
|
||
static float convert_texcoord_to_float(UINT32 num)
|
||
{
|
||
return (float) num / texcoord_divisor;
|
||
}
|
||
|
||
/*
|
||
* Model Cache
|
||
*/
|
||
|
||
#ifdef USE_MODEL_CACHE
|
||
|
||
static BOOL init_model_cache(void)
|
||
{
|
||
UINT i;
|
||
|
||
// get pointer to VBO interface
|
||
|
||
if(glBindBufferARB == NULL)
|
||
{
|
||
glBindBufferARB = (PFNGLBINDBUFFERARBPROC)
|
||
osd_gl_get_proc_address("glBindBufferARB");
|
||
glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)
|
||
osd_gl_get_proc_address("glDeleteBuffersARB");
|
||
glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)
|
||
osd_gl_get_proc_address("glGenBuffersARB");
|
||
glIsBufferARB = (PFNGLISBUFFERARBPROC)
|
||
osd_gl_get_proc_address("glIsBufferARB");
|
||
glBufferDataARB = (PFNGLBUFFERDATAARBPROC)
|
||
osd_gl_get_proc_address("glBufferDataARB");
|
||
glBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC)
|
||
osd_gl_get_proc_address("glBufferSubDataARB");
|
||
glMapBufferARB = (PFNGLMAPBUFFERARBPROC)
|
||
osd_gl_get_proc_address("glMapBufferARB");
|
||
glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)
|
||
osd_gl_get_proc_address("glUnmapBufferARB");
|
||
glGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC)
|
||
osd_gl_get_proc_address("glGetBufferParameterivARB");
|
||
glGetBufferPointervARB = (PFNGLGETBUFFERPOINTERVARBPROC)
|
||
osd_gl_get_proc_address("glGetBufferPointervARB");
|
||
}
|
||
|
||
// free any resident VBO
|
||
|
||
/*
|
||
for(i = 0; i < HASH_SIZE; i++);
|
||
if(glIsBufferARB(model_cache[i].vbo_id));
|
||
glDeleteBuffersARB(1, &model_cache[i].vbo_id);
|
||
*/
|
||
|
||
// free unfreed buffers
|
||
|
||
SAFE_FREE(model_cache);
|
||
SAFE_FREE(model_vertex_array);
|
||
SAFE_FREE(model_normal_array);
|
||
SAFE_FREE(model_color_array);
|
||
SAFE_FREE(model_texcoord_array);
|
||
|
||
// alloc buffers
|
||
|
||
model_cache = (HASH_ENTRY *)malloc(HASH_SIZE * sizeof(HASH_ENTRY));
|
||
model_vertex_array = (VEC3 *)malloc(MODEL_CACHE_SIZE * sizeof(VEC3));
|
||
model_normal_array = (VEC3 *)malloc(MODEL_CACHE_SIZE * sizeof(VEC3));
|
||
model_color_array = (VEC4 *)malloc(MODEL_CACHE_SIZE * sizeof(VEC4));
|
||
model_texcoord_array = (VEC2 *)malloc(MODEL_CACHE_SIZE * sizeof(VEC2));
|
||
|
||
// check buffer consistency
|
||
|
||
if( (model_cache == NULL) ||
|
||
(model_vertex_array == NULL) ||
|
||
(model_normal_array == NULL) ||
|
||
(model_color_array == NULL) ||
|
||
(model_texcoord_array == NULL) )
|
||
return FALSE;
|
||
|
||
// reset the model cache
|
||
|
||
cur_model_index = 0;
|
||
|
||
// setup the hash table
|
||
|
||
for(i = 0; i < HASH_SIZE; i++)
|
||
{
|
||
model_cache[i].ref_count = 0;
|
||
model_cache[i].addr = 0;
|
||
model_cache[i].vbo_id = (UINT)-1;
|
||
model_cache[i].index = 0;
|
||
model_cache[i].num_verts = 0;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
INLINE void cache_vertex(UINT index, VERTEX * src)
|
||
{
|
||
model_vertex_array[index]._0 = src->x;
|
||
model_vertex_array[index]._1 = src->y;
|
||
model_vertex_array[index]._2 = src->z;
|
||
|
||
model_normal_array[index]._0 = src->nx;
|
||
model_normal_array[index]._1 = src->ny;
|
||
model_normal_array[index]._2 = src->nz;
|
||
|
||
model_color_array[index]._0 = src->r;
|
||
model_color_array[index]._1 = src->g;
|
||
model_color_array[index]._2 = src->b;
|
||
model_color_array[index]._3 = src->a;
|
||
|
||
model_texcoord_array[index]._0 = src->u;
|
||
model_texcoord_array[index]._1 = src->v;
|
||
}
|
||
|
||
UINT model_addr = 0;
|
||
|
||
static BOOL cache_model(UINT32 *m, HASH_ENTRY *h)
|
||
{
|
||
VERTEX v[4], prev_v[4];
|
||
UINT link_data, i, j, num_verts, index;
|
||
GLfloat texture_width = 1.0f, texture_height = 1.0f,
|
||
nx, ny, nz, color[4];
|
||
BOOL end;
|
||
|
||
UINT has_tex = 0, old_tex_fmt = 0, old_tex_x = 0, old_tex_y = 0;
|
||
GLfloat old_tex_w = 0, old_tex_h = 0;
|
||
|
||
if (*m == 0)
|
||
return TRUE;
|
||
|
||
if(cur_model_index >= MODEL_CACHE_SIZE)
|
||
return FALSE;
|
||
|
||
index = 0; // number of cached vertices, actually
|
||
|
||
h->index = cur_model_index;
|
||
h->ref_count ++;
|
||
|
||
#ifdef ANALYZE_VROM_MODELS
|
||
message(0, "============================================================");
|
||
#endif
|
||
|
||
do
|
||
{
|
||
// setup polygon info
|
||
|
||
num_verts = (m[0] & 0x40) ? 4 : 3;
|
||
link_data = m[0] & 0xF;
|
||
|
||
end = m[1] & 4;
|
||
|
||
nx = (GLfloat) (((INT32) m[0]) >> 8) / 4194304.0f;
|
||
ny = (GLfloat) (((INT32) m[2]) >> 8) / 4194304.0f;
|
||
nz = (GLfloat) (((INT32) m[3]) >> 8) / 4194304.0f;
|
||
|
||
color[0] = (GLfloat) COLOR_RED(m) / 255.0f;
|
||
color[1] = (GLfloat) COLOR_GREEN(m) / 255.0f;
|
||
color[2] = (GLfloat) COLOR_BLUE(m) / 255.0f;
|
||
color[3] = 1.0f;
|
||
|
||
#ifdef ANALYZE_VROM_MODELS
|
||
|
||
if((IS_TEXTURE_ENABLED(m) ? 1 : 0) != has_tex)
|
||
message(0, "model at %08X: has_tex %u --> %u", model_addr, has_tex, IS_TEXTURE_ENABLED(m) ? 1 : 0);
|
||
has_tex = IS_TEXTURE_ENABLED(m) ? 1 : 0;
|
||
|
||
if(IS_TEXTURE_ENABLED(m))
|
||
{
|
||
UINT texture_format, texture_x, texture_y;
|
||
|
||
texture_width = (GLfloat) GET_TEXTURE_WIDTH(m);
|
||
texture_height = (GLfloat) GET_TEXTURE_HEIGHT(m);
|
||
texture_format = GET_TEXTURE_FORMAT(m);
|
||
|
||
texture_x = GET_TEXTURE_X(m);
|
||
texture_y = GET_TEXTURE_Y(m);
|
||
//IS_TEXTURE_TRANSPARENT(m);
|
||
//GET_TEXTURE_REPEAT(m);
|
||
|
||
if(old_tex_fmt != texture_format)
|
||
message(0, "model at %08X: tex_fmt %u --> %u", model_addr, old_tex_fmt, texture_format);
|
||
if(old_tex_x != texture_x)
|
||
message(0, "model at %08X: tex_x %u --> %u", model_addr, old_tex_x, texture_x);
|
||
if(old_tex_y != texture_y)
|
||
message(0, "model at %08X: tex_y %u --> %u", model_addr, old_tex_y, texture_y);
|
||
if(old_tex_w != texture_width)
|
||
message(0, "model at %08X: tex_w %f --> %f", model_addr, old_tex_w, texture_width);
|
||
if(old_tex_h != texture_height)
|
||
message(0, "model at %08X: tex_h %f --> %f", model_addr, old_tex_h, texture_height);
|
||
|
||
old_tex_fmt = texture_format;
|
||
old_tex_x = texture_x;
|
||
old_tex_y = texture_y;
|
||
old_tex_w = texture_width;
|
||
old_tex_h = texture_height;
|
||
}
|
||
|
||
#endif
|
||
|
||
// select texture coordinate format
|
||
|
||
if (IS_UV_16(m))
|
||
texcoord_divisor = 1.0f; // 16.0
|
||
else
|
||
texcoord_divisor = 8.0f; // 13.3
|
||
|
||
m += 7;
|
||
|
||
// fetch all previous vertices that we need
|
||
|
||
i = 0;
|
||
for(j = 0; j < 4; j++)
|
||
{
|
||
if ((link_data & 1))
|
||
v[i++] = prev_v[j];
|
||
link_data >>= 1;
|
||
}
|
||
|
||
// fetch remaining vertices
|
||
|
||
for( ; i < num_verts; i++)
|
||
{
|
||
v[i].x = convert_vertex_to_float(*m++);
|
||
v[i].y = convert_vertex_to_float(*m++);
|
||
v[i].z = convert_vertex_to_float(*m++);
|
||
v[i].nx = nx;
|
||
v[i].ny = ny;
|
||
v[i].nz = nz;
|
||
v[i].r = color[0];
|
||
v[i].g = color[1];
|
||
v[i].b = color[2];
|
||
v[i].a = color[3];
|
||
v[i].u = convert_texcoord_to_float((*m >> 16) / texture_width);
|
||
v[i].v = convert_texcoord_to_float((*m & 0xFFFF) / texture_height);
|
||
|
||
m++;
|
||
}
|
||
|
||
// save back old vertices
|
||
|
||
for(i = 0; i < num_verts; i++)
|
||
prev_v[i] = v[i];
|
||
|
||
// cache all the vertices
|
||
|
||
cache_vertex(cur_model_index + index++, &v[0]);
|
||
cache_vertex(cur_model_index + index++, &v[1]);
|
||
cache_vertex(cur_model_index + index++, &v[2]);
|
||
|
||
#ifdef CACHE_POLYS_AS_QUADS
|
||
|
||
if(num_verts == 3)
|
||
cache_vertex(cur_model_index + index++, &v[2]);
|
||
else
|
||
cache_vertex(cur_model_index + index++, &v[3]);
|
||
#else
|
||
if(num_verts == 4)
|
||
{
|
||
cache_vertex(cur_model_index + index++, &v[0]);
|
||
cache_vertex(cur_model_index + index++, &v[2]);
|
||
cache_vertex(cur_model_index + index++, &v[3]);
|
||
}
|
||
#endif
|
||
}
|
||
while(!end);
|
||
|
||
h->num_verts = index;
|
||
cur_model_index += index;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static BOOL draw_cached_model(UINT addr, UINT32 * m)
|
||
{
|
||
HASH_ENTRY * h = &model_cache[HASH_FUNC(addr)];
|
||
|
||
model_addr = addr;
|
||
|
||
if(h->ref_count == 0) // never referenced before, cache it
|
||
{
|
||
if(!cache_model(m, h))
|
||
{
|
||
error("draw_cached_model(): model cache overrun!\n");
|
||
return FALSE;
|
||
}
|
||
h->addr = addr;
|
||
}
|
||
else // already cached
|
||
{
|
||
if(h->addr != addr) // same hash entry for different models
|
||
{
|
||
error("draw_cached_model(): hash table hit! (%08X)\n", addr);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
glDisable(GL_TEXTURE_2D);
|
||
|
||
glEnableClientState(GL_VERTEX_ARRAY);
|
||
glEnableClientState(GL_NORMAL_ARRAY);
|
||
glEnableClientState(GL_COLOR_ARRAY);
|
||
// glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||
|
||
#ifdef CACHE_POLYS_AS_QUADS
|
||
glDrawArrays(GL_QUADS, h->index, h->num_verts);
|
||
#else
|
||
glDrawArrays(GL_TRIANGLES, h->index, h->num_verts);
|
||
#endif
|
||
|
||
glDisableClientState(GL_VERTEX_ARRAY);
|
||
glDisableClientState(GL_NORMAL_ARRAY);
|
||
glDisableClientState(GL_COLOR_ARRAY);
|
||
// glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||
|
||
glEnable(GL_TEXTURE_2D);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
#endif // USE_MODEL_CACHE
|
||
|
||
/*
|
||
* void osd_renderer_draw_model(UINT32 *mdl, UINT32 addr, BOOL little_endian);
|
||
*
|
||
* Draws a model.
|
||
*
|
||
* Parameters:
|
||
* mdl = Pointer to model to draw.
|
||
* addr = Real3D address (for debugging purposes, in case it
|
||
* needs to be printed.
|
||
* little_endian = True if memory is in little endian format.
|
||
*/
|
||
|
||
void osd_renderer_draw_model(UINT32 *mdl, UINT32 addr, BOOL little_endian)
|
||
{
|
||
VERTEX v[4], prev_v[4];
|
||
UINT32 header[7];
|
||
UINT link_data, texture_width = 1.0f, texture_height = 1.0f;
|
||
INT i, j, num_verts;
|
||
GLfloat u_coord, v_coord, nx, ny, nz, color[4];
|
||
|
||
#ifdef WIREFRAME
|
||
glPolygonMode(GL_FRONT, GL_LINE);
|
||
glPolygonMode(GL_BACK, GL_LINE);
|
||
glDisable(GL_TEXTURE_2D);
|
||
glColor3ub(0xFF, 0xFF, 0xFF);
|
||
#endif
|
||
|
||
//if (get_word(mdl) == 0)
|
||
// return;
|
||
|
||
/*
|
||
* Draw VROM (static) models
|
||
*/
|
||
|
||
#ifdef USE_MODEL_CACHE
|
||
if(!little_endian)
|
||
{
|
||
if(draw_cached_model(addr, mdl))
|
||
return;
|
||
// if draw_cached_model() failed, draw the uncached model
|
||
}
|
||
#endif
|
||
|
||
/*
|
||
* Draw RAM (dynamic) models
|
||
*/
|
||
|
||
do
|
||
{
|
||
/*
|
||
* Fetch the 7 header words
|
||
*/
|
||
|
||
for (i = 0; i < 7; i++)
|
||
header[i] = get_word(mdl++);
|
||
|
||
if( header[6] == 0 )
|
||
return;
|
||
|
||
/*
|
||
* Get normals
|
||
*/
|
||
|
||
nx = (GLfloat) (((INT32) header[1]) >> 8) / 4194304.0f;
|
||
ny = (GLfloat) (((INT32) header[2]) >> 8) / 4194304.0f;
|
||
nz = (GLfloat) (((INT32) header[3]) >> 8) / 4194304.0f;
|
||
|
||
/*
|
||
* Fetch the all of the vertices
|
||
*/
|
||
|
||
num_verts = IS_QUAD(header) ? 4 : 3;
|
||
link_data = GET_LINK_DATA(header);
|
||
|
||
i = 0;
|
||
for (j = 0; j < 4; j++) // fetch all previous vertices that we need
|
||
{
|
||
if ((link_data & 1))
|
||
v[i++] = prev_v[j];
|
||
link_data >>= 1;
|
||
}
|
||
|
||
for ( ; i < num_verts; i++) // fetch remaining vertices
|
||
{
|
||
v[i].x = convert_vertex_to_float(get_word(mdl++));
|
||
v[i].y = convert_vertex_to_float(get_word(mdl++));
|
||
v[i].z = convert_vertex_to_float(get_word(mdl++));
|
||
v[i].nx = nx;
|
||
v[i].ny = ny;
|
||
v[i].nz = nz;
|
||
v[i].uv = get_word(mdl++);
|
||
}
|
||
|
||
/*
|
||
* Set color and material properties.
|
||
*
|
||
* Something is hosed here. The Model 3 works differently. Setting
|
||
* the ambient and diffuse material properties to all 1.0 fixes VON2
|
||
* but causes issues in Scud Race (shadows turn white.)
|
||
*/
|
||
|
||
color[0] = (GLfloat) COLOR_RED(header) / 255.0f;
|
||
color[1] = (GLfloat) COLOR_GREEN(header) / 255.0f;
|
||
color[2] = (GLfloat) COLOR_BLUE(header) / 255.0f;
|
||
if (IS_OPAQUE(header))
|
||
color[3] = 1.0f; // TODO: if translucent polygon, modify this
|
||
else
|
||
color[3] = GET_TRANSLUCENCY(header) / 31.0f;
|
||
|
||
glColor4fv(color);
|
||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color);
|
||
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color);
|
||
{
|
||
GLfloat v[4] = { 1.0,1.0,1.0,1.0 };
|
||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, v);
|
||
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, v);
|
||
}
|
||
|
||
|
||
#ifdef ENABLE_LIGHTING
|
||
if (IS_LIGHTING_DISABLED(header))
|
||
glDisable(GL_LIGHTING);
|
||
#endif
|
||
|
||
/*
|
||
* Draw it (and save the vertices to prev_v[])
|
||
*/
|
||
|
||
if (IS_TEXTURE_ENABLED(header))
|
||
{
|
||
texture_width = GET_TEXTURE_WIDTH(header);
|
||
texture_height = GET_TEXTURE_HEIGHT(header);
|
||
|
||
bind_texture(GET_TEXTURE_X(header), GET_TEXTURE_Y(header),
|
||
texture_width, texture_height,
|
||
GET_TEXTURE_FORMAT(header), GET_TEXTURE_REPEAT(header));
|
||
if (IS_TEXTURE_TRANSPARENT(header))
|
||
{
|
||
glEnable(GL_ALPHA_TEST);
|
||
glAlphaFunc(GL_GREATER, 0.95f);
|
||
}
|
||
}
|
||
else
|
||
glDisable(GL_TEXTURE_2D);
|
||
|
||
if (IS_UV_16(header))
|
||
texcoord_divisor = 1.0f; // 16.0 texture coordinates
|
||
else
|
||
texcoord_divisor = 8.0f; // 13.3
|
||
|
||
glBegin((num_verts == 4) ? GL_QUADS : GL_TRIANGLES);
|
||
|
||
for (i = 0; i < num_verts; i++)
|
||
{
|
||
prev_v[i] = v[i];
|
||
u_coord = convert_texcoord_to_float(v[i].uv >> 16);
|
||
v_coord = convert_texcoord_to_float(v[i].uv & 0xFFFF);
|
||
glNormal3f(v[i].nx, v[i].ny, v[i].nz);
|
||
glTexCoord2f(u_coord / (GLfloat) texture_width, v_coord / (GLfloat) texture_height);
|
||
glVertex3f(v[i].x, v[i].y, v[i].z);
|
||
}
|
||
glEnd();
|
||
|
||
if (IS_TEXTURE_ENABLED(header))
|
||
glDisable(GL_ALPHA_TEST);
|
||
else
|
||
glEnable(GL_TEXTURE_2D);
|
||
|
||
#ifdef ENABLE_LIGHTING
|
||
if (IS_LIGHTING_DISABLED(header))
|
||
glEnable(GL_LIGHTING);
|
||
#endif
|
||
}
|
||
while (!STOP_BIT(header)); // continue until stop bit is hit
|
||
}
|
||
|
||
/******************************************************************/
|
||
/* Lighting */
|
||
/******************************************************************/
|
||
|
||
/*
|
||
* void osd_renderer_set_light(INT light_num, LIGHT *light);
|
||
*
|
||
* Sets a light.
|
||
*
|
||
* Parameters:
|
||
* light_num = Which light number.
|
||
* light = Light data.
|
||
*/
|
||
|
||
void osd_renderer_set_light(INT light_num, LIGHT *light)
|
||
{
|
||
GLfloat v[4];
|
||
|
||
switch (light->type)
|
||
{
|
||
case LIGHT_PARALLEL:
|
||
v[0] = light->u;
|
||
v[1] = light->v;
|
||
v[2] = light->w;
|
||
v[3] = 0.0f; // this is a directional light
|
||
glLightfv(GL_LIGHT0 + light_num, GL_POSITION, v);
|
||
|
||
v[0] = v[1] = v[2] = light->diffuse_intensity; // R, G, B
|
||
v[3] = 1.0f;
|
||
glLightfv(GL_LIGHT0 + light_num, GL_DIFFUSE, v);
|
||
|
||
v[0] = v[1] = v[2] = light->ambient_intensity; // R, G, B
|
||
v[3] = 1.0f;
|
||
glLightfv(GL_LIGHT0 + light_num, GL_AMBIENT, v);
|
||
|
||
break;
|
||
default:
|
||
error("Unhandled light type: %d", light->type);
|
||
break;
|
||
}
|
||
|
||
glEnable(GL_LIGHT0 + light_num);
|
||
}
|
||
|
||
/******************************************************************/
|
||
/* Viewport and Projection */
|
||
/******************************************************************/
|
||
|
||
/*
|
||
* void osd_renderer_set_coordinate_system(const MATRIX m);
|
||
*
|
||
* Applies the coordinate system matrix and makes adjustments so that the
|
||
* Model 3 coordinate system is properly handled.
|
||
*
|
||
* Parameters:
|
||
* m = Matrix.
|
||
*/
|
||
|
||
void osd_renderer_set_coordinate_system(const MATRIX m)
|
||
{
|
||
glLoadIdentity();
|
||
glScalef(1.0, -1.0, -1.0);
|
||
glMultMatrixf(m);
|
||
}
|
||
|
||
/*
|
||
* void osd_renderer_set_viewport(const VIEWPORT *vp);
|
||
*
|
||
* Sets up a viewport. Enables Z-buffering.
|
||
*
|
||
* Parameters:
|
||
* vp = Viewport and projection parameters.
|
||
*/
|
||
|
||
void osd_renderer_set_viewport(const VIEWPORT *vp)
|
||
{
|
||
glViewport((UINT) ((float) vp->x * xres_ratio),
|
||
(UINT) ((float) (384.0f - (vp->y + vp->height)) * yres_ratio),
|
||
(GLint) (vp->width * xres_ratio),
|
||
(GLint) (vp->height * yres_ratio));
|
||
|
||
glMatrixMode(GL_PROJECTION);
|
||
glLoadIdentity();
|
||
gluPerspective(vp->up + vp->down,
|
||
(GLfloat) vp->width / (GLfloat) vp->height,
|
||
0.1, 1000000.0);
|
||
|
||
glMatrixMode(GL_MODELVIEW);
|
||
glLoadIdentity();
|
||
glScalef(1.0, -1.0, -1.0); // Model 3 default coordinate system (for lighting)
|
||
|
||
glEnable(GL_DEPTH_TEST);
|
||
glDepthFunc(GL_LESS);
|
||
}
|
||
|
||
/******************************************************************/
|
||
/* Matrix Stack */
|
||
/* */
|
||
/* Matrices are stored in OpenGL's column major format so they */
|
||
/* can be passed directly to OpenGL matrix functions. */
|
||
/******************************************************************/
|
||
|
||
/*
|
||
* void osd_renderer_multiply_matrix(MATRIX m);
|
||
*
|
||
* Multiplies the top of the matrix stack by the specified matrix
|
||
*
|
||
* Parameters:
|
||
* m = Matrix to multiply.
|
||
*/
|
||
|
||
void osd_renderer_multiply_matrix(MATRIX m)
|
||
{
|
||
glMultMatrixf(m);
|
||
}
|
||
|
||
/*
|
||
* void osd_renderer_translate_matrix(float x, float y, float z);
|
||
*
|
||
* Translates the top of the matrix stack.
|
||
*
|
||
* Parameters:
|
||
* x = Translation along X axis.
|
||
* y = Y axis.
|
||
* z = Z axis.
|
||
*/
|
||
|
||
void osd_renderer_translate_matrix(float x, float y, float z)
|
||
{
|
||
glTranslatef(x, y, z);
|
||
}
|
||
|
||
/*
|
||
* void osd_renderer_push_matrix(void);
|
||
*
|
||
* Pushes a matrix on to the stack. The matrix pushed is the former top of the
|
||
* stack.
|
||
*/
|
||
|
||
void osd_renderer_push_matrix()
|
||
{
|
||
glPushMatrix();
|
||
}
|
||
|
||
/*
|
||
* void osd_renderer_pop_matrix(void);
|
||
*
|
||
* Pops a matrix off the top of the stack.
|
||
*/
|
||
|
||
void osd_renderer_pop_matrix(void)
|
||
{
|
||
glPopMatrix();
|
||
}
|
||
|
||
/******************************************************************/
|
||
/* Misc. State Management */
|
||
/******************************************************************/
|
||
|
||
/*
|
||
* void osd_renderer_begin(void);
|
||
*
|
||
* Called just before rendering begins for the current frame. Does nothing.
|
||
*/
|
||
|
||
void osd_renderer_begin_3d_scene(void)
|
||
{
|
||
}
|
||
|
||
/*
|
||
* void osd_renderer_end(void);
|
||
*
|
||
* Called just after rendering ends for the current frame. Does nothing.
|
||
*/
|
||
|
||
void osd_renderer_end_3d_scene(void)
|
||
{
|
||
}
|
||
|
||
/*
|
||
* void osd_renderer_clear(BOOL fbuf, BOOL zbuf);
|
||
*
|
||
* Clears the frame and/or Z-buffer.
|
||
*
|
||
* Parameters:
|
||
* fbuf = If true, framebuffer (color buffer) is cleared.
|
||
* zbuf = If true, Z-buffer is cleared.
|
||
*/
|
||
|
||
void osd_renderer_clear(BOOL fbuf, BOOL zbuf)
|
||
{
|
||
GLbitfield buffers = 0;
|
||
|
||
if (fbuf) buffers |= GL_COLOR_BUFFER_BIT;
|
||
if (zbuf) buffers |= GL_DEPTH_BUFFER_BIT;
|
||
|
||
glClear(buffers);
|
||
}
|
||
|
||
/******************************************************************/
|
||
/* Tile Layer Interface */
|
||
/******************************************************************/
|
||
|
||
static BOOL coff_enabled = FALSE;
|
||
static GLfloat coff[3];
|
||
|
||
void osd_renderer_set_color_offset(BOOL is_enabled,
|
||
FLOAT32 r,
|
||
FLOAT32 g,
|
||
FLOAT32 b)
|
||
{
|
||
if((coff_enabled = is_enabled) != FALSE)
|
||
{
|
||
coff[0] = r;
|
||
coff[1] = g;
|
||
coff[2] = b;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* void osd_renderer_draw_layer(UINT layer_num);
|
||
*
|
||
* Draws a layer. Disables Z-buffering and creates an orthogonal projection.
|
||
*
|
||
* Parameters:
|
||
* layer_num = Layer to draw.
|
||
*/
|
||
|
||
void osd_renderer_draw_layer(UINT layer_num)
|
||
{
|
||
GLfloat temp[3]; // combiner color
|
||
|
||
/*
|
||
* Disable lighting and set the replace texture mode
|
||
*/
|
||
|
||
glDisable(GL_LIGHTING);
|
||
|
||
if(!coff_enabled) // no color offset
|
||
{
|
||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* The color combiner operations is set as follows.
|
||
*
|
||
* final_color = texture_color <20> primary_color
|
||
* final_alpha = texture_alpha
|
||
*
|
||
* OpenGL's color combiner doesn't allow specification of individual
|
||
* color component operations (to my knowledge) -- Stefano.
|
||
*/
|
||
|
||
// setup color combiner
|
||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
|
||
|
||
if(coff[0] > 0.0f && coff[1] > 0.0f && coff[2] > 0.0f)
|
||
{
|
||
// set combiner mode to ADD
|
||
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD);
|
||
temp[0] = coff[0];
|
||
temp[1] = coff[1];
|
||
temp[2] = coff[2];
|
||
}
|
||
else if(coff[0] < 0.0f && coff[1] < 0.0f && coff[2] < 0.0f)
|
||
{
|
||
// set combiner mode to SUB
|
||
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_SUBTRACT);
|
||
temp[0] = -coff[0];
|
||
temp[1] = -coff[1];
|
||
temp[2] = -coff[2];
|
||
}
|
||
#if 0
|
||
else
|
||
error("osd_renderer_draw_layer: non-uniform color offset\n");
|
||
#endif
|
||
|
||
// setup color combiner operation
|
||
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
|
||
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
|
||
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
|
||
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
|
||
glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1.0f);
|
||
|
||
// setup alpha combiner operation
|
||
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
|
||
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
|
||
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
|
||
glTexEnvi(GL_TEXTURE_ENV, GL_ALPHA_SCALE, 1.0f);
|
||
|
||
// setup primary fragment color
|
||
glColor3fv(temp);
|
||
}
|
||
|
||
/*
|
||
* Set orthogonal projection and disable Z-buffering and lighting
|
||
*/
|
||
|
||
glDisable(GL_DEPTH_TEST);
|
||
|
||
glViewport(0, 0, xres, yres);
|
||
glMatrixMode(GL_PROJECTION);
|
||
glLoadIdentity();
|
||
gluOrtho2D(0.0, 1.0, 1.0, 0.0);
|
||
glMatrixMode(GL_MODELVIEW);
|
||
glLoadIdentity();
|
||
|
||
/*
|
||
* Draw the texture
|
||
*/
|
||
|
||
glEnable(GL_BLEND); // enable blending, an alpha of 0xFF is opaque, 0 is transparent
|
||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||
|
||
glBindTexture(GL_TEXTURE_2D, layer_texture[layer_num]);
|
||
glBegin(GL_QUADS);
|
||
glTexCoord2f(0.0, 0.0); glVertex3f(0.0, 0.0, 0.0);
|
||
glTexCoord2f(496.0 / 512.0, 0.0); glVertex3f(1.0, 0.0, 0.0);
|
||
glTexCoord2f(496.0 / 512.0, 384.0 / 512.0); glVertex3f(1.0, 1.0, 0.0);
|
||
glTexCoord2f(0.0, 384.0 / 512.0); glVertex3f(0.0, 1.0, 0.0);
|
||
glEnd();
|
||
|
||
glDisable(GL_BLEND);
|
||
|
||
/*
|
||
* Re-enable lighting
|
||
*/
|
||
|
||
#ifdef ENABLE_LIGHTING
|
||
glEnable(GL_LIGHTING);
|
||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||
#endif
|
||
}
|
||
|
||
/*
|
||
* void osd_renderer_get_layer_buffer(UINT layer_num, UINT8 **buffer,
|
||
* UINT *pitch);
|
||
*
|
||
* Obtains a layer buffer for the caller (the tilegen) to render to.
|
||
*
|
||
* Parameters:
|
||
* layer_num = The layer number (0-3.)
|
||
* buffer = A pointer to the buffer pointer to set to the layer
|
||
* memory.
|
||
* pitch = Width of layer buffer in pixels will be written to this
|
||
* pointer.
|
||
*/
|
||
|
||
void osd_renderer_get_layer_buffer(UINT layer_num, UINT8 **buffer, UINT *pitch)
|
||
{
|
||
*pitch = 512; // currently all layer textures are 512x512
|
||
*buffer = (UINT8 *) layer[layer_num];
|
||
}
|
||
|
||
/*
|
||
* void osd_renderer_free_layer_buffer(UINT layer_num);
|
||
*
|
||
* Releases the layer buffer. It is not actually freed, this is primarily for
|
||
* APIs that need an "unlock" call after drawing to a texture is done. Here,
|
||
* we use it to upload the layer texture.
|
||
*
|
||
* Parameters:
|
||
* layer_num = Layer number.
|
||
*/
|
||
|
||
void osd_renderer_free_layer_buffer(UINT layer_num)
|
||
{
|
||
glBindTexture(GL_TEXTURE_2D, layer_texture[layer_num]);
|
||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 512, 512, GL_RGBA, GL_UNSIGNED_BYTE, layer[layer_num]);
|
||
}
|
||
|
||
/*
|
||
* void osd_renderer_draw_text(int x, int y, const char* string, DWORD color, BOOL shadow);
|
||
*
|
||
* Draws text on screen
|
||
*
|
||
* Parameters:
|
||
* x = Left X-coordinate of the text
|
||
* y = Top Y-coordinate of the text
|
||
* string = Text string
|
||
* color = Packed 24-bit RGBA color of the text
|
||
* shadow = if TRUE, draws a shadow behind the text
|
||
*/
|
||
void osd_renderer_draw_text(int x, int y, const char* string, DWORD color, BOOL shadow)
|
||
{
|
||
// TODO: needs to be implemented
|
||
}
|
||
|
||
|
||
/******************************************************************/
|
||
/* Initialization and Set Up */
|
||
/******************************************************************/
|
||
|
||
/*
|
||
* void osd_renderer_set_memory(UINT8 *culling_ram_8e_ptr,
|
||
* UINT8 *culling_ram_8c_ptr,
|
||
* UINT8 *polygon_ram_ptr,
|
||
* UINT8 *texture_ram_ptr,
|
||
* UINT8 *vrom_ptr);
|
||
*
|
||
* Receives the Real3D memory regions.
|
||
*
|
||
* Currently, this function checks the Model 3 stepping and configures the
|
||
* renderer appropriately.
|
||
*
|
||
* Parameters:
|
||
* culling_ram_8e_ptr = Pointer to Real3D culling RAM at 0x8E000000.
|
||
* culling_ram_8c_ptr = Pointer to Real3D culling RAM at 0x8C000000.
|
||
* polygon_ram_ptr = Pointer to Real3D polygon RAM.
|
||
* texture_ram_ptr = Pointer to Real3D texture RAM.
|
||
* vrom_ptr = Pointer to VROM.
|
||
*/
|
||
|
||
void osd_renderer_set_memory(UINT8 *polygon_ram_ptr, UINT8 *texture_ram_ptr,
|
||
UINT8 *vrom_ptr)
|
||
{
|
||
polygon_ram = polygon_ram_ptr;
|
||
texture_ram = texture_ram_ptr;
|
||
vrom = vrom_ptr;
|
||
|
||
if (m3_config.step == 0x10)
|
||
vertex_divisor = 32768.0f; // 17.15-format vertices
|
||
else
|
||
vertex_divisor = 524288.0f; // 13.19
|
||
}
|
||
|
||
/*
|
||
* BOOL osd_gl_check_extension(CHAR *extname);
|
||
*
|
||
* Checks if an extension is supported.
|
||
*
|
||
* Parameters:
|
||
* extname = String containing the extension name.
|
||
*
|
||
* Returns:
|
||
* Non-zero if the extension is not supported.
|
||
*/
|
||
|
||
BOOL osd_gl_check_extension(CHAR *extname)
|
||
{
|
||
CHAR *p, *end;
|
||
INT extname_len, n;
|
||
|
||
p = (CHAR *) glGetString(GL_EXTENSIONS);
|
||
if (p == 0)
|
||
return 0;
|
||
extname_len = strlen(extname);
|
||
end = p + strlen(p);
|
||
|
||
while (p < end)
|
||
{
|
||
n = strcspn(p, " ");
|
||
if (extname_len == n)
|
||
{
|
||
if (strncmp(extname, p, n) == 0)
|
||
return 0;
|
||
}
|
||
p += (n + 1);
|
||
}
|
||
|
||
return 1; // not supported
|
||
}
|
||
|
||
/*
|
||
* void osd_gl_set_mode(UINT new_xres, UINT new_yres);
|
||
*
|
||
* Receives the OpenGL physical resolution and reconfigures appropriately.
|
||
* Also generates layer textures and configures some miscellaneous stuff.
|
||
*
|
||
* NOTE: Do NOT call this a second time unless you call osd_gl_unset_mode()
|
||
* first.
|
||
*
|
||
* Parameters:
|
||
* xres = X resolution in pixels.
|
||
* yres = Y resolution in pixels.
|
||
*/
|
||
|
||
void osd_gl_set_mode(UINT new_xres, UINT new_yres)
|
||
{
|
||
const GLfloat zero[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||
INT i;
|
||
|
||
osd_renderer_invalidate_textures(0, 0, 0, 0, 2048, 2048, NULL, 0);
|
||
|
||
xres = new_xres;
|
||
yres = new_yres;
|
||
xres_ratio = (float) xres / 496.0f; // Model 3 resolution is 496x384
|
||
yres_ratio = (float) yres / 384.0f;
|
||
|
||
/*
|
||
* Misc. init
|
||
*/
|
||
|
||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||
glShadeModel(GL_SMOOTH);
|
||
glClearColor(0.0, 0.0, 0.0, 0.0); // background color
|
||
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // nicer perspective view
|
||
|
||
glClearDepth(1.0);
|
||
|
||
/*
|
||
* Disable default ambient lighting and enable lighting
|
||
*/
|
||
|
||
#ifdef ENABLE_LIGHTING
|
||
glEnable(GL_LIGHTING);
|
||
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, zero);
|
||
#endif
|
||
|
||
/*
|
||
* Enable 2D texture mapping, GL_MODULATE is for the 3D scene, layer
|
||
* rendering will have to use GL_REPLACE.
|
||
*/
|
||
|
||
glEnable(GL_TEXTURE_2D);
|
||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||
|
||
/*
|
||
* Create the 2D layer textures
|
||
*/
|
||
|
||
for(i = 0; i < 4; i++)
|
||
{
|
||
SAFE_FREE(layer[i]);
|
||
layer[i] = (GLubyte *) malloc(512*512*4);
|
||
}
|
||
|
||
glGenTextures(4, layer_texture);
|
||
|
||
for (i = 0; i < 4; i++) // set up properties for each texture
|
||
{
|
||
glBindTexture(GL_TEXTURE_2D, layer_texture[i]);
|
||
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||
|
||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, layer[i]);
|
||
}
|
||
|
||
/*
|
||
* Set vertex format
|
||
*/
|
||
|
||
if (m3_config.step == 0x10)
|
||
vertex_divisor = 32768.0f; // 17.15-format vertices
|
||
else
|
||
vertex_divisor = 524288.0f; // 13.19
|
||
|
||
/*
|
||
* Setup model cache
|
||
*/
|
||
|
||
#ifdef USE_MODEL_CACHE
|
||
|
||
if(!init_model_cache())
|
||
error("init_model_cache failed!\n");
|
||
|
||
glVertexPointer(3, GL_FLOAT, 0, model_vertex_array);
|
||
glNormalPointer(GL_FLOAT, 0, model_normal_array);
|
||
glColorPointer(4, GL_FLOAT, 0, model_color_array);
|
||
glTexCoordPointer(2, GL_FLOAT, 0, model_texcoord_array);
|
||
|
||
#endif
|
||
}
|
||
|
||
/*
|
||
* void osd_gl_unset_mode(void);
|
||
*
|
||
* "Unsets" the current mode -- frees textures. osd_gl_set_mode() must be
|
||
* called before the renderer is used again.
|
||
*/
|
||
|
||
void osd_gl_unset_mode(void)
|
||
{
|
||
osd_renderer_invalidate_textures(0, 0, 0, 0, 2048, 2048, NULL, 0);
|
||
glDeleteTextures(4, layer_texture);
|
||
}
|
||
|
||
UINT32 osd_renderer_get_features(void)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
// Not supported but the interface requires them
|
||
void osd_renderer_get_palette_buffer(UINT32 **buffer, int *width, int *pitch) { };
|
||
void osd_renderer_free_palette_buffer(void) { };
|
||
void osd_renderer_get_priority_buffer(int layer_num, UINT8 **buffer, int *pitch) { };
|
||
void osd_renderer_free_priority_buffer(int layer_num) { }; |