Removing legacy 3D engine files that were already moved to Graphics/Legacy3D

This commit is contained in:
Bart Trzynadlowski 2016-03-21 23:12:17 +00:00
parent 34301e97e1
commit d61b01ab6f
6 changed files with 0 additions and 4014 deletions

View file

@ -1,992 +0,0 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel 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 Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Models.cpp
*
* Model parsing, caching, and drawing.
*
* TO-DO List:
* -----------
* - If vertex normals aren't offset from polygon normals, would that improve
* specular lighting?
* - Check to see if vertices in LA Machineguns and Dirt Devils contain color
* values rather than normals.
* - More should be predecoded into the polygon structures, so that things like
* texture base coordinates are not re-decoded in two different places!
*/
#include <cmath>
#include <cstring>
#include "Supermodel.h"
/******************************************************************************
Definitions and Constants
******************************************************************************/
/*
* VBO Vertex Layout
*
* All vertex information is stored in an array of GLfloats. Offset and size
* information is defined here for now.
*/
#define VBO_VERTEX_OFFSET_X 0 // vertex X
#define VBO_VERTEX_OFFSET_Y 1 // vertex Y
#define VBO_VERTEX_OFFSET_Z 2 // vertex Z
#define VBO_VERTEX_OFFSET_NX 3 // normal X
#define VBO_VERTEX_OFFSET_NY 4 // normal Y
#define VBO_VERTEX_OFFSET_NZ 5 // normal Z
#define VBO_VERTEX_OFFSET_R 6 // color (untextured polys) and material (textured polys) R
#define VBO_VERTEX_OFFSET_G 7 // color and material G
#define VBO_VERTEX_OFFSET_B 8 // color and material B
#define VBO_VERTEX_OFFSET_TRANSLUCENCE 9 // translucence level (0.0 fully transparent, 1.0 opaque)
#define VBO_VERTEX_OFFSET_LIGHTENABLE 10 // lighting enabled (0.0 luminous, 1.0 light enabled)
#define VBO_VERTEX_OFFSET_SHININESS 11 // shininess (if negative, disables specular lighting)
#define VBO_VERTEX_OFFSET_FOGINTENSITY 12 // fog intensity (0.0 no fog applied, 1.0 all fog applied)
#define VBO_VERTEX_OFFSET_U 13 // texture U coordinate (in texels, relative to sub-texture)
#define VBO_VERTEX_OFFSET_V 14 // texture V coordinate
#define VBO_VERTEX_OFFSET_TEXTURE_X 15 // sub-texture parameters, X (position in overall texture map, in texels)
#define VBO_VERTEX_OFFSET_TEXTURE_Y 16 // "" Y ""
#define VBO_VERTEX_OFFSET_TEXTURE_W 17 // sub-texture parameters, width of texture in texels
#define VBO_VERTEX_OFFSET_TEXTURE_H 18 // "" height of texture in texels
#define VBO_VERTEX_OFFSET_TEXPARAMS_EN 19 // texture parameter: ==1 texturing enabled, ==0 disabled (per-polygon)
#define VBO_VERTEX_OFFSET_TEXPARAMS_TRANS 20 // texture parameter: >=0 use transparency bit, <0 no transparency (per-polygon)
#define VBO_VERTEX_OFFSET_TEXPARAMS_UWRAP 21 // texture parameters: U wrap mode: ==1 mirrored repeat, ==0 normal repeat
#define VBO_VERTEX_OFFSET_TEXPARAMS_VWRAP 22 // "" V wrap mode ""
#define VBO_VERTEX_OFFSET_TEXFORMAT 23 // texture format 0-7 (also ==0 indicates contour texture - see also texParams.trans)
#define VBO_VERTEX_OFFSET_TEXMAP 24 // texture map number
#define VBO_VERTEX_SIZE 25 // total size (may include padding for alignment)
/******************************************************************************
Math Routines
******************************************************************************/
// Macro to generate column-major (OpenGL) index from y,x subscripts
#define CMINDEX(y,x) (x*4+y)
static void CrossProd(GLfloat out[3], GLfloat a[3], GLfloat b[3])
{
out[0] = a[1]*b[2]-a[2]*b[1];
out[1] = a[2]*b[0]-a[0]*b[2];
out[2] = a[0]*b[1]-a[1]*b[0];
}
// 3x3 matrix used (upper-left of m[])
static void MultMat3Vec3(GLfloat out[3], GLfloat m[4*4], GLfloat v[3])
{
out[0] = m[CMINDEX(0,0)]*v[0]+m[CMINDEX(0,1)]*v[1]+m[CMINDEX(0,2)]*v[2];
out[1] = m[CMINDEX(1,0)]*v[0]+m[CMINDEX(1,1)]*v[1]+m[CMINDEX(1,2)]*v[2];
out[2] = m[CMINDEX(2,0)]*v[0]+m[CMINDEX(2,1)]*v[1]+m[CMINDEX(2,2)]*v[2];
}
static GLfloat Sign(GLfloat x)
{
if (x > 0.0f)
return 1.0f;
else if (x < 0.0f)
return -1.0f;
return 0.0f;
}
// Inverts and transposes a 3x3 matrix (upper-left of the 4x4), returning a
// 4x4 matrix with the extra components undefined (do not use them!)
static void InvertTransposeMat3(GLfloat out[4*4], GLfloat m[4*4])
{
GLfloat invDet;
GLfloat a00 = m[CMINDEX(0,0)], a01 = m[CMINDEX(0,1)], a02 = m[CMINDEX(0,2)];
GLfloat a10 = m[CMINDEX(1,0)], a11 = m[CMINDEX(1,1)], a12 = m[CMINDEX(1,2)];
GLfloat a20 = m[CMINDEX(2,0)], a21 = m[CMINDEX(2,1)], a22 = m[CMINDEX(2,2)];
invDet = 1.0f/(a00*(a22*a11-a21*a12)-a10*(a22*a01-a21*a02)+a20*(a12*a01-a11*a02));
out[CMINDEX(0,0)] = invDet*(a22*a11-a21*a12); out[CMINDEX(1,0)] = invDet*(-(a22*a01-a21*a02)); out[CMINDEX(2,0)] = invDet*(a12*a01-a11*a02);
out[CMINDEX(0,1)] = invDet*(-(a22*a10-a20*a12)); out[CMINDEX(1,1)] = invDet*(a22*a00-a20*a02); out[CMINDEX(2,1)] = invDet*(-(a12*a00-a10*a02));
out[CMINDEX(0,2)] = invDet*(a21*a10-a20*a11); out[CMINDEX(1,2)] = invDet*(-(a21*a00-a20*a01)); out[CMINDEX(2,2)] = invDet*(a11*a00-a10*a01);
}
static void PrintMatrix(GLfloat m[4*4])
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
printf("%g\t", m[CMINDEX(i,j)]);
printf("\n");
}
}
/******************************************************************************
Display Lists
Every instance of a model encountered in the scene database during rendering
is stored in the display list along with its current transformation matrices
and other state information. Display lists are bound to model caches for
performance: only one VBO has to be bound for an entire display list.
Binding display lists to model caches may cause priority problems among
alpha polygons. Therefore, it may be necessary in the future to decouple them.
******************************************************************************/
// Draws the display list
void CRender3D::DrawDisplayList(ModelCache *Cache, POLY_STATE state)
{
DisplayList *D;
// Bind and activate VBO (pointers activate currently bound VBO)
glBindBuffer(GL_ARRAY_BUFFER, Cache->vboID);
glVertexPointer(3, GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_X*sizeof(GLfloat)));
glNormalPointer(GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_NX*sizeof(GLfloat)));
glTexCoordPointer(2, GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_U*sizeof(GLfloat)));
glColorPointer(3, GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_R*sizeof(GLfloat)));
if (subTextureLoc != -1) glVertexAttribPointer(subTextureLoc, 4, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXTURE_X*sizeof(GLfloat)));
if (texParamsLoc != -1) glVertexAttribPointer(texParamsLoc, 4, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXPARAMS_EN*sizeof(GLfloat)));
if (texFormatLoc != -1) glVertexAttribPointer(texFormatLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXFORMAT*sizeof(GLfloat)));
if (texMapLoc != -1) glVertexAttribPointer(texMapLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXMAP*sizeof(GLfloat)));
if (transLevelLoc != -1) glVertexAttribPointer(transLevelLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TRANSLUCENCE*sizeof(GLfloat)));
if (lightEnableLoc != -1) glVertexAttribPointer(lightEnableLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_LIGHTENABLE*sizeof(GLfloat)));
if (shininessLoc != -1) glVertexAttribPointer(shininessLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_SHININESS*sizeof(GLfloat)));
if (fogIntensityLoc != -1) glVertexAttribPointer(fogIntensityLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_FOGINTENSITY*sizeof(GLfloat)));
// Set up state
if (state == POLY_STATE_ALPHA)
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
else
{
glDisable(GL_BLEND);
}
// Draw if there are items in the list
D = Cache->ListHead[state];
while (D != NULL)
{
if (D->isViewport)
{
if (D->next != NULL) // if nothing follows, no point in doing this
{
if (!D->next->isViewport)
{
if (lightingLoc != -1) glUniform3fv(lightingLoc, 2, D->Data.Viewport.lightingParams);
if (projectionMatrixLoc != -1) glUniformMatrix4fv(projectionMatrixLoc, 1, GL_FALSE, D->Data.Viewport.projectionMatrix);
glFogf(GL_FOG_DENSITY, D->Data.Viewport.fogParams[3]);
glFogf(GL_FOG_START, D->Data.Viewport.fogParams[4]);
glFogfv(GL_FOG_COLOR, &(D->Data.Viewport.fogParams[0]));
if (spotEllipseLoc != -1) glUniform4fv(spotEllipseLoc, 1, D->Data.Viewport.spotEllipse);
if (spotRangeLoc != -1) glUniform2fv(spotRangeLoc, 1, D->Data.Viewport.spotRange);
if (spotColorLoc != -1) glUniform3fv(spotColorLoc, 1, D->Data.Viewport.spotColor);
glViewport(D->Data.Viewport.x, D->Data.Viewport.y, D->Data.Viewport.width, D->Data.Viewport.height);
}
}
}
else
{
GLint frontFace;
if (D->Data.Model.frontFace == -GL_CW) // no backface culling (all normals have lost their Z component)
glDisable(GL_CULL_FACE);
else // use appropriate winding convention
{
glGetIntegerv(GL_FRONT_FACE, &frontFace);
if (frontFace != D->Data.Model.frontFace)
glFrontFace(D->Data.Model.frontFace);
}
if (modelViewMatrixLoc != -1)
glUniformMatrix4fv(modelViewMatrixLoc, 1, GL_FALSE, D->Data.Model.modelViewMatrix);
glDrawArrays(GL_TRIANGLES, D->Data.Model.index, D->Data.Model.numVerts);
if (D->Data.Model.frontFace == -GL_CW)
glEnable(GL_CULL_FACE);
}
D = D->next;
}
}
// Appends an instance of a model or viewport to the display list, copying over the required state information
bool CRender3D::AppendDisplayList(ModelCache *Cache, bool isViewport, const struct VBORef *Model)
{
int lm, i;
if ((Cache->listSize+2) > Cache->maxListSize) // a model may have 2 states (viewports are added to both display lists)
return FAIL;
//return ErrorLog("Display list is full.");
// Insert states into the display list
for (i = 0; i < 2; i++)
{
if (isViewport)
{
// Get index for new display list item and advance to next one
lm = Cache->listSize++;
// Viewport parameters
Cache->List[lm].Data.Viewport.x = viewportX;
Cache->List[lm].Data.Viewport.y = viewportY;
Cache->List[lm].Data.Viewport.width = viewportWidth;
Cache->List[lm].Data.Viewport.height = viewportHeight;
// Copy over lighting and fog state
memcpy(Cache->List[lm].Data.Viewport.lightingParams, lightingParams, sizeof(lightingParams));
memcpy(Cache->List[lm].Data.Viewport.fogParams, fogParams, sizeof(fogParams));
memcpy(Cache->List[lm].Data.Viewport.spotEllipse, spotEllipse, sizeof(spotEllipse));
memcpy(Cache->List[lm].Data.Viewport.spotRange, spotRange, sizeof(spotRange));
memcpy(Cache->List[lm].Data.Viewport.spotColor, spotColor, sizeof(spotColor));
// Copy projection matrix
glGetFloatv(GL_PROJECTION_MATRIX, Cache->List[lm].Data.Viewport.projectionMatrix);
}
else if (Model->numVerts[i] > 0) // vertices exist for this state
{
// Get index for new display list item and advance to next one
lm = Cache->listSize++;
// Point to VBO for current model and state
Cache->List[lm].Data.Model.index = Model->index[i];
Cache->List[lm].Data.Model.numVerts = Model->numVerts[i];
// Copy modelview matrix
glGetFloatv(GL_MODELVIEW_MATRIX, Cache->List[lm].Data.Model.modelViewMatrix);
/*
* Determining if winding was reversed (but not polygon normal):
*
* Real3D performs backface culling in view space based on the
* polygon normal unlike OpenGL, which uses the computed normal
* from the edges (in screen space) of the polygon. Consequently,
* it is possible to create a matrix that mirrors an axis without
* rotating the normal, which in turn flips the polygon winding and
* makes it invisible in OpenGL but not on Real3D, because the
* normal is still facing the right way.
*
* To detect such a situation, we create a fictitious polygon with
* edges X = [1 0 0] and Y = [0 1 0], with normal Z = [0 0 1]. We
* rotate the edges by the matrix then compute a normal P, which is
* what OpenGL would use for culling. We transform the normal Z by
* the normal matrix (normals are special and must be multiplied by
* Transpose(Inverse(M)), not M). If the Z components of P and the
* transformed Z vector have opposite signs, the OpenGL winding
* mode must be switched in order to draw correctly. The X axis may
* have been flipped, for example, changing the winding mode while
* leaving the polygon normal unaffected. OpenGL would erroneously
* discard these polygons, so we flip the winding convention,
* ensuring they are drawn correctly.
*
* We have to adjust the Z vector (fictitious normal) by the sign
* of the Z axis specified by the coordinate system matrix (#0).
* This is described further in InsertPolygon(), where the vertices
* are ordered in clockwise fashion.
*/
GLfloat x[3] = { 1.0f, 0.0f, 0.0f };
GLfloat y[3] = { 0.0f, 1.0f, 0.0f };
GLfloat z[3] = { 0.0f, 0.0f, -1.0f*matrixBasePtr[0x5] };
GLfloat m[4*4];
GLfloat xT[3], yT[3], zT[3], pT[3];
InvertTransposeMat3(m,Cache->List[lm].Data.Model.modelViewMatrix);
MultMat3Vec3(xT,Cache->List[lm].Data.Model.modelViewMatrix,x);
MultMat3Vec3(yT,Cache->List[lm].Data.Model.modelViewMatrix,y);
MultMat3Vec3(zT,m,z);
CrossProd(pT,xT,yT);
float s = Sign(zT[2]*pT[2]);
if (s < 0.0f)
Cache->List[lm].Data.Model.frontFace = GL_CCW;
else if (s > 0.0f)
Cache->List[lm].Data.Model.frontFace = GL_CW;
else
Cache->List[lm].Data.Model.frontFace = -GL_CW;
}
else // nothing to do, continue loop
continue;
// Update list pointers and set list node type
Cache->List[lm].isViewport = isViewport;
Cache->List[lm].next = NULL; // current end of list
if (Cache->ListHead[i] == NULL)
{
Cache->ListHead[i] = &(Cache->List[lm]);
Cache->ListTail[i] = Cache->ListHead[i];
}
else
{
Cache->ListTail[i]->next = &(Cache->List[lm]);
Cache->ListTail[i] = &(Cache->List[lm]);
}
}
return OKAY;
}
// Clears the display list in preparation for a new frame
void CRender3D::ClearDisplayList(ModelCache *Cache)
{
Cache->listSize = 0;
for (int i = 0; i < 2; i++)
{
Cache->ListHead[i] = NULL;
Cache->ListTail[i] = NULL;
}
}
/******************************************************************************
Model Caching
Note that as vertices are inserted into the appropriate local vertex buffer
(sorted by polygon state -- alpha and normal), the VBO index is advanced to
reserve space and does not correspond to the actual position of each vertex.
Vertices are copied in batches sorted by state when the model is complete.
******************************************************************************/
// Inserts a vertex into the local vertex buffer, incrementing both the local and VBO pointers. The normal is scaled by normFlip.
void CRender3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P, float normFlip)
{
GLfloat r, g, b;
GLfloat translucence, fogIntensity, texWidth, texHeight, texBaseX, texBaseY, contourProcessing;
unsigned baseIdx, texFormat, texEnable, lightEnable, modulate, colorIdx;
TexSheet *texSheet;
int s, texPage, shininess;
// Texture selection
texEnable = P->header[6]&0x04000000;
texFormat = (P->header[6]>>7)&7;
texWidth = (GLfloat) (32<<((P->header[3]>>3)&7));
texHeight = (GLfloat) (32<<((P->header[3]>>0)&7));
texPage = (P->header[4]&0x40) ? 1024 : 0; // treat texture page as Y coordinate
texSheet = fmtToTexSheet[texFormat]; // get X&Y offset of texture sheet within texture map
texBaseX = (GLfloat) (texSheet->xOffset + (((32*(((P->header[4]&0x1F)<<1)|((P->header[5]>>7)&1))) + (int)texOffsetXY[0])&2047));
texBaseY = (GLfloat) (texSheet->yOffset + (((32*(P->header[5]&0x1F)+texPage) + (int)texOffsetXY[1])&2047));
/*
* Lighting and Color Modulation:
*
* It appears that there is a modulate bit which causes the polygon color
* to be multiplied by texel colors. However, if polygons are luminous,
* this appears to be disabled (not quite correct yet, though).
*/
lightEnable = !(P->header[6]&0x00010000);
//modulate = !(P->header[4]&0x80);
modulate = P->header[3]&0x80; // seems to work better
// Material color
if ((P->header[1]&2) == 0)
{
colorIdx = ((P->header[4]>>20)&0x7FF) - 0;
b = (GLfloat) (polyRAM[0x400+colorIdx]&0xFF) * (1.0f/255.0f);
g = (GLfloat) ((polyRAM[0x400+colorIdx]>>8)&0xFF) * (1.0f/255.0f);
r = (GLfloat) ((polyRAM[0x400+colorIdx]>>16)&0xFF) * (1.0f/255.0f);
}
else
{
// Colors are 8-bit (almost certainly true, see Star Wars)
r = (GLfloat) (P->header[4]>>24) * (1.0f/255.0f);
g = (GLfloat) ((P->header[4]>>16)&0xFF) * (1.0f/255.0f);
b = (GLfloat) ((P->header[4]>>8)&0xFF) * (1.0f/255.0f);
}
// Determine modulation settings
if (texEnable)
{
//if (!lightEnable|| !modulate)
if (!modulate)
r = g = b = 1.0f;
}
// Specular shininess
shininess = (P->header[0]>>26)&0x3F;
//shininess = (P->header[0]>>28)&0xF;
//if (shininess)
// printf("%X\n", shininess);
if (!(P->header[0]&0x80) || (shininess == 0)) // bit 0x80 seems to enable specular lighting
shininess = -1; // disable
#if 0
if (texFormat==5)//texFormat==6||texFormat==2)
{
//printf("%03X\n", P->header[4]>>8);
//texEnable=0;
g=b=1.0;
r=1.0f;
}
#endif
#if 0
int testWord = 0;
int testBit = 7;
//if ((P->header[testWord]&(1<<testBit)))
if (((P->header[0]>>24) & 0x3) != 0)
//if (!((P->header[0]>>26) & 0x3F) && (P->header[0]&0x80))
{
texEnable = 0;
r=b=0;
g=1.0f;
g = ((P->header[0]>>26)&0x3F) * (1.0f/64.0f);
//if (!lightEnable)
// b=1.0f;
lightEnable=0;
}
#endif
// Determine whether polygon is translucent
translucence = (GLfloat) ((P->header[6]>>18)&0x1F) * (1.0f/31.0f);
if ((P->header[6]&0x00800000)) // if set, polygon is opaque
translucence = 1.0f;
// Fog intensity (for luminous polygons)
fogIntensity = (GLfloat) ((P->header[6]>>11)&0x1F) * (1.0f/31.0f);
if (!(P->header[6]&0x00010000)) // if not luminous, always use full fog intensity
fogIntensity = 1.0f;
/*
* Contour processing. Any alpha value sufficiently close to 0 seems to
* cause pixels to be discarded entirely on Model 3 (no modification of the
* depth buffer). Strictly speaking, only T1RGB5 format textures are
* "contour textures" (in Real3D lingo), we enable contour processing for
* alpha blended texture formats as well in order to discard fully
* transparent pixels.
*/
if ((P->header[6]&0x80000000) || (texFormat==7) || // contour processing enabled or RGBA4 texture
((texFormat==1) && (P->header[6]&2)) || // A4L4 interleaved (these formats are not being interpreted correctly, see Scud Race clock tower)
((texFormat==3) && (P->header[6]&4))) // A4L4 interleaved
contourProcessing = 1.0f;
else
contourProcessing = -1.0f;
// Store to local vertex buffer
s = P->state;
baseIdx = Cache->curVertIdx[s]*VBO_VERTEX_SIZE;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_X] = V->x;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_Y] = V->y;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_Z] = V->z;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_R] = r;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_G] = g;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_B] = b;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TRANSLUCENCE] = translucence;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_LIGHTENABLE] = lightEnable ? 1.0f : 0.0f;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_SHININESS] = (GLfloat) shininess;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_FOGINTENSITY] = fogIntensity;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_NX] = V->n[0]*normFlip;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_NY] = V->n[1]*normFlip;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_NZ] = V->n[2]*normFlip;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_U] = V->u;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_V] = V->v;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXTURE_X] = texBaseX;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXTURE_Y] = texBaseY;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXTURE_W] = texWidth;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXTURE_H] = texHeight;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXPARAMS_EN] = texEnable ? 1.0f : 0.0f;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXPARAMS_TRANS] = contourProcessing;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXPARAMS_UWRAP] = (P->header[2]&2) ? 1.0f : 0.0f;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXPARAMS_VWRAP] = (P->header[2]&1) ? 1.0f : 0.0f;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXFORMAT] = (float)texFormat;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXMAP] = (float)texSheet->mapNum;
Cache->curVertIdx[s]++;
Cache->vboCurOffset += VBO_VERTEX_SIZE*sizeof(GLfloat);
}
bool CRender3D::InsertPolygon(ModelCache *Cache, const Poly *P)
{
GLfloat n[3], v1[3], v2[3], normZFlip;
int i;
bool doubleSided;
// Bounds testing: up to 12 triangles will be inserted (worst case: double sided quad is 6 triangles)
if ((Cache->curVertIdx[P->state]+6*2) >= Cache->maxVertIdx)
return ErrorLocalVertexOverflow(); // local buffers are not expected to overflow
if ((Cache->vboCurOffset+6*2*VBO_VERTEX_SIZE*sizeof(GLfloat)) >= Cache->vboMaxOffset)
return FAIL; // this just indicates we may need to re-cache
// Is the polygon double sided?
doubleSided = (P->header[1]&0x10) ? true : false;
/*
* Determine polygon winding by taking cross product of vectors formed from
* 3 polygon vertices (the middle one being the origin). In reality, back-
* face culling is determined by the polygon normal and two-sided polygons
* exist. This is just a temporary hack.
*
* If the cross product points the same way as the normal, the winding is
* clockwise and can be kept, otherwise it must be reversed.
*
* NOTE: This assumes that the Model 3 base coordinate system's Z axis
* (into the screen) is -1, like OpenGL. For some games (eg., Lost World),
* this is not the case. Assuming games consistently use the same type of
* coordinate system matrix, it seems that inverting the whole dot product
* when Z is positive helps. I don't understand exactly why... but it has
* to do with using the correct Z convention to identify a vector pointing
* toward or away from the screen.
*/
v1[0] = P->Vert[0].x-P->Vert[1].x;
v1[1] = P->Vert[0].y-P->Vert[1].y;
v1[2] = P->Vert[0].z-P->Vert[1].z;
v2[0] = P->Vert[2].x-P->Vert[1].x;
v2[1] = P->Vert[2].y-P->Vert[1].y;
v2[2] = P->Vert[2].z-P->Vert[1].z;
CrossProd(n,v1,v2);
normZFlip = -1.0f*matrixBasePtr[0x5]; // coordinate system m13 component
if (normZFlip*(n[0]*P->n[0]+n[1]*P->n[1]+n[2]*P->n[2]) >= 0.0) // clockwise winding confirmed
{
// Store the first triangle
for (i = 0; i < 3; i++)
{
InsertVertex(Cache, &(P->Vert[i]), P, 1.0f);
}
if (doubleSided) // store backside as counter-clockwise
{
for (i = 2; i >=0; i--)
{
InsertVertex(Cache, &(P->Vert[i]), P, -1.0f);
}
}
// If quad, second triangle will just be vertices 1, 3, 4
if (P->numVerts == 4)
{
InsertVertex(Cache, &(P->Vert[0]), P, 1.0f);
InsertVertex(Cache, &(P->Vert[2]), P, 1.0f);
InsertVertex(Cache, &(P->Vert[3]), P, 1.0f);
if (doubleSided)
{
InsertVertex(Cache, &(P->Vert[0]), P, -1.0f);
InsertVertex(Cache, &(P->Vert[3]), P, -1.0f);
InsertVertex(Cache, &(P->Vert[2]), P, -1.0f);
}
}
}
else // counterclockwise winding, reverse it
{
for (i = 2; i >=0; i--)
{
InsertVertex(Cache, &(P->Vert[i]), P, 1.0f);
}
if (doubleSided) // store backside as clockwise
{
for (i = 0; i < 3; i++)
{
InsertVertex(Cache, &(P->Vert[i]), P, -1.0f);
}
}
if (P->numVerts == 4)
{
InsertVertex(Cache, &(P->Vert[0]), P, 1.0f);
InsertVertex(Cache, &(P->Vert[3]), P, 1.0f);
InsertVertex(Cache, &(P->Vert[2]), P, 1.0f);
if (doubleSided)
{
InsertVertex(Cache, &(P->Vert[0]), P, -1.0f);
InsertVertex(Cache, &(P->Vert[2]), P, -1.0f);
InsertVertex(Cache, &(P->Vert[3]), P, -1.0f);
}
}
}
return OKAY;
}
// Begins caching a new model by resetting to the start of the local vertex buffer
struct VBORef *CRender3D::BeginModel(ModelCache *Cache)
{
struct VBORef *Model;
unsigned m = Cache->numModels;
// Determine whether we've exceeded the model cache limits (caller will have to recache)
if (m >= Cache->maxModels)
{
//ErrorLog("Too many %s models.", Cache->dynamic?"dynamic":"static");
return NULL;
}
Model = &(Cache->Models[m]);
// Reset to the beginning of the local vertex buffer
for (int i = 0; i < 2; i++)
Cache->curVertIdx[i] = 0;
// Clear the VBO reference to 0 and clear texture references
Model->Clear();
// Record starting index of first opaque polygon in VBO (alpha poly index will be re-set in EndModel())
Model->index[POLY_STATE_NORMAL] = Cache->vboCurOffset/(VBO_VERTEX_SIZE*sizeof(GLfloat));
Model->index[POLY_STATE_ALPHA] = Model->index[POLY_STATE_NORMAL];
return Model;
}
// Uploads all vertices from the local vertex buffer to the VBO, sets up the VBO reference, updates the LUT
void CRender3D::EndModel(ModelCache *Cache, struct VBORef *Model, int lutIdx, UINT16 texOffset)
{
int m = Cache->numModels++;
// Record the number of vertices, completing the VBORef
for (int i = 0; i < 2; i++)
Model->numVerts[i] = Cache->curVertIdx[i];
// First alpha polygon immediately follows the normal polygons
Model->index[POLY_STATE_ALPHA] = Model->index[POLY_STATE_NORMAL] + Model->numVerts[POLY_STATE_NORMAL];
// Upload from local vertex buffer to real VBO
glBindBuffer(GL_ARRAY_BUFFER, Cache->vboID);
if (Model->numVerts[POLY_STATE_NORMAL] > 0)
glBufferSubData(GL_ARRAY_BUFFER, Model->index[POLY_STATE_NORMAL]*VBO_VERTEX_SIZE*sizeof(GLfloat), Cache->curVertIdx[POLY_STATE_NORMAL]*VBO_VERTEX_SIZE*sizeof(GLfloat), Cache->verts[POLY_STATE_NORMAL]);
if (Model->numVerts[POLY_STATE_ALPHA] > 0)
glBufferSubData(GL_ARRAY_BUFFER, Model->index[POLY_STATE_ALPHA]*VBO_VERTEX_SIZE*sizeof(GLfloat), Cache->curVertIdx[POLY_STATE_ALPHA]*VBO_VERTEX_SIZE*sizeof(GLfloat), Cache->verts[POLY_STATE_ALPHA]);
// Record LUT index in the model VBORef
Model->lutIdx = lutIdx;
// Texture offset of this model state
Model->texOffset = texOffset;
// Update the LUT and link up to any existing model that already exists here
if (Cache->lut[lutIdx] >= 0) // another texture offset state already cached
Model->nextTexOffset = &(Cache->Models[Cache->lut[lutIdx]]);
Cache->lut[lutIdx] = m;
}
/*
* CacheModel():
*
* Decodes and caches a complete model. Returns NULL if any sort of overflow in
* the cache occurred. In this case, the model cache should be cleared before
* being used again because an incomplete model will be stored, wasting vertex
* buffer space.
*
* A pointer to the VBO reference for the cached model is returned when
* successful.
*/
struct VBORef *CRender3D::CacheModel(ModelCache *Cache, int lutIdx, UINT16 texOffset, const UINT32 *data)
{
Vertex Prev[4]; // previous vertices
int numPolys = 0;
bool done = false;
// Sega Rally 2 bad models
//if (lutIdx == 0x27a1 || lutIdx == 0x21e0)
// return FAIL;
if (data == NULL)
return NULL;
// Start constructing a new model
struct VBORef *Model = BeginModel(Cache);
if (NULL == Model)
return NULL; // too many models!
// Cache all polygons
while (!done)
{
Poly P; // current polygon
GLfloat mag;
GLfloat uvScale;
int texEnable, texFormat, texWidth, texHeight, texPage, texBaseX, texBaseY;
unsigned i, j, vmask;
UINT32 ix, iy, iz, it;
// Set current header pointer (header is 7 words)
P.header = data;
data += 7; // data will now point to first vertex
if (P.header[6]==0)// || P.header[0]==0)
break;
// Obtain basic polygon parameters
done = P.header[1]&4; // last polygon?
P.numVerts = (P.header[0]&0x40)?4:3;
// Texture data
texEnable = P.header[6]&0x04000000;
texFormat = (P.header[6]>>7)&7;
texWidth = (32<<((P.header[3]>>3)&7));
texHeight = (32<<((P.header[3]>>0)&7));
texPage = (P.header[4]&0x40) ? 1024 : 0; // treat texture page as Y coordinate
texBaseX = (32*(((P.header[4]&0x1F)<<1)|((P.header[5]>>7)&1))) + (int)texOffsetXY[0];
texBaseY = (32*(P.header[5]&0x1F)+texPage) + (int)texOffsetXY[1];
texBaseX &= 2047;
texBaseY &= 2047;
uvScale = (P.header[1]&0x40)?1.0f:(1.0f/8.0f);
// Determine whether this is an alpha polygon (TODO: when testing textures, test if texturing enabled? Might not matter)
if (((P.header[6]&0x00800000)==0) || // translucent polygon
(texFormat==7) || // RGBA4 texture
(texFormat==4)) // A4L4 texture
P.state = POLY_STATE_ALPHA;
else
P.state = POLY_STATE_NORMAL;
if (texFormat==1) // A4L4 interleaved
{
if ((P.header[6]&2))
P.state = POLY_STATE_ALPHA;
else
P.state = POLY_STATE_NORMAL;
}
if (texFormat==3) // A4L4 interleaved
{
if ((P.header[6]&4))
P.state = POLY_STATE_ALPHA;
else
P.state = POLY_STATE_NORMAL;
}
// Decode the texture
if (texEnable)
{
// If model cache is static, record texture reference in model cache entry for later decoding.
// If cache is dynamic, or if it's not possible to record the texture reference (due to lack of
// memory) then decode the texture now.
if (Cache->dynamic || !Model->texRefs.AddRef(texFormat, texBaseX, texBaseY, texWidth, texHeight))
DecodeTexture(texFormat, texBaseX, texBaseY, texWidth, texHeight);
}
// Polygon normal is in upper 24 bits: sign + 1.22 fixed point
P.n[0] = (GLfloat) (((INT32)P.header[1])>>8) * (1.0f/4194304.0f);
P.n[1] = (GLfloat) (((INT32)P.header[2])>>8) * (1.0f/4194304.0f);
P.n[2] = (GLfloat) (((INT32)P.header[3])>>8) * (1.0f/4194304.0f);
// Fetch reused vertices according to bitfield, then new verts
i = 0;
j = 0;
vmask = 1;
for (i = 0; i < 4; i++) // up to 4 reused vertices
{
if ((P.header[0x00]&vmask))
{
P.Vert[j] = Prev[i];
++j;
}
vmask <<= 1;
}
for (; j < P.numVerts; j++) // remaining vertices are new and defined here
{
// Fetch vertices
ix = data[0];
iy = data[1];
iz = data[2];
it = data[3];
/*
// Check for bad vertices (Sega Rally 2)
if (((ix>>28)==7) || ((iy>>28)==7) || ((iz>>28)==7))
{
//printf("%X ix=%08X, iy=%08X, iz=%08X\n", lutIdx, ix, iy, iz);
goto StopDecoding;
}
*/
// Decode vertices
P.Vert[j].x = (GLfloat) (((INT32)ix)>>8) * vertexFactor;
P.Vert[j].y = (GLfloat) (((INT32)iy)>>8) * vertexFactor;
P.Vert[j].z = (GLfloat) (((INT32)iz)>>8) * vertexFactor;
P.Vert[j].n[0] = P.n[0]+(GLfloat)(INT8)(ix&0xFF); // vertex normals are offset from polygon normal
P.Vert[j].n[1] = P.n[1]+(GLfloat)(INT8)(iy&0xFF);
P.Vert[j].n[2] = P.n[2]+(GLfloat)(INT8)(iz&0xFF);
P.Vert[j].u = (GLfloat) ((UINT16)(it>>16)) * uvScale; // TO-DO: might these be signed?
P.Vert[j].v = (GLfloat) ((UINT16)(it&0xFFFF)) * uvScale;
data += 4;
// Normalize the vertex normal
mag = sqrt(P.Vert[j].n[0]*P.Vert[j].n[0]+P.Vert[j].n[1]*P.Vert[j].n[1]+P.Vert[j].n[2]*P.Vert[j].n[2]);
P.Vert[j].n[0] /= mag;
P.Vert[j].n[1] /= mag;
P.Vert[j].n[2] /= mag;
}
// Copy current vertices into previous vertex array
for (i = 0; i < 4; i++)
Prev[i] = P.Vert[i];
// Copy this polygon into the model buffer
if (OKAY != InsertPolygon(Cache,&P))
return NULL;
++numPolys;
}
// Finish model and enter it into the LUT
EndModel(Cache,Model,lutIdx,texOffset);
return Model;
}
/******************************************************************************
Cache Management
******************************************************************************/
/*
* Look up a model. Use this to determine if a model needs to be cached
* (returns NULL if so).
*/
struct VBORef *CRender3D::LookUpModel(ModelCache *Cache, int lutIdx, UINT16 texOffset)
{
int m = Cache->lut[lutIdx];
// Has any state associated with this model LUT index been cached at all?
if (m < 0)
return NULL;
// Has the specified texture offset been cached?
for (struct VBORef *Model = &(Cache->Models[m]); Model != NULL; Model = Model->nextTexOffset)
{
if (Model->texOffset == texOffset)
return Model;
}
return NULL; // no match found, we must cache this new model state
}
// Discard all models in the cache and the display list
void CRender3D::ClearModelCache(ModelCache *Cache)
{
Cache->vboCurOffset = 0;
for (int i = 0; i < 2; i++)
Cache->curVertIdx[i] = 0;
for (int i = 0; i < Cache->numModels; i++)
Cache->lut[Cache->Models[i].lutIdx] = -1;
Cache->numModels = 0;
ClearDisplayList(Cache);
}
bool CRender3D::CreateModelCache(ModelCache *Cache, unsigned vboMaxVerts,
unsigned localMaxVerts, unsigned maxNumModels, unsigned numLUTEntries,
unsigned displayListSize, bool isDynamic)
{
unsigned i;
int vboBytes, localBytes;
bool success;
Cache->dynamic = isDynamic;
/*
* VBO allocation:
*
* Progressively smaller VBOs, in steps of localMaxVerts are allocated
* until successful. If the size dips below localMaxVerts, localMaxVerts is
* attempted as the final try.
*/
glGetError(); // clear error flag
glGenBuffers(1, &(Cache->vboID));
glBindBuffer(GL_ARRAY_BUFFER, Cache->vboID);
vboBytes = vboMaxVerts*VBO_VERTEX_SIZE*sizeof(GLfloat);
localBytes = localMaxVerts*VBO_VERTEX_SIZE*sizeof(GLfloat);
// Try allocating until size is
success = false;
while (vboBytes >= localBytes)
{
glBufferData(GL_ARRAY_BUFFER, vboBytes, 0, isDynamic?GL_STREAM_DRAW:GL_STATIC_DRAW);
if (glGetError() == GL_NO_ERROR)
{
success = true;
break;
}
vboBytes -= localBytes;
}
if (!success)
{
// Last ditch attempt: try the local buffer size
vboBytes = localBytes;
glBufferData(GL_ARRAY_BUFFER, vboBytes, 0, isDynamic?GL_STREAM_DRAW:GL_STATIC_DRAW);
if (glGetError() != GL_NO_ERROR)
return ErrorLog("OpenGL was unable to provide a %s vertex buffer.", isDynamic?"dynamic":"static");
}
DebugLog("%s vertex buffer size: %1.2f MB", isDynamic?"Dynamic":"Static", (float)vboBytes/(float)0x100000);
InfoLog("%s vertex buffer size: %1.2f MB", isDynamic?"Dynamic":"Static", (float)vboBytes/(float)0x100000);
// Set the VBO to the size we obtained
Cache->vboMaxOffset = vboBytes;
Cache->vboCurOffset = 0;
// Attempt to allocate space for local VBO
for (i = 0; i < 2; i++)
{
Cache->verts[i] = new(std::nothrow) GLfloat[localMaxVerts*VBO_VERTEX_SIZE];
Cache->curVertIdx[i] = 0;
}
Cache->maxVertIdx = localMaxVerts;
// ... model array
Cache->Models = new(std::nothrow) VBORef[maxNumModels];
Cache->maxModels = maxNumModels;
Cache->numModels = 0;
// ... LUT
Cache->lut = new(std::nothrow) INT16[numLUTEntries];
Cache->lutSize = numLUTEntries;
// ... display list
Cache->List = new(std::nothrow) DisplayList[displayListSize];
ClearDisplayList(Cache);
Cache->maxListSize = displayListSize;
// Check if memory allocation succeeded
if ((Cache->verts[0]==NULL) || (Cache->verts[1]==NULL) || (Cache->Models==NULL) || (Cache->lut==NULL) || (Cache->List==NULL))
{
DestroyModelCache(Cache);
return ErrorLog("Insufficient memory for model cache.");
}
// Clear LUT (MUST be done here because ClearModelCache() won't do it for dynamic models)
for (i = 0; i < numLUTEntries; i++)
Cache->lut[i] = -1;
// All good!
return OKAY;
}
void CRender3D::DestroyModelCache(ModelCache *Cache)
{
glDeleteBuffers(1, &(Cache->vboID));
for (int i = 0; i < 2; i++)
{
if (Cache->verts[i] != NULL)
delete [] Cache->verts[i];
}
if (Cache->Models != NULL)
delete [] Cache->Models;
if (Cache->lut != NULL)
delete [] Cache->lut;
if (Cache->List != NULL)
delete [] Cache->List;
memset(Cache, 0, sizeof(ModelCache));
}

