mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2025-02-16 17:35:39 +00:00
Removing legacy 3D engine files that were already moved to Graphics/Legacy3D
This commit is contained in:
parent
34301e97e1
commit
d61b01ab6f
|
@ -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
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
Loading…
Reference in a new issue