File diff suppressed because it is too large Load diff

View file

@ -1,480 +0,0 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel 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 Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Render3D.h
*
* Header file defining the CRender3D class: OpenGL Real3D graphics engine.
*/
#ifndef INCLUDED_RENDER3D_H
#define INCLUDED_RENDER3D_H
#include "Pkgs/glew.h"
/******************************************************************************
Internal Definitions and Data Structures
NOTE: These should probably be moved inside the Render3D namespace at some
point.
******************************************************************************/
// Model caches sort models by alpha (translucency) state
enum POLY_STATE
{
POLY_STATE_NORMAL = 0,
POLY_STATE_ALPHA
};
struct Vertex
{
GLfloat x,y,z; // vertex
GLfloat n[3]; // normal X, Y, Z
GLfloat u,v; // texture U, V coordinates (in texels, relative to selected texture)
};
struct Poly
{
Vertex Vert[4];
GLfloat n[3]; // polygon normal (used for backface culling)
POLY_STATE state; // alpha or normal?
unsigned numVerts; // triangle (3) or quad (4)
const UINT32 *header; // pointer to Real3D 7-word polygon header
};
/*
* VBORef:
*
* Reference to model polygons stored in a VBO. Each reference has two sets of
* vertices: normal and alpha. Copies of the model with different texture
* offsets applied are searchable via the linked list of texture offset states.
*/
struct VBORef
{
unsigned index[2]; // index of model polygons in VBO
unsigned numVerts[2]; // number of vertices
unsigned lutIdx; // LUT index associated with this model (for fast LUT clearing)
struct VBORef *nextTexOffset; // linked list of models with different texture offset states
UINT16 texOffset; // texture offset data for this model
CTextureRefs texRefs; // unique texture references contained in this model
/*
* Clear():
*
* Clears the VBORef by setting all fields to 0 and clearing the texture
* references.
*/
inline void Clear(void)
{
texRefs.Clear();
lutIdx = 0;
texOffset = 0;
nextTexOffset = NULL;
for (int i = 0; i < 2; i++)
{
index[i] = 0;
numVerts[i] = 0;
}
}
};
// Display list items: model instances and viewport settings
struct DisplayList
{
bool isViewport; // if true, this is a viewport node
union
{
// Viewport data
struct
{
GLfloat projectionMatrix[4*4]; // projection matrix
GLfloat lightingParams[6]; // lighting parameters (see RenderViewport() and vertex shader)
GLfloat spotEllipse[4]; // spotlight ellipse (see RenderViewport())
GLfloat spotRange[2]; // Z range
GLfloat spotColor[3]; // color
GLfloat fogParams[5]; // fog parameters (...)
GLint x, y; // viewport coordinates (scaled and in OpenGL format)
GLint width, height; // viewport dimensions (scaled for display surface size)
} Viewport;
// Model data
struct
{
GLfloat modelViewMatrix[4*4]; // model-view matrix
unsigned index; // index in VBO
unsigned numVerts; // number of vertices
GLint frontFace; // GL_CW (default), GL_CCW, or -GL_CW to indicate no culling
} Model;
} Data;
DisplayList *next; // next display list item with the same state (alpha or non-alpha)
};
/*
* ModelCache:
*
* A model cache tracks all models in a particular region (ie., VROM or polygon
* RAM). It contains a look-up table to quickly obtain VBO indices. Be careful
* when accessing the LUT, there are some special cases.
*
* If the model cache is marked dynamic, cached models may not necessarily be
* retained. Clearing the model cache is also much faster. The LUT entry for
* the last model cached will be valid, but because the LUT may not be
* cleared, one cannot assume a model exists because there is a LUT entry
* pointing to it. Always use NeedToCache() to determine whether caching is
* necessary before reading the LUT!
*/
struct ModelCache
{
// Cache type
bool dynamic;
// Vertex buffer object
unsigned vboMaxOffset; // size of VBO (in bytes)
unsigned vboCurOffset; // current offset in VBO (in bytes)
GLuint vboID; // OpenGL VBO handle
// Local vertex buffers (enough for a single model)
unsigned maxVertIdx; // size of each local vertex buffer (in vertices)
unsigned curVertIdx[2]; // current vertex index (in vertices)
GLfloat *verts[2];
// Array of cached models
unsigned maxModels; // maximum number of models
unsigned numModels; // current number stored
VBORef *Models;
/*
* Look-Up Table:
*
* Can be accessed directly with a LUT index to determine the model index.
* However, it should not be used to determine whether a model needs to be
* cached. Use NeedToCache() instead. A valid index, for example, may still
* have to be re-cached if the model cache is dynamic (polygon RAM).
*/
unsigned lutSize; // number of elements in LUT
INT16 *lut; // stores indices into Models[] or -1 if not yet cached
// Display list
unsigned maxListSize; // maximum number of display list items
unsigned listSize; // number of items in display list
DisplayList *List; // holds all display list items
DisplayList *ListHead[2]; // heads of linked lists for each state
DisplayList *ListTail[2]; // current tail node for each state
};
struct TexSheet
{
unsigned sheetNum;
unsigned mapNum;
unsigned xOffset;
unsigned yOffset;
/*
* Texture Format Buffer
*
* Records the format that a texture (at a given location within the
* texture sheet) is currently stored in. A negative value indicates the
* texture has not been accessed and converted yet and non-negative values
* correspond to the texture format bits in the polygon headers. They can
* be used to determine whether a texture needs to be updated.
*/
int texWidth[2048/32][2048/32];
int texHeight[2048/32][2048/32];
INT8 texFormat[2048/32][2048/32];
};
/******************************************************************************
CRender3D Classes
******************************************************************************/
/*
* CRender3DConfig:
*
* Settings used by CRender3D.
*/
class CRender3DConfig
{
public:
string vertexShaderFile; // path to vertex shader or "" to use internal shader
string fragmentShaderFile; // fragment shader
unsigned maxTexMaps; // maximum number of texture maps to use (1-9)
unsigned maxTexMapExtent; // maximum extent of texture maps (where num of tex sheets per map = extent ^ 2)
bool multiTexture; // if enabled and no external fragment shader, select internal shader w/ multiple texture sheet support
// Defaults
CRender3DConfig(void)
{
// strings will be clear to begin with
maxTexMaps = 9;
maxTexMapExtent = 4;
multiTexture = false;
}
};
/*
* CRender3D:
*
* 3D renderer. Lots of work to do here :)
*/
class CRender3D
{
friend class CTextureRefs;
public:
/*
* RenderFrame(void):
*
* Renders the complete scene database. Must be called between BeginFrame() and
* EndFrame(). This function traverses the scene database and builds up display
* lists.
*/
void RenderFrame(void);
/*
* BeginFrame(void):
*
* Prepare to render a new frame. Must be called once per frame prior to
* drawing anything.
*/
void BeginFrame(void);
/*
* EndFrame(void):
*
* Signals the end of rendering for this frame. Must be called last during
* the frame.
*/
void EndFrame(void);
/*
* UploadTextures(x, y, width, height):
*
* Signals that a portion of texture RAM has been updated.
*
* Parameters:
* x X position within texture RAM.
* y Y position within texture RAM.
* width Width of texture data in texels.
* height Height.
*/
void UploadTextures(unsigned x, unsigned y, unsigned width, unsigned height);
/*
* AttachMemory(cullingRAMLoPtr, cullingRAMHiPtr, polyRAMPtr, vromPtr,
* textureRAMPtr):
*
* Attaches RAM and ROM areas. This must be done prior to any rendering
* otherwise the program may crash with an access violation.
*
* Parameters:
* cullingRAMLoPtr Pointer to low culling RAM (4 MB).
* cullingRAMHiPtr Pointer to high culling RAM (1 MB).
* polyRAMPtr Pointer to polygon RAM (4 MB).
* vromPtr Pointer to video ROM (64 MB).
* textureRAMPtr Pointer to texture RAM (8 MB).
*/
void AttachMemory(const UINT32 *cullingRAMLoPtr,
const UINT32 *cullingRAMHiPtr, const UINT32 *polyRAMPtr,
const UINT32 *vromPtr, const UINT16 *textureRAMPtr);
/*
* SetStep(stepID):
*
* Sets the Model 3 hardware stepping, which also determines the Real3D
* functionality. The default is Step 1.0. This should be called prior to
* any other emulation functions and after Init().
*
* Parameters:
* stepID 0x10 for Step 1.0, 0x15 for Step 1.5, 0x20 for Step 2.0,
* or 0x21 for Step 2.1. Anything else defaults to 1.0.
*/
void SetStep(int stepID);
/*
* Init(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes):
*
* One-time initialization of the context. Must be called before any other
* members (meaning it should be called even before being attached to any
* other objects that want to use it).
*
* External shader files are loaded according to configuration settings.
*
* Parameters:
* xOffset X offset of the viewable area within OpenGL display
* surface, in pixels.
* yOffset Y offset.
* xRes Horizontal resolution of the viewable area.
* yRes Vertical resolution.
* totalXRes Horizontal resolution of the complete display area.
* totalYRes Vertical resolution.
*
* Returns:
* OKAY is successful, otherwise FAILED if a non-recoverable error
* occurred. Any allocated memory will not be freed until the
* destructor is called. Prints own error messages.
*/
bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes);
/*
* CRender3D(void):
* ~CRender3D(void):
*
* Constructor and destructor.
*/
CRender3D(void);
~CRender3D(void);
private:
/*
* Private Members
*/
// Real3D address translation
const UINT32 *TranslateCullingAddress(UINT32 addr);
const UINT32 *TranslateModelAddress(UINT32 addr);
// Model caching and display list management
void DrawDisplayList(ModelCache *Cache, POLY_STATE state);
bool AppendDisplayList(ModelCache *Cache, bool isViewport, const struct VBORef *Model);
void ClearDisplayList(ModelCache *Cache);
bool InsertPolygon(ModelCache *cache, const Poly *p);
void InsertVertex(ModelCache *cache, const Vertex *v, const Poly *p, float normFlip);
struct VBORef *BeginModel(ModelCache *cache);
void EndModel(ModelCache *cache, struct VBORef *Model, int lutIdx, UINT16 texOffset);
struct VBORef *CacheModel(ModelCache *cache, int lutIdx, UINT16 texOffset, const UINT32 *data);
struct VBORef *LookUpModel(ModelCache *cache, int lutIdx, UINT16 texOffset);
void ClearModelCache(ModelCache *cache);
bool CreateModelCache(ModelCache *cache, unsigned vboMaxVerts, unsigned localMaxVerts, unsigned maxNumModels, unsigned numLUTEntries, unsigned displayListSize, bool isDynamic);
void DestroyModelCache(ModelCache *cache);
// Texture management
void DecodeTexture(int format, int x, int y, int width, int height);
// Matrix stack
void MultMatrix(UINT32 matrixOffset);
void InitMatrixStack(UINT32 matrixBaseAddr);
// Scene database traversal
bool DrawModel(UINT32 modelAddr);
void DescendCullingNode(UINT32 addr);
void DescendPointerList(UINT32 addr);
void DescendNodePtr(UINT32 nodeAddr);
void RenderViewport(UINT32 addr, int pri);
// In-frame error reporting
bool ErrorLocalVertexOverflow(void);
bool ErrorUnableToCacheModel(UINT32 modelAddr);
void ClearErrors(void);
/*
* Data
*/
// Stepping
int step;
int offset; // offset to subtract for words 3 and higher of culling nodes
GLfloat vertexFactor; // fixed-point conversion factor for vertices
// Memory (passed from outside)
const UINT32 *cullingRAMLo; // 4 MB
const UINT32 *cullingRAMHi; // 1 MB
const UINT32 *polyRAM; // 4 MB
const UINT32 *vrom; // 64 MB
const UINT16 *textureRAM; // 8 MB
// Error reporting
unsigned errorMsgFlags; // tracks which errors have been printed this frame
// Real3D Base Matrix Pointer
const float *matrixBasePtr;
// Current viewport parameters (updated as viewports are traversed)
GLfloat lightingParams[6];
GLfloat fogParams[5];
GLfloat spotEllipse[4];
GLfloat spotRange[2];
GLfloat spotColor[3];
GLint viewportX, viewportY;
GLint viewportWidth, viewportHeight;
// Scene graph stack
int listDepth; // how many lists have we recursed into
int stackDepth; // for debugging and error handling purposes
// Texture offset (during scene graph processing)
GLfloat texOffsetXY[2]; // decoded X, Y offsets
UINT16 texOffset; // raw texture offset data as it appears in culling node
// Resolution and scaling factors (to support resolutions higher than 496x384) and offsets
GLfloat xRatio, yRatio;
unsigned xOffs, yOffs;
unsigned totalXRes, totalYRes;
// Texture details
static int defaultFmtToTexSheetNum[8]; // default mapping from Model3 texture format to texture sheet
unsigned numTexMaps; // total number of texture maps
GLuint texMapIDs[9]; // GL texture IDs of texture maps
unsigned numTexSheets; // total number of texture sheets
TexSheet *texSheets; // texture sheet objects
TexSheet *fmtToTexSheet[8]; // final mapping from Model3 texture format to texture sheet
// Shader programs and input data locations
GLuint shaderProgram; // shader program object
GLuint vertexShader; // vertex shader handle
GLuint fragmentShader; // fragment shader
GLint textureMapLoc; // location of "textureMap" uniform (if available)
GLint textureMapLocs[8]; // location of "textureMap[0-7]" uniforms (if available)
GLint modelViewMatrixLoc; // uniform
GLint projectionMatrixLoc; // uniform
GLint lightingLoc; // uniform
GLint mapSizeLoc; // uniform
GLint spotEllipseLoc; // uniform
GLint spotRangeLoc; // uniform
GLint spotColorLoc; // uniform
GLint subTextureLoc; // attribute
GLint texParamsLoc; // attribute
GLint texFormatLoc; // attribute
GLint texMapLoc; // attribute
GLint transLevelLoc; // attribute
GLint lightEnableLoc; // attribute
GLint shininessLoc; // attribute
GLint fogIntensityLoc; // attribute
// Model caching
ModelCache VROMCache; // VROM (static) models
ModelCache PolyCache; // polygon RAM (dynamic) models
/*
* Texture Decode Buffer
*
* Textures are decoded and copied from texture RAM into this temporary buffer
* before being uploaded. Dimensions are 512x512.
*/
GLfloat *textureBuffer; // RGBA8 format
};
#endif // INCLUDED_RENDER3D_H

View file

@ -1,663 +0,0 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel 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 Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Shaders3D.h
*
* Header file containing the 3D vertex and fragment shaders.
*/
#ifndef INCLUDED_SHADERS3D_H
#define INCLUDED_SHADERS3D_H
// Vertex shader
static const char vertexShaderSource[] =
{
"/**\n"
" ** Supermodel\n"
" ** A Sega Model 3 Arcade Emulator.\n"
" ** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson \n"
" **\n"
" ** This file is part of Supermodel.\n"
" **\n"
" ** Supermodel is free software: you can redistribute it and/or modify it under\n"
" ** the terms of the GNU General Public License as published by the Free \n"
" ** Software Foundation, either version 3 of the License, or (at your option)\n"
" ** any later version.\n"
" **\n"
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT\n"
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n"
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n"
" ** more details.\n"
" **\n"
" ** You should have received a copy of the GNU General Public License along\n"
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>.\n"
" **/\n"
" \n"
"/*\n"
" * Vertex.glsl\n"
" *\n"
" * Vertex shader for 3D rendering.\n"
" */\n"
" \n"
"#version 120\n"
"\n"
"// Global uniforms\n"
"uniform mat4\tmodelViewMatrix;\t// model -> view space matrix\n"
"uniform mat4\tprojectionMatrix;\t// view space -> screen space matrix\n"
"uniform vec3\tlighting[2];\t\t// lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)\n"
"uniform vec4\tspotEllipse;\t\t// spotlight ellipse position: .x=X position (normalized device coordinates), .y=Y position, .z=half-width, .w=half-height)\n"
"uniform vec2\tspotRange;\t\t\t// spotlight Z range: .x=start (viewspace coordinates), .y=limit\n"
"uniform vec3\tspotColor;\t\t\t// spotlight RGB color\n"
"\n"
"// Custom vertex attributes\n"
"attribute vec4\tsubTexture;\t\t// .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)\n"
"attribute vec4\ttexParams;\t\t// .x=texture enable (if 1, else 0), .y=use transparency (if >=0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode\n"
"attribute float\ttexFormat;\t\t// T1RGB5 contour texture (if > 0)\n"
"attribute float\ttexMap;\t\t// texture map number\n"
"attribute float\ttransLevel;\t\t// translucence level, 0.0 (transparent) to 1.0 (opaque). if less than 1.0, replace alpha value\n"
"attribute float\tlightEnable;\t// lighting enabled (1.0) or luminous (0.0), drawn at full intensity\n"
"attribute float\tshininess;\t\t// specular shininess (if >= 0.0) or disable specular lighting (negative)\n"
"attribute float\tfogIntensity;\t// fog intensity (1.0, full fog effect, 0.0, no fog) \n"
"\n"
"// Custom outputs to fragment shader\n"
"varying vec4\tfsSubTexture;\n"
"varying vec4\tfsTexParams;\n"
"varying float\tfsTexFormat;\n"
"varying float\tfsTexMap;\n"
"varying float\tfsTransLevel;\n"
"varying vec3\tfsLightIntensity;\t// total light intensity for this vertex\n"
"varying float\tfsSpecularTerm;\t\t// specular light term (additive)\n"
"varying float\tfsFogFactor;\t\t// fog factor\n"
"varying float\tfsViewZ;\n"
"\n"
"// Gets the 3x3 matrix out of a 4x4 (because mat3(mat4matrix) does not work on ATI!)\n"
"mat3 GetLinearPart( mat4 m )\n"
"{\n"
"\tmat3 result;\n"
"\t\n"
"\tresult[0][0] = m[0][0]; \n"
"\tresult[0][1] = m[0][1]; \n"
"\tresult[0][2] = m[0][2]; \n"
"\n"
"\tresult[1][0] = m[1][0]; \n"
"\tresult[1][1] = m[1][1]; \n"
"\tresult[1][2] = m[1][2]; \n"
"\t\n"
"\tresult[2][0] = m[2][0]; \n"
"\tresult[2][1] = m[2][1]; \n"
"\tresult[2][2] = m[2][2]; \n"
"\t\n"
"\treturn result;\n"
"}\n"
"\n"
"void main(void)\n"
"{\n"
"\tvec3\tviewVertex;\t\t// vertex coordinates in view space\n"
"\tvec3\tviewNormal;\t\t// vertex normal in view space\n"
"\tvec3\tsunVector;\t\t// sun lighting vector (as reflecting away from vertex)\n"
"\tfloat\tsunFactor;\t\t// sun light projection along vertex normal (0.0 to 1.0)\n"
"\tvec3\thalfway;\n"
"\tfloat\tspecFactor;\n"
"\t\n"
"\t// Transform vertex\n"
"\tgl_Position = projectionMatrix * modelViewMatrix * gl_Vertex;\n"
"\tviewVertex = vec3(modelViewMatrix * gl_Vertex);\t\n"
"\t\n"
"\t/*\n"
"\t * Modulation\n"
"\t *\n"
" \t * Polygon color serves as material color (modulating the light intensity)\n"
"\t * for textured polygons. The fragment shader will ignore (overwrite) the\n"
"\t * the color passed to it if the fragment is textured. \n"
"\t *\n"
"\t * Untextured fragments must be set to the polygon color and the light\n"
"\t * intensity is initialized to 1.0 here. Alpha must be set to 1.0 because\n"
"\t * the fragment shader multiplies it by the polygon translucency setting. \n"
"\t *\n"
"\t * TO-DO: Does OpenGL set alpha to 1.0 by default if no alpha is specified\n"
"\t * for the vertex? If so, we can remove that line from here.\n"
"\t */\n"
"\n"
"\tgl_FrontColor = gl_Color;\t// untextured polygons will use this\n"
"\tgl_FrontColor.a = 1.0;\t\n"
"\tfsLightIntensity = vec3(1.0,1.0,1.0);\n"
"\tif (texParams.x > 0.5)\t\t// textured\n"
"\t\tfsLightIntensity *= gl_Color.rgb;\n"
"\t\t\n"
"\t/*\n"
" \t * Sun Light\n"
"\t *\n"
"\t * Parallel light source and ambient lighting are only applied for non-\n"
"\t * luminous polygons.\n"
" \t */\n"
"\tfsSpecularTerm = 0.0;\n"
" \tif (lightEnable > 0.5)\t// not luminous\n"
"\t{\n"
"\t\t// Normal -> view space\n"
"\t\tviewNormal = normalize(GetLinearPart(modelViewMatrix)*gl_Normal);\n"
"\n"
"\t\t// Real3D -> OpenGL view space convention (TO-DO: do this outside of shader)\n"
"\t\tsunVector = lighting[0]*vec3(1.0,-1.0,-1.0);\n"
"\t\t\n"
"\t\t// Compute diffuse factor for sunlight\n"
"\t\tsunFactor = max(dot(sunVector,viewNormal),0.0);\n"
"\t\t\n"
"\t\t// Total light intensity: sum of all components\n"
"\t\tfsLightIntensity *= (sunFactor*lighting[1].x+lighting[1].y);\n"
"\t\t\n"
"\t\t/*\n"
"\t\t * Specular Lighting\n"
"\t\t *\n"
"\t\t * The specular term is treated similarly to the \"separate specular\n"
"\t\t * color\" functionality of OpenGL: it is added as a highlight in the\n"
"\t\t * fragment shader. This allows even black textures to be lit.\n"
"\t\t *\n"
"\t\t * TO-DO: Ambient intensity viewport parameter is known but what about\n"
"\t\t * the intensity of the specular term? Always applied with full \n"
"\t\t * intensity here but this is unlikely to be correct.\n"
"\t\t */\n"
" \t\tif (shininess >= 0.0)\n"
" \t\t{\n"
" \t\t\t// Standard specular lighting equation\n"
" \t\t\tvec3 V = normalize(-viewVertex);\n"
" \t\t\tvec3 H = normalize(sunVector+V);\t// halfway vector\n"
" \t\t\tfloat s = max(10.0,64.0-shininess);\t\t// seems to look nice, but probably not correct\n"
" \t\t\tfsSpecularTerm = pow(max(dot(viewNormal,H),0.0),s);\n"
" \t\t\tif (sunFactor <= 0.0) fsSpecularTerm = 0.0;\n"
" \t\t\t\n"
" \t\t\t// Faster approximation \t\t\t\n"
" \t\t\t//float temp = max(dot(viewNormal,H),0.0);\n"
" \t\t\t//float s = 64.0-shininess;\n"
" \t\t\t//fsSpecularTerm = temp/(s-temp*s+temp);\n"
" \t\t\t\n"
" \t\t\t// Phong formula\n"
" \t\t\t//vec3 R = normalize(2.0*dot(sunVector,viewNormal)*viewNormal - sunVector);\n"
" \t\t\t//vec3 V = normalize(-viewVertex);\n"
" \t\t\t//float s = max(2.0,64.0-shininess);\n"
" \t\t\t//fsSpecularTerm = pow(max(dot(R,V),0.0),s);\n"
" \t\t}\n"
"\t}\n"
"\t\n"
"\t// Fog\n"
"\tfloat z = length(viewVertex);\n"
"\tfsFogFactor = clamp(1.0-fogIntensity*(gl_Fog.start+z*gl_Fog.density), 0.0, 1.0);\n"
"\n"
"\t// Pass viewspace Z coordinate (for spotlight)\n"
"\tfsViewZ = -viewVertex.z;\t// convert Z from GL->Real3D convention (want +Z to be further into screen)\n"
"\n"
"\t// Pass remaining parameters to fragment shader\n"
"\tgl_TexCoord[0] = gl_MultiTexCoord0;\n"
"\tfsSubTexture = subTexture;\n"
"\tfsTexParams = texParams;\n"
"\tfsTransLevel = transLevel;\n"
"\tfsTexFormat = texFormat;\n"
"\tfsTexMap = texMap;\n"
"}\n"
};
// Fragment shader (single texture sheet)
static const char fragmentShaderSingleSheetSource[] =
{
"/**\n"
" ** Supermodel\n"
" ** A Sega Model 3 Arcade Emulator.\n"
" ** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson \n"
" **\n"
" ** This file is part of Supermodel.\n"
" **\n"
" ** Supermodel is free software: you can redistribute it and/or modify it under\n"
" ** the terms of the GNU General Public License as published by the Free \n"
" ** Software Foundation, either version 3 of the License, or (at your option)\n"
" ** any later version.\n"
" **\n"
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT\n"
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n"
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n"
" ** more details.\n"
" **\n"
" ** You should have received a copy of the GNU General Public License along\n"
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>.\n"
" **/\n"
" \n"
"/*\n"
" * Fragment.glsl\n"
" *\n"
" * Fragment shader for 3D rendering.\n"
" */\n"
"\n"
"#version 120\n"
"\n"
"// Global uniforms\n"
"uniform sampler2D\ttextureMap;\t\t// complete texture map, 2048x2048 texels\n"
"uniform vec4\t\tspotEllipse;\t\t// spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)\n"
"uniform vec2\t\tspotRange;\t\t// spotlight Z range: .x=start (viewspace coordinates), .y=limit\n"
"uniform vec3\t\tspotColor;\t\t// spotlight RGB color\n"
"uniform vec3\t\tlighting[2];\t\t// lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)\n"
"uniform float\t\tmapSize;\t\t// texture map size (2048,4096,6144 etc)\n"
"\n"
"// Inputs from vertex shader \n"
"varying vec4\t\tfsSubTexture;\t// .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)\n"
"varying vec4\t\tfsTexParams;\t// .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode\n"
"varying float\t\tfsTexFormat;\t// T1RGB5 contour texture (if > 0)\n"
"varying float\t\tfsTexMap;\t\t// texture map number\n"
"varying float\t\tfsTransLevel;\t// translucence level, 0.0 (transparent) to 1.0 (opaque)\n"
"varying vec3\t\tfsLightIntensity;\t// lighting intensity \n"
"varying float\t\tfsSpecularTerm;\t// specular highlight\n"
"varying float\t\tfsFogFactor;\t// fog factor\n"
"varying float\t\tfsViewZ;\t\t// Z distance to fragment from viewpoint at origin\n"
"\n"
"/*\n"
" * WrapTexelCoords():\n"
" *\n"
" * Computes the normalized OpenGL S,T coordinates within the 2048x2048 texture\n"
" * sheet, taking into account wrapping behavior.\n"
" *\n"
" * Computing normalized OpenGL texture coordinates (0 to 1) within the \n"
" * Real3D texture sheet:\n"
" *\n"
" * If the texture is not mirrored, we simply have to clamp the\n"
" * coordinates to fit within the texture dimensions, add the texture\n"
" * X, Y position to select the appropriate one, and normalize by 2048\n"
" * (the dimensions of the Real3D texture sheet).\n"
" *\n"
" *\t\t= [(u,v)%(w,h)+(x,y)]/(2048,2048)\n"
" *\n"
" * If mirroring is enabled, textures are mirrored every odd multiple of\n"
" * the original texture. To detect whether we are in an odd multiple, \n"
" * simply divide the coordinate by the texture dimension and check \n"
" * whether the result is odd. Then, clamp the coordinates as before but\n"
" * subtract from the last texel to mirror them:\n"
" *\n"
" * \t\t= [M*((w-1,h-1)-(u,v)%(w,h)) + (1-M)*(u,v)%(w,h) + (x,y)]/(2048,2048)\n"
" *\t\twhere M is 1.0 if the texture must be mirrored.\n"
" *\n"
" * As an optimization, this function computes TWO texture coordinates\n"
" * simultaneously. The first is texCoord.xy, the second is in .zw. The other\n"
" * parameters must have .xy = .zw.\n"
" */\n"
"vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEnable)\n"
"{\n"
"\tvec4\tclampedCoord, mirror, glTexCoord;\n"
"\t\n"
"\tclampedCoord = mod(texCoord,texSize);\t\t\t\t\t\t// clamp coordinates to within texture size\n"
"\tmirror = mirrorEnable * mod(floor(texCoord/texSize),2.0);\t// whether this texel needs to be mirrored\n"
"\n"
"\tglTexCoord = (\tmirror*(texSize-clampedCoord) +\n"
"\t\t\t\t\t(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +\n"
"\t\t\t\t\ttexOffset\n"
"\t\t\t\t ) / mapSize;\n"
"\treturn glTexCoord;\n"
"}\n"
"\n"
"/*\n"
" * main():\n"
" *\n"
" * Fragment shader entry point.\n"
" */\n"
"\n"
"void main(void)\n"
"{\t\n"
"\tvec4\tuv_top, uv_bot, c[4];\n"
"\tvec2\tr;\n"
"\tvec4\tfragColor;\n"
"\tvec2\tellipse;\n"
"\tvec3\tlightIntensity;\n"
"\tfloat\tinsideSpot;\n"
"\tint\t\tx;\n"
"\t\n"
"\t// Get polygon color for untextured polygons (textured polygons will overwrite)\n"
"\tif (fsTexParams.x < 0.5)\n"
"\t\tfragColor = gl_Color;\t\t\n"
"\telse\n"
"\t// Textured polygons: set fragment color to texel value\n"
"\t{\t\t\t\n"
"\t\t/*\n"
"\t\t * Bilinear Filtering\n"
"\t\t *\n"
"\t\t * In order to get this working on ATI, the number of operations is\n"
"\t\t * reduced by putting everything into vec4s. uv_top holds the UV \n"
"\t\t * coordinates for the top two texels (.xy=left, .zw=right) and uv_bot\n"
"\t\t * is for the lower two.\n"
"\t\t */\n"
"\n"
"\t\t// Compute fractional blending factor, r, and lower left corner of texel 0\n"
"\t\tuv_bot.xy = gl_TexCoord[0].st-vec2(0.5,0.5);\t// move into the lower left blending texel \n"
"\t\tr = uv_bot.xy-floor(uv_bot.xy);\t\t\t\t\t// fractional part\n"
"\t\tuv_bot.xy = floor(uv_bot.xy);\t\t\t\t\t// integral part\n"
"\t\t\n"
"\t\t// Compute texel coordinates\n"
"\t\tuv_bot.xy += vec2(0.5,0.5);\t// offset to center of pixel (should not be needed but it fixes a lot of glitches, esp. on Nvidia)\n"
"\t\tuv_bot.zw = uv_bot.xy + vec2(1.0,0.0);\t\t\t// compute coordinates of the other three neighbors\n"
"\t\tuv_top = uv_bot + vec4(0.0,1.0,0.0,1.0);\n"
"\n"
"\t\t// Compute the properly wrapped texel coordinates\n"
"\t\tuv_top = WrapTexelCoords(uv_top,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));\n"
"\t\tuv_bot = WrapTexelCoords(uv_bot,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));\n"
"\n"
"\t\t// Fetch the texels\n"
"\t\tc[0]=texture2D(textureMap,uv_bot.xy);\t// bottom-left (base texel)\n"
"\t\tc[1]=texture2D(textureMap,uv_bot.zw);\t// bottom-right\n"
"\t\tc[2]=texture2D(textureMap,uv_top.xy);\t// top-left\n"
"\t\tc[3]=texture2D(textureMap,uv_top.zw);\t// top-right\t\t\n"
"\n"
"\t\t// Interpolate texels and blend result with material color to determine final (unlit) fragment color\n"
"\t\t// fragColor = (c[0]*(1.0-r.s)*(1.0-r.t) + c[1]*r.s*(1.0-r.t) + c[2]*(1.0-r.s)*r.t + c[3]*r.s*r.t);\n"
"\t\t// Faster method:\n"
"\t\tc[0] += (c[1]-c[0])*r.s;\t\t\t// 2 alu\n"
"\t\tc[2] += (c[3]-c[2])*r.s;\t\t\t// 2 alu\n"
"\t\tfragColor = c[0]+(c[2]-c[0])*r.t;\t//2 alu\n"
"\t\n"
"\t\t/*\n"
"\t\t * T1RGB5:\n"
"\t\t *\n"
"\t\t * The transparency bit determines whether to discard pixels (if set).\n"
"\t\t * What is unknown is how this bit behaves when interpolated. OpenGL\n"
"\t\t * processes it as an alpha value, so it might concievably be blended\n"
"\t\t * with neighbors. Here, an arbitrary threshold is chosen.\n"
"\t\t *\n"
"\t\t * To-do: blending could probably enabled and this would work even\n"
"\t\t * better with a hard threshold.\n"
"\t\t *\n"
"\t\t * Countour processing also seems to be enabled for RGBA4 textures.\n"
"\t\t * When the alpha value is 0.0 (or close), pixels are discarded \n"
"\t\t * entirely.\n"
"\t\t */\n"
"\t\tif (fsTexParams.y > 0.5)\t// contour processing enabled\n"
"\t\t{\n"
"\t\t\tif (fragColor.a < 0.01)\t// discard anything with alpha == 0\n"
"\t\t\t\tdiscard;\n"
"\t\t}\n"
"\t\t\n"
"\t\t// If contour texture and not discarded, force alpha to 1.0 because will later be modified by polygon translucency\n"
"\t\tif (fsTexFormat < 0.5)\t\t// contour (T1RGB5) texture\n"
"\t\t\tfragColor.a = 1.0;\n"
"\t}\n"
"\n"
"\t// Compute spotlight and apply lighting\n"
"\tellipse = (gl_FragCoord.xy-spotEllipse.xy)/spotEllipse.zw;\n"
"\tinsideSpot = dot(ellipse,ellipse);\n"
"\tif ((insideSpot <= 1.0) && (fsViewZ>=spotRange.x) && (fsViewZ<spotRange.y))\n"
"\t\tlightIntensity = fsLightIntensity+(1.0-insideSpot)*spotColor;\n"
"\telse\n"
"\t\tlightIntensity = fsLightIntensity;\n"
"\tfragColor.rgb *= lightIntensity;\n"
"\tfragColor.rgb += vec3(fsSpecularTerm,fsSpecularTerm,fsSpecularTerm);\n"
"\t\n"
"\t// Translucency (modulates existing alpha channel for RGBA4 texels)\n"
"\tfragColor.a *= fsTransLevel;\n"
"\n"
"\t// Apply fog under the control of fog factor setting from polygon header\n"
"\tfragColor.rgb = mix(gl_Fog.color.rgb, fragColor.rgb, fsFogFactor);\n"
"\n"
"\t// Store final color\n"
"\tgl_FragColor = fragColor;\n"
"}\n"
};
// Fragment shader (8 texture sheets)
static const char fragmentShaderMultiSheetSource[] =
{
"/**\n"
" ** Supermodel\n"
" ** A Sega Model 3 Arcade Emulator.\n"
" ** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson \n"
" **\n"
" ** This file is part of Supermodel.\n"
" **\n"
" ** Supermodel is free software: you can redistribute it and/or modify it under\n"
" ** the terms of the GNU General Public License as published by the Free \n"
" ** Software Foundation, either version 3 of the License, or (at your option)\n"
" ** any later version.\n"
" **\n"
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT\n"
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n"
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n"
" ** more details.\n"
" **\n"
" ** You should have received a copy of the GNU General Public License along\n"
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>.\n"
" **/\n"
" \n"
"/*\n"
" * Fragment_MultiSheet.glsl\n"
" *\n"
" * Fragment shader for 3D rendering. Uses 8 texture sheets to decode the \n"
" * different possible formats.\n"
" */\n"
"\n"
"#version 120\n"
"\n"
"// Global uniforms\n"
"uniform sampler2D\ttextureMap0;\t\t// complete texture map (fmt 0), 2048x2048 texels\n"
"uniform sampler2D\ttextureMap1;\t\t// complete texture map (fmt 1), 2048x2048 texels\n"
"uniform sampler2D\ttextureMap2;\t\t// complete texture map (fmt 2), 2048x2048 texels\n"
"uniform sampler2D\ttextureMap3;\t\t// complete texture map (fmt 3), 2048x2048 texels\n"
"uniform sampler2D\ttextureMap4;\t\t// complete texture map (fmt 4), 2048x2048 texels\n"
"uniform sampler2D\ttextureMap5;\t\t// complete texture map (fmt 5), 2048x2048 texels\n"
"uniform sampler2D\ttextureMap6;\t\t// complete texture map (fmt 6), 2048x2048 texels\n"
"uniform sampler2D\ttextureMap7;\t\t// complete texture map (fmt 7), 2048x2048 texels\n"
"uniform vec4\t\tspotEllipse;\t\t// spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)\n"
"uniform vec2\t\tspotRange;\t\t\t// spotlight Z range: .x=start (viewspace coordinates), .y=limit\n"
"uniform vec3\t\tspotColor;\t\t\t// spotlight RGB color\n"
"uniform vec3\t\tlighting[2];\t\t// lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)\n"
"uniform float\t\tmapSize;\t\t// texture map size (2048,4096,6144 etc)\n"
"\n"
"// Inputs from vertex shader \n"
"varying vec4\t\tfsSubTexture;\t// .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)\n"
"varying vec4\t\tfsTexParams;\t// .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode\n"
"varying float\t\tfsTexFormat;\t// T1RGB5 contour texture (if > 0)\n"
"varying float\t\tfsTexMap;\t// texture map number\n"
"varying float\t\tfsTransLevel;\t// translucence level, 0.0 (transparent) to 1.0 (opaque)\n"
"varying vec3\t\tfsLightIntensity;\t// lighting intensity \n"
"varying float\t\tfsSpecularTerm;\t// specular highlight\n"
"varying float\t\tfsFogFactor;\t// fog factor\n"
"varying float\t\tfsViewZ;\t\t// Z distance to fragment from viewpoint at origin\n"
"\n"
"/*\n"
" * WrapTexelCoords():\n"
" *\n"
" * Computes the normalized OpenGL S,T coordinates within the 2048x2048 texture\n"
" * sheet, taking into account wrapping behavior.\n"
" *\n"
" * Computing normalized OpenGL texture coordinates (0 to 1) within the \n"
" * Real3D texture sheet:\n"
" *\n"
" * If the texture is not mirrored, we simply have to clamp the\n"
" * coordinates to fit within the texture dimensions, add the texture\n"
" * X, Y position to select the appropriate one, and normalize by 2048\n"
" * (the dimensions of the Real3D texture sheet).\n"
" *\n"
" *\t\t= [(u,v)%(w,h)+(x,y)]/(2048,2048)\n"
" *\n"
" * If mirroring is enabled, textures are mirrored every odd multiple of\n"
" * the original texture. To detect whether we are in an odd multiple, \n"
" * simply divide the coordinate by the texture dimension and check \n"
" * whether the result is odd. Then, clamp the coordinates as before but\n"
" * subtract from the last texel to mirror them:\n"
" *\n"
" * \t\t= [M*((w-1,h-1)-(u,v)%(w,h)) + (1-M)*(u,v)%(w,h) + (x,y)]/(2048,2048)\n"
" *\t\twhere M is 1.0 if the texture must be mirrored.\n"
" *\n"
" * As an optimization, this function computes TWO texture coordinates\n"
" * simultaneously. The first is texCoord.xy, the second is in .zw. The other\n"
" * parameters must have .xy = .zw.\n"
" */\n"
"vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEnable)\n"
"{\n"
"\tvec4\tclampedCoord, mirror, glTexCoord;\n"
"\t\n"
"\tclampedCoord = mod(texCoord,texSize);\t\t\t\t\t\t// clamp coordinates to within texture size\n"
"\tmirror = mirrorEnable * mod(floor(texCoord/texSize),2.0);\t// whether this texel needs to be mirrored\n"
"\n"
"\tglTexCoord = (\tmirror*(texSize-clampedCoord) +\n"
"\t\t\t\t\t(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +\n"
"\t\t\t\t\ttexOffset\n"
"\t\t\t\t ) / mapSize;\n"
"\treturn glTexCoord;\n"
"}\n"
"\n"
"/*\n"
" * main():\n"
" *\n"
" * Fragment shader entry point.\n"
" */\n"
"\n"
"void main(void)\n"
"{\t\n"
"\tvec4\tuv_top, uv_bot, c[4];\n"
"\tvec2\tr;\n"
"\tvec4\tfragColor;\n"
"\tvec2\tellipse;\n"
"\tvec3\tlightIntensity;\n"
"\tfloat\tinsideSpot;\n"
"\tint\t\tx;\n"
"\t\n"
"\t// Get polygon color for untextured polygons (textured polygons will overwrite)\n"
"\tif (fsTexParams.x < 0.5)\n"
"\t\tfragColor = gl_Color;\t\t\n"
"\telse\n"
"\t// Textured polygons: set fragment color to texel value\n"
"\t{\t\t\t\n"
"\t\t/*\n"
"\t\t * Bilinear Filtering\n"
"\t\t *\n"
"\t\t * In order to get this working on ATI, the number of operations is\n"
"\t\t * reduced by putting everything into vec4s. uv_top holds the UV \n"
"\t\t * coordinates for the top two texels (.xy=left, .zw=right) and uv_bot\n"
"\t\t * is for the lower two.\n"
"\t\t */\n"
"\n"
"\t\t// Compute fractional blending factor, r, and lower left corner of texel 0\n"
"\t\tuv_bot.xy = gl_TexCoord[0].st-vec2(0.5,0.5);\t// move into the lower left blending texel \n"
"\t\tr = uv_bot.xy-floor(uv_bot.xy);\t\t\t\t\t// fractional part\n"
"\t\tuv_bot.xy = floor(uv_bot.xy);\t\t\t\t\t// integral part\n"
"\t\t\n"
"\t\t// Compute texel coordinates\n"
"\t\tuv_bot.xy += vec2(0.5,0.5);\t// offset to center of pixel (should not be needed but it fixes a lot of glitches, esp. on Nvidia)\n"
"\t\tuv_bot.zw = uv_bot.xy + vec2(1.0,0.0);\t\t\t// compute coordinates of the other three neighbors\n"
"\t\tuv_top = uv_bot + vec4(0.0,1.0,0.0,1.0);\n"
"\n"
"\t\t// Compute the properly wrapped texel coordinates\n"
"\t\tuv_top = WrapTexelCoords(uv_top,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));\n"
"\t\tuv_bot = WrapTexelCoords(uv_bot,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));\n"
"\n"
"\t\t// Fetch the texels from the given texture map\n"
"\t\tif (fsTexMap < 0.5f)\t{\n"
"\t\t\tc[0]=texture2D(textureMap0, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap0, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap0, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap0, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexMap < 1.5f) {\n"
" c[0]=texture2D(textureMap1, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap1, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap1, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap1, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexMap < 2.5f) {\n"
" c[0]=texture2D(textureMap2, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap2, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap2, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap2, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexMap < 3.5f) {\n"
" c[0]=texture2D(textureMap3, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap3, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap3, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap3, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexMap < 4.5f) {\n"
" c[0]=texture2D(textureMap4, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap4, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap4, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap4, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexMap < 5.5f) {\n"
" c[0]=texture2D(textureMap5, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap5, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap5, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap5, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexMap < 6.5f) {\n"
"\t\t\tc[0]=texture2D(textureMap6, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap6, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap6, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap6, uv_top.zw); // top-right\n"
"\t\t} else {\n"
" c[0]=texture2D(textureMap7, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap7, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap7, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap7, uv_top.zw); // top-right\n"
"\t\t} \n"
"\n"
"\t\t// Interpolate texels and blend result with material color to determine final (unlit) fragment color\n"
"\t\t// fragColor = (c[0]*(1.0-r.s)*(1.0-r.t) + c[1]*r.s*(1.0-r.t) + c[2]*(1.0-r.s)*r.t + c[3]*r.s*r.t);\n"
"\t\t// Faster method:\n"
"\t\tc[0] += (c[1]-c[0])*r.s;\t\t\t// 2 alu\n"
"\t\tc[2] += (c[3]-c[2])*r.s;\t\t\t// 2 alu\n"
"\t\tfragColor = c[0]+(c[2]-c[0])*r.t;\t// 2 alu\n"
"\t\n"
"\t\t/*\n"
"\t\t * T1RGB5:\n"
"\t\t *\n"
"\t\t * The transparency bit determines whether to discard pixels (if set).\n"
"\t\t * What is unknown is how this bit behaves when interpolated. OpenGL\n"
"\t\t * processes it as an alpha value, so it might concievably be blended\n"
"\t\t * with neighbors. Here, an arbitrary threshold is chosen.\n"
"\t\t *\n"
"\t\t * To-do: blending could probably enabled and this would work even\n"
"\t\t * better with a hard threshold.\n"
"\t\t *\n"
"\t\t * Countour processing also seems to be enabled for RGBA4 textures.\n"
"\t\t * When the alpha value is 0.0 (or close), pixels are discarded \n"
"\t\t * entirely.\n"
"\t\t */\n"
"\t\tif (fsTexParams.y > 0.5)\t// contour processing enabled\n"
"\t\t{\n"
"\t\t\tif (fragColor.a < 0.01)\t// discard anything with alpha == 0\n"
"\t\t\t\tdiscard;\n"
"\t\t}\n"
"\t\t\n"
"\t\t// If contour texture and not discarded, force alpha to 1.0 because will later be modified by polygon translucency\n"
"\t\tif (fsTexFormat < 0.5)\t\t// contour (T1RGB5) texture map\n"
"\t\t\tfragColor.a = 1.0;\n"
"\t}\n"
"\n"
"\t// Compute spotlight and apply lighting\n"
"\tellipse = (gl_FragCoord.xy-spotEllipse.xy)/spotEllipse.zw;\n"
"\tinsideSpot = dot(ellipse,ellipse);\n"
"\tif ((insideSpot <= 1.0) && (fsViewZ>=spotRange.x) && (fsViewZ<spotRange.y))\n"
"\t\tlightIntensity = fsLightIntensity+(1.0-insideSpot)*spotColor;\n"
"\telse\n"
"\t\tlightIntensity = fsLightIntensity;\n"
"\tfragColor.rgb *= lightIntensity;\n"
"\tfragColor.rgb += vec3(fsSpecularTerm,fsSpecularTerm,fsSpecularTerm);\n"
"\t\n"
"\t// Translucency (modulates existing alpha channel for RGBA4 texels)\n"
"\tfragColor.a *= fsTransLevel;\n"
"\n"
"\t// Apply fog under the control of fog factor setting from polygon header\n"
"\tfragColor.rgb = mix(gl_Fog.color.rgb, fragColor.rgb, fsFogFactor);\n"
"\n"
"\t// Store final color\n"
"\tgl_FragColor = fragColor;\n"
"}\n"
};
#endif // INCLUDED_SHADERS3D_H

View file

@ -1,329 +0,0 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel 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 Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* CTextureRefs.cpp
*
* Class that tracks unique texture references, eg in a cached model.
*
* Texture references are stored internally as a 27-bit field (3 bits for format, 6 bits each for x, y, width & height) to save space.
*
* A pre-allocated array is used for storing up to TEXREFS_ARRAY_SIZE texture references. When that limit is exceeded, it switches
* to using a hashset to store the texture references, but this requires extra memory allocation.
*/
#include "Supermodel.h"
CTextureRefs::CTextureRefs() : m_size(0), m_hashCapacity(0), m_hashEntries(NULL)
{
//
}
CTextureRefs::~CTextureRefs()
{
DeleteAllHashEntries();
}
unsigned CTextureRefs::GetSize() const
{
return m_size;
}
void CTextureRefs::Clear()
{
// Delete all hash entries
DeleteAllHashEntries();
m_size = 0;
m_hashCapacity = 0;
m_hashEntries = NULL;
}
bool CTextureRefs::ContainsRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height)
{
// Pack texture reference into bitfield
unsigned texRef = (fmt&7)<<24|(x&0x7E0)<<13|(y&0x7E0)<<7|(width&0x7E0)<<1|(height&0x7E0)>>5;
// Check if using array or hashset
if (m_size <= TEXREFS_ARRAY_SIZE)
{
// See if texture reference held in array
for (unsigned i = 0; i < m_size; i++)
{
if (texRef == m_array[i])
return true;
}
return false;
}
else
// See if texture reference held in hashset
return HashContains(texRef);
}
bool CTextureRefs::AddRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height)
{
// Pack texture reference into bitfield
unsigned texRef = (fmt&7)<<24|(x&0x7E0)<<13|(y&0x7E0)<<7|(width&0x7E0)<<1|(height&0x7E0)>>5;
// Check if using array or hashset
if (m_size <= TEXREFS_ARRAY_SIZE)
{
// See if already held in array, if so nothing to do
for (unsigned i = 0; i < m_size; i++)
{
if (texRef == m_array[i])
return true;
}
// If not, check if array is full
if (m_size == TEXREFS_ARRAY_SIZE)
{
// If so, set initial hashset capacity to 47 to initialize it
UpdateHashCapacity(47);
// Copy array into hashset
for (unsigned i = 0; i < TEXREFS_ARRAY_SIZE; i++)
AddToHash(m_array[i]);
// Add texture reference to hashset
AddToHash(texRef);
}
else
{
// Add texture reference to array
m_array[m_size] = texRef;
m_size++;
}
return true;
}
else
// Add texture reference to hashset
return AddToHash(texRef);
}
bool CTextureRefs::RemoveRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height)
{
// Pack texture reference into bitfield
unsigned texRef = (fmt&7)<<24|(x&0x7E0)<<13|(y&0x7E0)<<7|(width&0x7E0)<<1|(height&0x7E0)>>5;
// Check if using array or hashset
if (m_size <= TEXREFS_ARRAY_SIZE)
{
for (unsigned i = 0; i < m_size; i++)
{
if (texRef == m_array[i])
{
for (unsigned j = i + 1; j < m_size; j++)
m_array[j - 1] = m_array[j];
m_size--;
return true;
}
}
return false;
}
else
{
// Remove texture reference from hashset
bool removed = RemoveFromHash(texRef);
// See if should switch back to array
if (m_size == TEXREFS_ARRAY_SIZE)
{
// Loop through all hash entries and copy texture references into array
unsigned j = 0;
for (unsigned i = 0; i < m_hashCapacity; i++)
{
for (HashEntry *entry = m_hashEntries[i]; entry; entry = entry->nextEntry)
m_array[j++] = entry->texRef;
}
// Delete all hash entries
DeleteAllHashEntries();
}
return removed;
}
}
void CTextureRefs::DecodeAllTextures(CRender3D *Render3D)
{
// Check if using array or hashset
if (m_size <= TEXREFS_ARRAY_SIZE)
{
// Loop through elements in array and call CRender3D::DecodeTexture
for (unsigned i = 0; i < m_size; i++)
{
// Unpack texture reference from bitfield
unsigned texRef = m_array[i];
unsigned fmt = texRef>>24;
unsigned x = (texRef>>13)&0x7E0;
unsigned y = (texRef>>7)&0x7E0;
unsigned width = (texRef>>1)&0x7E0;
unsigned height = (texRef<<5)&0x7E0;
Render3D->DecodeTexture(fmt, x, y, width, height);
}
}
else
{
// Loop through all hash entriesa and call CRender3D::DecodeTexture
for (unsigned i = 0; i < m_hashCapacity; i++)
{
for (HashEntry *entry = m_hashEntries[i]; entry; entry = entry->nextEntry)
{
// Unpack texture reference from bitfield
unsigned texRef = entry->texRef;
unsigned fmt = texRef>>24;
unsigned x = (texRef>>13)&0x7E0;
unsigned y = (texRef>>7)&0x7E0;
unsigned width = (texRef>>1)&0x7E0;
unsigned height = (texRef<<5)&0x7E0;
Render3D->DecodeTexture(fmt, x, y, width, height);
}
}
}
}
bool CTextureRefs::UpdateHashCapacity(unsigned capacity)
{
unsigned oldCapacity = m_hashCapacity;
HashEntry **oldEntries = m_hashEntries;
// Update capacity and create new empty entries array
m_hashCapacity = capacity;
m_hashEntries = new(std::nothrow) HashEntry*[capacity];
if (!m_hashEntries)
return false;
memset(m_hashEntries, NULL, capacity * sizeof(HashEntry*));
if (oldEntries)
{
// Redistribute entries into new entries array
for (unsigned i = 0; i < oldCapacity; i++)
{
HashEntry *entry = oldEntries[i];
while (entry)
{
HashEntry *nextEntry = entry->nextEntry;
unsigned hash = entry->texRef % capacity;
entry->nextEntry = m_hashEntries[hash];
m_hashEntries[hash] = entry;
entry = nextEntry;
}
}
// Delete old entries array
delete[] oldEntries;
}
return true;
}
HashEntry *CTextureRefs::CreateHashEntry(unsigned texRef, bool &hashCapacityUpdated)
{
// Update size and increase hash capacity if required
m_size++;
hashCapacityUpdated = m_size >= m_hashCapacity;
if (hashCapacityUpdated)
{
if (m_hashCapacity < 89)
UpdateHashCapacity(89); // Capacity of 89 gives good sequence of mostly prime capacities (89, 179, 359, 719, 1439, 2879 etc)
else
UpdateHashCapacity(2 * m_hashCapacity + 1);
}
return new(std::nothrow) HashEntry(texRef);
}
void CTextureRefs::DeleteHashEntry(HashEntry *entry)
{
// Update size and delete hash entry
m_size--;
delete entry;
}
void CTextureRefs::DeleteAllHashEntries()
{
if (!m_hashEntries)
return;
// Delete all hash entries and their storage
for (unsigned i = 0; i < m_hashCapacity; i++)
{
HashEntry *entry = m_hashEntries[i];
if (entry)
delete entry;
}
delete[] m_hashEntries;
}
bool CTextureRefs::AddToHash(unsigned texRef)
{
// Convert texture reference to hash value
unsigned hash = texRef % m_hashCapacity;
// Loop through linked list for hash value and see if have texture reference already
HashEntry *headEntry = m_hashEntries[hash];
HashEntry *entry = headEntry;
while (entry && texRef != entry->texRef)
entry = entry->nextEntry;
// If found, nothing to do
if (entry)
return true;
// Otherwise, create new hash entry for texture reference
bool hashCapacityUpdated;
entry = CreateHashEntry(texRef, hashCapacityUpdated);
// If couldn't create entry (ie out of memory), let caller know
if (!entry)
return false;
if (hashCapacityUpdated)
{
// If hash capacity was increased recalculate hash value
hash = texRef % m_hashCapacity;
headEntry = m_hashEntries[hash];
}
// Store hash entry in linked list for hash value
entry->nextEntry = headEntry;
m_hashEntries[hash] = entry;
return true;
}
bool CTextureRefs::RemoveFromHash(unsigned texRef)
{
// Convert texture reference to hash value
unsigned hash = texRef % m_hashCapacity;
// Loop through linked list for hash value and see if have texture reference
HashEntry *entry = m_hashEntries[hash];
HashEntry *prevEntry = NULL;
while (entry && texRef != entry->texRef)
{
prevEntry = entry;
entry = entry->nextEntry;
}
// If not found, nothing to do
if (!entry)
return false;
// Otherwise, remove entry from linked list for hash value
if (prevEntry)
prevEntry->nextEntry = entry->nextEntry;
else
m_hashEntries[hash] = entry->nextEntry;
// Delete hash entry storage
DeleteHashEntry(entry);
return true;
}
bool CTextureRefs::HashContains(unsigned texRef) const
{
// Convert texture reference to hash value
unsigned hash = texRef % m_hashCapacity;
// Loop through linked list for hash value and see if have texture reference
HashEntry *entry = m_hashEntries[hash];
while (entry && texRef != entry->texRef)
entry = entry->nextEntry;
return !!entry;
}

View file

@ -1,164 +0,0 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel 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 Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* CTextureRefs.h
*
* Class that tracks unique texture references, eg in a cached model.
*/
#ifndef INCLUDED_TEXTUREREFS_H
#define INCLUDED_TEXTUREREFS_H
#define TEXREFS_ARRAY_SIZE 12
// Hash entry that holds a texture reference in the hashset
struct HashEntry
{
const unsigned texRef; // Texture reference as a bitfield
HashEntry *nextEntry; // Next entry with the same hash
HashEntry(unsigned theTexRef) : texRef(theTexRef), nextEntry(NULL) { }
};
class CRender3D;
class CTextureRefs
{
public:
/*
* CTextureRefs():
*
* Constructor.
*/
CTextureRefs();
/*
* ~CTextureRefs():
*
* Destructor.
*/
~CTextureRefs();
/*
* GetSize():
*
* Returns number of unique texture references held.
*/
unsigned GetSize() const;
/*
* Clear():
*
* Removes all texture references.
*/
void Clear();
/*
* ContainsRef(fmt, x, y, width, height):
*
* Returns true if holds the given texture reference.
*/
bool ContainsRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height);
/*
* AddRef(fmt, x, y, width, height):
*
* Adds the given texture reference. Returns false if it was not possible to add the reference (ie out of memory).
*/
bool AddRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height);
/*
* RemoveRef(fmt, x, y, width, height):
*
* Removes the given texture reference. Return true if the reference was found.
*/
bool RemoveRef(unsigned fmt, unsigned x, unsigned y, unsigned width, unsigned height);
/*
* RemoveRef(fmt, x, y, width, height):
*
* Decodes all texture references held, calling CRender3D::DecodeTexture for each one.
*/
void DecodeAllTextures(CRender3D *Render3D);
private:
// Number of texture references held.
unsigned m_size;
// Pre-allocated array used to hold first TEXREFS_ARRAY_SIZE texture references.
unsigned m_array[TEXREFS_ARRAY_SIZE];
// Dynamically allocated hashset used to hold texture references when there are more than TEXREFS_ARRAY_SIZE.
unsigned m_hashCapacity;
HashEntry **m_hashEntries;
/*
* UpdateHashCapacity(hashCapacity)
*
* Increases capacity of the hashset to given size.
*/
bool UpdateHashCapacity(unsigned hashCapacity);
/*
* CreateHashEntry(texRef, hashCapacityUpdated)
*
* Creates and returns a new hash entry, updating the capacity if required (hashCapacityUpdated is set to true).
*/
HashEntry *CreateHashEntry(unsigned texRef, bool &hashCapacityUpdated);
/*
* DeleteHashEntry(entry)
*
* Deletes the given hash entry and its storage.
*/
void DeleteHashEntry(HashEntry *entry);
/*
* DeleteAllHashEntries()
*
* Deletes all hash entries and their storage.
*/
void DeleteAllHashEntries();
/*
* AddToHash(texRef)
*
* Adds the given texture reference (as a bitfield) to the hashset.
*/
bool AddToHash(unsigned texRef);
/*
* RemoveFromHash(texRef)
*
* Removes the given texture reference (as a bitfield) from the hashset.
*/
bool RemoveFromHash(unsigned texRef);
/*
* HashContains(texRef)
*
* Returns true if given texture reference (as a bitfield) is held in the hashset.
*/
bool HashContains(unsigned texRef) const;
};
#endif // INCLUDED_TEXTUREREFS_H