2016-03-21 04:10:14 +00:00
# include " New3D.h "
# include "PolyHeader.h"
# include "Texture.h"
# include "Vec.h"
2016-05-19 19:04:44 +00:00
# include <cmath>
2016-05-09 16:26:34 +00:00
# include <algorithm>
2016-05-10 03:08:36 +00:00
# include <limits>
2016-03-21 04:10:14 +00:00
2016-03-26 22:44:26 +00:00
# define MAX_RAM_POLYS 100000
# define MAX_ROM_POLYS 500000
2016-03-21 04:10:14 +00:00
# ifndef M_PI
# define M_PI 3.14159265359
# endif
namespace New3D {
CNew3D : : CNew3D ( )
{
m_cullingRAMLo = nullptr ;
m_cullingRAMHi = nullptr ;
m_polyRAM = nullptr ;
m_vrom = nullptr ;
m_textureRAM = nullptr ;
}
CNew3D : : ~ CNew3D ( )
{
2016-03-26 22:44:26 +00:00
m_vbo . Destroy ( ) ;
2016-03-21 04:10:14 +00:00
}
void CNew3D : : AttachMemory ( const UINT32 * cullingRAMLoPtr , const UINT32 * cullingRAMHiPtr , const UINT32 * polyRAMPtr , const UINT32 * vromPtr , const UINT16 * textureRAMPtr )
{
m_cullingRAMLo = cullingRAMLoPtr ;
m_cullingRAMHi = cullingRAMHiPtr ;
m_polyRAM = polyRAMPtr ;
m_vrom = vromPtr ;
m_textureRAM = textureRAMPtr ;
}
void CNew3D : : SetStep ( int stepID )
{
m_step = stepID ;
if ( ( m_step ! = 0x10 ) & & ( m_step ! = 0x15 ) & & ( m_step ! = 0x20 ) & & ( m_step ! = 0x21 ) ) {
m_step = 0x10 ;
}
if ( m_step > 0x10 ) {
m_offset = 0 ; // culling nodes are 10 words
m_vertexFactor = ( 1.0f / 2048.0f ) ; // vertices are in 13.11 format
}
else {
m_offset = 2 ; // 8 words
m_vertexFactor = ( 1.0f / 128.0f ) ; // 17.7
}
2016-03-26 22:44:26 +00:00
m_vbo . Create ( GL_ARRAY_BUFFER , GL_DYNAMIC_DRAW , sizeof ( Poly ) * ( MAX_RAM_POLYS + MAX_ROM_POLYS ) ) ;
2016-03-21 04:10:14 +00:00
}
bool CNew3D : : Init ( unsigned xOffset , unsigned yOffset , unsigned xRes , unsigned yRes , unsigned totalXResParam , unsigned totalYResParam )
{
// Resolution and offset within physical display area
m_xRatio = xRes / 496.0f ;
m_yRatio = yRes / 384.0f ;
m_xOffs = xOffset ;
m_yOffs = yOffset ;
m_totalXRes = totalXResParam ;
m_totalYRes = totalYResParam ;
m_r3dShader . LoadShader ( ) ;
glUseProgram ( 0 ) ;
return OKAY ; // OKAY ? wtf ..
}
void CNew3D : : UploadTextures ( unsigned x , unsigned y , unsigned width , unsigned height )
{
m_texSheet . Invalidate ( x , y , width , height ) ;
}
void CNew3D : : RenderScene ( int priority , bool alpha )
{
if ( alpha ) {
glEnable ( GL_BLEND ) ;
}
for ( auto & n : m_nodes ) {
2016-03-22 23:39:59 +00:00
if ( n . viewport . priority ! = priority | | n . models . empty ( ) ) {
2016-03-21 04:10:14 +00:00
continue ;
}
2016-03-22 23:39:59 +00:00
glViewport ( n . viewport . x , n . viewport . y , n . viewport . width , n . viewport . height ) ;
glMatrixMode ( GL_PROJECTION ) ;
glLoadMatrixf ( n . viewport . projectionMatrix ) ;
glMatrixMode ( GL_MODELVIEW ) ;
2016-03-21 04:10:14 +00:00
m_r3dShader . SetViewportUniforms ( & n . viewport ) ;
for ( auto & m : n . models ) {
2016-03-22 23:39:59 +00:00
bool matrixLoaded = false ;
2016-03-26 22:44:26 +00:00
if ( m . meshes - > empty ( ) ) {
2016-03-21 04:10:14 +00:00
continue ;
}
2016-03-22 23:39:59 +00:00
m_r3dShader . SetModelStates ( & m ) ;
2016-03-21 04:10:14 +00:00
2016-03-26 22:44:26 +00:00
for ( auto & mesh : * m . meshes ) {
2016-03-21 04:10:14 +00:00
if ( alpha ) {
if ( ! mesh . textureAlpha & & ! mesh . polyAlpha ) {
continue ;
}
}
else {
if ( mesh . textureAlpha | | mesh . polyAlpha ) {
continue ;
}
}
2016-03-22 23:39:59 +00:00
if ( ! matrixLoaded ) {
glLoadMatrixf ( m . modelMat ) ;
matrixLoaded = true ; // do this here to stop loading matrices we don't need. Ie when rendering non transparent etc
}
2016-03-21 04:10:14 +00:00
2016-03-26 22:44:26 +00:00
if ( mesh . textured ) {
2016-05-30 13:25:00 +00:00
int x , y ;
CalcTexOffset ( m . textureOffsetX , m . textureOffsetY , m . page , mesh . x , mesh . y , x , y ) ;
auto tex1 = m_texSheet . BindTexture ( m_textureRAM , mesh . format , mesh . mirrorU , mesh . mirrorV , x , y , mesh . width , mesh . height ) ;
2016-05-04 00:35:07 +00:00
if ( tex1 ) {
tex1 - > BindTexture ( ) ;
tex1 - > SetWrapMode ( mesh . mirrorU , mesh . mirrorV ) ;
}
if ( mesh . microTexture ) {
glActiveTexture ( GL_TEXTURE1 ) ;
2016-05-04 10:30:58 +00:00
auto tex2 = m_texSheet . BindTexture ( m_textureRAM , 0 , false , false , 0 , 1024 , 128 , 128 ) ;
2016-05-04 00:35:07 +00:00
if ( tex2 ) {
tex2 - > BindTexture ( ) ;
}
glActiveTexture ( GL_TEXTURE0 ) ;
2016-03-26 22:44:26 +00:00
}
2016-03-21 04:10:14 +00:00
}
m_r3dShader . SetMeshUniforms ( & mesh ) ;
glDrawArrays ( GL_TRIANGLES , mesh . vboOffset * 3 , mesh . triangleCount * 3 ) ; // times 3 to convert triangles to vertices
}
}
}
glDisable ( GL_BLEND ) ;
glDepthMask ( GL_TRUE ) ;
2016-05-27 19:30:40 +00:00
glDisable ( GL_STENCIL_TEST ) ;
2016-03-21 04:10:14 +00:00
}
void CNew3D : : RenderFrame ( void )
{
// release any resources from last frame
2016-03-26 22:44:26 +00:00
m_polyBufferRam . clear ( ) ; // clear dyanmic model memory buffer
2016-03-21 04:10:14 +00:00
m_nodes . clear ( ) ; // memory will grow during the object life time, that's fine, no need to shrink to fit
m_modelMat . Release ( ) ; // would hope we wouldn't need this but no harm in checking
m_nodeAttribs . Reset ( ) ;
glDepthFunc ( GL_LEQUAL ) ;
glEnable ( GL_DEPTH_TEST ) ;
glActiveTexture ( GL_TEXTURE0 ) ;
glEnable ( GL_CULL_FACE ) ;
glFrontFace ( GL_CW ) ;
2016-05-27 19:30:40 +00:00
glStencilFunc ( GL_EQUAL , 0 , 0xFF ) ; // basically stencil test passes if the value is zero
glStencilOp ( GL_KEEP , GL_INCR , GL_INCR ) ; // if the stencil test passes, we incriment the value
glStencilMask ( 0xFF ) ;
2016-04-19 22:05:12 +00:00
RenderViewport ( 0x800000 ) ; // build model structure
2016-03-26 22:44:26 +00:00
m_vbo . Bind ( true ) ;
m_vbo . BufferSubData ( MAX_ROM_POLYS * sizeof ( Poly ) , m_polyBufferRam . size ( ) * sizeof ( Poly ) , m_polyBufferRam . data ( ) ) ; // upload all the dynamic data to GPU in one go
if ( m_polyBufferRom . size ( ) ) {
2016-03-21 04:10:14 +00:00
2016-03-26 22:44:26 +00:00
// sync rom memory with vbo
int romBytes = ( int ) m_polyBufferRom . size ( ) * sizeof ( Poly ) ;
int vboBytes = m_vbo . GetSize ( ) ;
int size = romBytes - vboBytes ;
if ( size ) {
//check we haven't blown up the memory buffers
//we will lose rom models for 1 frame is this happens, not the end of the world, as probably won't ever happen anyway
if ( m_polyBufferRom . size ( ) > = MAX_ROM_POLYS ) {
m_polyBufferRom . clear ( ) ;
2016-03-26 22:48:51 +00:00
m_romMap . clear ( ) ;
2016-03-26 22:44:26 +00:00
m_vbo . Reset ( ) ;
}
else {
m_vbo . AppendData ( size , & m_polyBufferRom [ vboBytes / sizeof ( Poly ) ] ) ;
}
}
}
2016-03-21 04:10:14 +00:00
glEnableClientState ( GL_VERTEX_ARRAY ) ;
glEnableClientState ( GL_NORMAL_ARRAY ) ;
glEnableClientState ( GL_TEXTURE_COORD_ARRAY ) ;
glEnableClientState ( GL_COLOR_ARRAY ) ;
// before draw, specify vertex and index arrays with their offsets, offsetof is maybe evil ..
glVertexPointer ( 3 , GL_FLOAT , sizeof ( Vertex ) , 0 ) ;
glNormalPointer ( GL_FLOAT , sizeof ( Vertex ) , ( void * ) offsetof ( Vertex , normal ) ) ;
glTexCoordPointer ( 2 , GL_FLOAT , sizeof ( Vertex ) , ( void * ) offsetof ( Vertex , texcoords ) ) ;
glColorPointer ( 4 , GL_UNSIGNED_BYTE , sizeof ( Vertex ) , ( void * ) offsetof ( Vertex , color ) ) ;
m_r3dShader . SetShader ( true ) ;
for ( int pri = 0 ; pri < = 3 ; pri + + ) {
2016-05-27 19:30:40 +00:00
glClear ( GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ) ;
2016-03-21 04:10:14 +00:00
RenderScene ( pri , false ) ;
RenderScene ( pri , true ) ;
}
m_r3dShader . SetShader ( false ) ; // unbind shader
2016-03-26 22:44:26 +00:00
m_vbo . Bind ( false ) ;
2016-03-21 04:10:14 +00:00
glDisable ( GL_CULL_FACE ) ;
glDisableClientState ( GL_VERTEX_ARRAY ) ;
glDisableClientState ( GL_NORMAL_ARRAY ) ;
glEnableClientState ( GL_TEXTURE_COORD_ARRAY ) ;
glDisableClientState ( GL_COLOR_ARRAY ) ;
}
void CNew3D : : BeginFrame ( void )
{
}
void CNew3D : : EndFrame ( void )
{
}
/******************************************************************************
Real3D Address Translation
Functions that interpret word - granular Real3D addresses and return pointers .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Translates 24-bit culling RAM addresses
const UINT32 * CNew3D : : TranslateCullingAddress ( UINT32 addr )
{
addr & = 0x00FFFFFF ; // caller should have done this already
if ( ( addr > = 0x800000 ) & & ( addr < 0x840000 ) ) {
return & m_cullingRAMHi [ addr & 0x3FFFF ] ;
}
else if ( addr < 0x100000 ) {
return & m_cullingRAMLo [ addr ] ;
}
return NULL ;
}
// Translates model references
const UINT32 * CNew3D : : TranslateModelAddress ( UINT32 modelAddr )
{
modelAddr & = 0x00FFFFFF ; // caller should have done this already
if ( modelAddr < 0x100000 ) {
return & m_polyRAM [ modelAddr ] ;
}
else {
return & m_vrom [ modelAddr ] ;
}
}
bool CNew3D : : DrawModel ( UINT32 modelAddr )
{
2016-03-26 22:44:26 +00:00
const UINT32 * modelAddress ;
bool cached = false ;
2016-03-21 04:10:14 +00:00
Model * m ;
modelAddress = TranslateModelAddress ( modelAddr ) ;
// create a new model to push onto the vector
m_nodes . back ( ) . models . emplace_back ( Model ( ) ) ;
2016-03-26 22:44:26 +00:00
// get the last model in the array
2016-03-21 04:10:14 +00:00
m = & m_nodes . back ( ) . models . back ( ) ;
2016-03-26 22:44:26 +00:00
if ( IsVROMModel ( modelAddr ) & & ! IsDynamicModel ( ( UINT32 * ) modelAddress ) ) {
// try to find meshes in the rom cache
2016-05-19 19:04:44 +00:00
m - > meshes = m_romMap [ modelAddr ] ; // will create an entry with a null pointer if empty
2016-03-28 19:58:36 +00:00
2016-05-19 19:04:44 +00:00
if ( m - > meshes ) {
2016-03-26 22:44:26 +00:00
cached = true ;
}
else {
m - > meshes = std : : make_shared < std : : vector < Mesh > > ( ) ;
m_romMap [ modelAddr ] = m - > meshes ; // store meshes in our rom map here
}
2016-03-28 19:58:36 +00:00
m - > dynamic = false ;
2016-03-26 22:44:26 +00:00
}
else {
m - > meshes = std : : make_shared < std : : vector < Mesh > > ( ) ;
}
2016-03-21 04:10:14 +00:00
2016-03-26 22:44:26 +00:00
// copy current model matrix
2016-03-21 04:10:14 +00:00
for ( int i = 0 ; i < 16 ; i + + ) {
m - > modelMat [ i ] = m_modelMat . currentMatrix [ i ] ;
}
2016-03-22 23:39:59 +00:00
//calculate determinant
m - > determinant = Determinant3x3 ( m_modelMat ) ;
2016-03-26 22:44:26 +00:00
// update texture offsets
m - > textureOffsetX = m_nodeAttribs . currentTexOffsetX ;
m - > textureOffsetY = m_nodeAttribs . currentTexOffsetY ;
2016-05-30 13:25:00 +00:00
m - > page = m_nodeAttribs . page ;
2016-03-26 22:44:26 +00:00
if ( ! cached ) {
CacheModel ( m , modelAddress ) ;
}
2016-03-21 04:10:14 +00:00
return true ;
}
// Descends into a 10-word culling node
void CNew3D : : DescendCullingNode ( UINT32 addr )
{
const UINT32 * node , * lodTable ;
2016-05-19 22:23:50 +00:00
UINT32 matrixOffset , child1Ptr , sibling2Ptr ;
2016-03-21 04:10:14 +00:00
float x , y , z ;
int tx , ty ;
if ( m_nodeAttribs . StackLimit ( ) ) {
return ;
}
node = TranslateCullingAddress ( addr ) ;
if ( NULL = = node ) {
return ;
}
// Extract known fields
2016-05-19 22:23:50 +00:00
child1Ptr = node [ 0x07 - m_offset ] & 0x7FFFFFF ; // mask colour table bits
sibling2Ptr = node [ 0x08 - m_offset ] & 0x1FFFFFF ; // mask colour table bits
2016-03-21 04:10:14 +00:00
matrixOffset = node [ 0x03 - m_offset ] & 0xFFF ;
2016-06-03 00:01:34 +00:00
float test = ToFloat ( Convert16BitProFloat ( node [ 9 - m_offset ] > > 16 ) ) ;
2016-05-13 19:26:10 +00:00
2016-05-19 22:23:50 +00:00
if ( ( node [ 0x00 ] & 0x07 ) ! = 0x06 ) { // colour table seems to indicate no siblings
if ( ! ( sibling2Ptr & 0x1000000 ) & & sibling2Ptr ) {
DescendCullingNode ( sibling2Ptr ) ; // no need to mask bit, would already be zero
}
2016-04-18 22:25:05 +00:00
}
2016-05-18 23:06:41 +00:00
if ( ( node [ 0x00 ] & 0x04 ) ) {
m_colorTableAddr = ( ( node [ 0x03 - m_offset ] > > 19 ) < < 0 ) | ( ( node [ 0x07 - m_offset ] > > 28 ) < < 13 ) | ( ( node [ 0x08 - m_offset ] > > 25 ) < < 17 ) ;
m_colorTableAddr & = 0x000FFFFF ; // clamp to 4MB (in words) range
}
2016-05-19 22:23:50 +00:00
x = * ( float * ) & node [ 0x04 - m_offset ] ;
2016-03-21 04:10:14 +00:00
y = * ( float * ) & node [ 0x05 - m_offset ] ;
z = * ( float * ) & node [ 0x06 - m_offset ] ;
m_nodeAttribs . Push ( ) ; // save current attribs
if ( ! m_offset ) // Step 1.5+
{
tx = 32 * ( ( node [ 0x02 ] > > 7 ) & 0x3F ) ;
2016-05-30 13:25:00 +00:00
ty = 32 * ( node [ 0x02 ] & 0x1F ) ;
2016-03-21 04:10:14 +00:00
// apply texture offsets, else retain current ones
if ( ( node [ 0x02 ] & 0x8000 ) ) {
m_nodeAttribs . currentTexOffsetX = tx ;
m_nodeAttribs . currentTexOffsetY = ty ;
2016-05-30 13:25:00 +00:00
m_nodeAttribs . page = ( node [ 0x02 ] & 0x4000 ) > > 14 ;
2016-03-21 04:10:14 +00:00
}
}
// Apply matrix and translation
m_modelMat . PushMatrix ( ) ;
// apply translation vector
if ( ( node [ 0x00 ] & 0x10 ) ) {
m_modelMat . Translate ( x , y , z ) ;
}
// multiply matrix, if specified
else if ( matrixOffset ) {
MultMatrix ( matrixOffset , m_modelMat ) ;
}
// Descend down first link
if ( ( node [ 0x00 ] & 0x08 ) ) // 4-element LOD table
{
2016-05-19 22:23:50 +00:00
lodTable = TranslateCullingAddress ( child1Ptr ) ;
2016-03-21 04:10:14 +00:00
if ( NULL ! = lodTable ) {
if ( ( node [ 0x03 - m_offset ] & 0x20000000 ) ) {
DescendCullingNode ( lodTable [ 0 ] & 0xFFFFFF ) ;
}
else {
DrawModel ( lodTable [ 0 ] & 0xFFFFFF ) ; //TODO
}
}
}
else {
2016-05-19 22:23:50 +00:00
DescendNodePtr ( child1Ptr ) ;
2016-03-21 04:10:14 +00:00
}
m_modelMat . PopMatrix ( ) ;
// Restore old texture offsets
m_nodeAttribs . Pop ( ) ;
}
void CNew3D : : DescendNodePtr ( UINT32 nodeAddr )
{
// Ignore null links
if ( ( nodeAddr & 0x00FFFFFF ) = = 0 ) {
return ;
}
switch ( ( nodeAddr > > 24 ) & 0xFF ) // pointer type encoded in upper 8 bits
{
case 0x00 : // culling node
DescendCullingNode ( nodeAddr & 0xFFFFFF ) ;
break ;
2016-04-18 14:06:10 +00:00
case 0x01 : // model (perhaps bit 2 is a flag in this case?)
2016-03-21 04:10:14 +00:00
case 0x03 :
DrawModel ( nodeAddr & 0xFFFFFF ) ;
break ;
case 0x04 : // pointer list
DescendPointerList ( nodeAddr & 0xFFFFFF ) ;
break ;
default :
break ;
}
}
void CNew3D : : DescendPointerList ( UINT32 addr )
{
const UINT32 * list ;
UINT32 nodeAddr ;
2016-04-17 00:00:51 +00:00
int index ;
2016-03-21 04:10:14 +00:00
list = TranslateCullingAddress ( addr ) ;
if ( NULL = = list ) {
return ;
}
2016-04-17 00:00:51 +00:00
index = 0 ;
2016-03-21 04:10:14 +00:00
2016-04-17 00:00:51 +00:00
while ( true ) {
2016-03-21 04:10:14 +00:00
2016-04-17 00:00:51 +00:00
if ( list [ index ] & 0x01000000 ) {
break ; // empty list
2016-03-21 04:10:14 +00:00
}
2016-04-17 00:00:51 +00:00
nodeAddr = list [ index ] & 0x00FFFFFF ; // clear upper 8 bits to ensure this is processed as a culling node
2016-03-21 04:10:14 +00:00
2016-04-17 00:00:51 +00:00
DescendCullingNode ( nodeAddr ) ;
2016-03-21 04:10:14 +00:00
2016-04-17 00:00:51 +00:00
if ( list [ index ] & 0x02000000 ) {
break ; // list end
2016-03-21 04:10:14 +00:00
}
2016-04-17 00:00:51 +00:00
index + + ;
}
2016-03-21 04:10:14 +00:00
}
/******************************************************************************
Matrix Stack
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Macro to generate column-major (OpenGL) index from y,x subscripts
# define CMINDEX(y,x) (x*4+y)
/*
* MultMatrix ( ) :
*
* Multiplies the matrix stack by the specified Real3D matrix . The matrix
* index is a 12 - bit number specifying a matrix number relative to the base .
* The base matrix MUST be set up before calling this function .
*/
void CNew3D : : MultMatrix ( UINT32 matrixOffset , Mat4 & mat )
{
GLfloat m [ 4 * 4 ] ;
const float * src = & m_matrixBasePtr [ matrixOffset * 12 ] ;
if ( m_matrixBasePtr = = NULL ) // LA Machineguns
return ;
m [ CMINDEX ( 0 , 0 ) ] = src [ 3 ] ;
m [ CMINDEX ( 0 , 1 ) ] = src [ 4 ] ;
m [ CMINDEX ( 0 , 2 ) ] = src [ 5 ] ;
m [ CMINDEX ( 0 , 3 ) ] = src [ 0 ] ;
m [ CMINDEX ( 1 , 0 ) ] = src [ 6 ] ;
m [ CMINDEX ( 1 , 1 ) ] = src [ 7 ] ;
m [ CMINDEX ( 1 , 2 ) ] = src [ 8 ] ;
m [ CMINDEX ( 1 , 3 ) ] = src [ 1 ] ;
m [ CMINDEX ( 2 , 0 ) ] = src [ 9 ] ;
m [ CMINDEX ( 2 , 1 ) ] = src [ 10 ] ;
m [ CMINDEX ( 2 , 2 ) ] = src [ 11 ] ;
m [ CMINDEX ( 2 , 3 ) ] = src [ 2 ] ;
m [ CMINDEX ( 3 , 0 ) ] = 0.0 ;
m [ CMINDEX ( 3 , 1 ) ] = 0.0 ;
m [ CMINDEX ( 3 , 2 ) ] = 0.0 ;
m [ CMINDEX ( 3 , 3 ) ] = 1.0 ;
mat . MultMatrix ( m ) ;
}
/*
* InitMatrixStack ( ) :
*
* Initializes the modelview ( model space - > view space ) matrix stack and
* Real3D coordinate system . These are the last transforms to be applied ( and
* the first to be defined on the stack ) before projection .
*
* Model 3 games tend to define the following unusual base matrix :
*
* 0 0 - 1 0
* 1 0 0 0
* 0 - 1 0 0
* 0 0 0 1
*
* When this is multiplied by a column vector , the output is :
*
* - Z
* X
* - Y
* 1
*
* My theory is that the Real3D GPU accepts vectors in Z , X , Y order . The games
* store everything as X , Y , Z and perform the translation at the end . The Real3D
* also has Y and Z coordinates opposite of the OpenGL convention . This
* function inserts a compensating matrix to undo these things .
*
* NOTE : This function assumes we are in GL_MODELVIEW matrix mode .
*/
void CNew3D : : InitMatrixStack ( UINT32 matrixBaseAddr , Mat4 & mat )
{
GLfloat m [ 4 * 4 ] ;
// This matrix converts vectors back from the weird Model 3 Z,X,Y ordering
// and also into OpenGL viewspace (-Y,-Z)
m [ CMINDEX ( 0 , 0 ) ] = 0.0 ; m [ CMINDEX ( 0 , 1 ) ] = 1.0 ; m [ CMINDEX ( 0 , 2 ) ] = 0.0 ; m [ CMINDEX ( 0 , 3 ) ] = 0.0 ;
m [ CMINDEX ( 1 , 0 ) ] = 0.0 ; m [ CMINDEX ( 1 , 1 ) ] = 0.0 ; m [ CMINDEX ( 1 , 2 ) ] = - 1.0 ; m [ CMINDEX ( 1 , 3 ) ] = 0.0 ;
m [ CMINDEX ( 2 , 0 ) ] = - 1.0 ; m [ CMINDEX ( 2 , 1 ) ] = 0.0 ; m [ CMINDEX ( 2 , 2 ) ] = 0.0 ; m [ CMINDEX ( 2 , 3 ) ] = 0.0 ;
m [ CMINDEX ( 3 , 0 ) ] = 0.0 ; m [ CMINDEX ( 3 , 1 ) ] = 0.0 ; m [ CMINDEX ( 3 , 2 ) ] = 0.0 ; m [ CMINDEX ( 3 , 3 ) ] = 1.0 ;
2016-04-08 23:29:31 +00:00
mat . LoadMatrix ( m ) ;
2016-03-21 04:10:14 +00:00
// Set matrix base address and apply matrix #0 (coordinate system matrix)
m_matrixBasePtr = ( float * ) TranslateCullingAddress ( matrixBaseAddr ) ;
MultMatrix ( 0 , mat ) ;
}
// Draws viewports of the given priority
2016-04-19 22:05:12 +00:00
void CNew3D : : RenderViewport ( UINT32 addr )
2016-03-21 04:10:14 +00:00
{
2016-05-05 22:01:59 +00:00
static const GLfloat color [ 8 ] [ 3 ] =
{ // RGB1 color translation
2016-03-21 04:10:14 +00:00
{ 0.0 , 0.0 , 0.0 } , // off
{ 0.0 , 0.0 , 1.0 } , // blue
{ 0.0 , 1.0 , 0.0 } , // green
{ 0.0 , 1.0 , 1.0 } , // cyan
{ 1.0 , 0.0 , 0.0 } , // red
{ 1.0 , 0.0 , 1.0 } , // purple
{ 1.0 , 1.0 , 0.0 } , // yellow
2016-05-13 19:26:10 +00:00
{ 1.0 , 1.0 , 1.0 } // white
2016-03-21 04:10:14 +00:00
} ;
// Translate address and obtain pointer
2016-05-05 22:01:59 +00:00
const uint32_t * vpnode = TranslateCullingAddress ( addr ) ;
2016-03-21 04:10:14 +00:00
if ( NULL = = vpnode ) {
return ;
}
2016-05-05 22:01:59 +00:00
if ( vpnode [ 0x01 ] = = 0 ) { // memory probably hasn't been set up yet, abort
2016-03-21 04:10:14 +00:00
return ;
}
2016-05-05 22:01:59 +00:00
if ( ! ( vpnode [ 0 ] & 0x20 ) ) { // only if viewport enabled
uint32_t curPri = ( vpnode [ 0x00 ] > > 3 ) & 3 ; // viewport priority
uint32_t nodeAddr = vpnode [ 0x02 ] ; // scene database node pointer
2016-05-03 21:17:37 +00:00
2016-05-05 22:01:59 +00:00
// create node object
m_nodes . emplace_back ( Node ( ) ) ;
m_nodes . back ( ) . models . reserve ( 2048 ) ; // create space for models
2016-03-21 04:10:14 +00:00
2016-05-05 22:01:59 +00:00
// get pointer to its viewport
Viewport * vp = & m_nodes . back ( ) . viewport ;
2016-03-21 04:10:14 +00:00
2016-05-05 22:01:59 +00:00
vp - > priority = curPri ;
2016-03-21 04:10:14 +00:00
2016-05-05 22:01:59 +00:00
// Fetch viewport parameters (TO-DO: would rounding make a difference?)
int vpX = ( int ) ( ( ( vpnode [ 0x1A ] & 0xFFFF ) / 16.0f ) + 0.5f ) ; // viewport X (12.4 fixed point)
int vpY = ( int ) ( ( ( vpnode [ 0x1A ] > > 16 ) / 16.0f ) + 0.5f ) ; // viewport Y (12.4)
int vpWidth = ( int ) ( ( ( vpnode [ 0x14 ] & 0xFFFF ) / 4.0f ) + 0.5f ) ; // width (14.2)
int vpHeight = ( int ) ( ( ( vpnode [ 0x14 ] > > 16 ) / 4.0f ) + 0.5f ) ; // height (14.2)
2016-05-13 19:26:10 +00:00
uint32_t matrixBase = vpnode [ 0x16 ] & 0xFFFFFF ; // matrix base address
2016-03-21 04:10:14 +00:00
2016-05-05 22:01:59 +00:00
if ( vpX ) {
vpX + = 2 ;
}
2016-03-21 04:10:14 +00:00
2016-05-05 22:01:59 +00:00
if ( vpY ) {
vpY + = 2 ;
}
2016-04-02 22:13:59 +00:00
2016-05-05 22:01:59 +00:00
LODBlendTable * tableTest = ( LODBlendTable * ) TranslateCullingAddress ( vpnode [ 0x17 ] ) ;
2016-04-02 22:13:59 +00:00
2016-05-05 22:01:59 +00:00
float angle_left = - atan2 ( * ( float * ) & vpnode [ 12 ] , * ( float * ) & vpnode [ 13 ] ) ;
float angle_right = atan2 ( * ( float * ) & vpnode [ 16 ] , - * ( float * ) & vpnode [ 17 ] ) ;
float angle_top = atan2 ( * ( float * ) & vpnode [ 14 ] , * ( float * ) & vpnode [ 15 ] ) ;
float angle_bottom = - atan2 ( * ( float * ) & vpnode [ 18 ] , - * ( float * ) & vpnode [ 19 ] ) ;
2016-03-28 20:11:46 +00:00
2016-05-05 22:01:59 +00:00
float near = 0.25f ;
float far = 2e6 ;
2016-03-21 04:10:14 +00:00
2016-05-05 22:01:59 +00:00
if ( m_step = = 0x10 ) {
near = 8 ;
}
2016-04-08 23:29:31 +00:00
2016-05-05 22:01:59 +00:00
float l = near * tanf ( angle_left ) ;
float r = near * tanf ( angle_right ) ;
float t = near * tanf ( angle_top ) ;
float b = near * tanf ( angle_bottom ) ;
2016-03-31 09:27:42 +00:00
2016-05-05 22:01:59 +00:00
// TO-DO: investigate clipping near/far planes
2016-03-31 09:27:42 +00:00
2016-05-05 22:01:59 +00:00
if ( ( vpX = = 0 ) & & ( vpWidth > = 495 ) & & ( vpY = = 0 ) & & ( vpHeight > = 383 ) )
{
float windowAR = ( float ) m_totalXRes / ( float ) m_totalYRes ;
float originalAR = 496 / 384.f ;
float correction = windowAR / originalAR ; // expand horizontal frustum planes
2016-03-21 04:10:14 +00:00
2016-05-05 22:01:59 +00:00
vp - > x = 0 ;
vp - > y = m_yOffs + ( GLint ) ( ( float ) ( 384 - ( vpY + vpHeight ) ) * m_yRatio ) ;
vp - > width = m_totalXRes ;
vp - > height = ( GLint ) ( ( float ) vpHeight * m_yRatio ) ;
2016-03-31 09:27:42 +00:00
2016-05-05 22:01:59 +00:00
vp - > projectionMatrix . Frustum ( l * correction , r * correction , b , t , near , far ) ;
}
else
{
vp - > x = m_xOffs + ( GLint ) ( ( float ) vpX * m_xRatio ) ;
vp - > y = m_yOffs + ( GLint ) ( ( float ) ( 384 - ( vpY + vpHeight ) ) * m_yRatio ) ;
vp - > width = ( GLint ) ( ( float ) vpWidth * m_xRatio ) ;
vp - > height = ( GLint ) ( ( float ) vpHeight * m_yRatio ) ;
vp - > projectionMatrix . Frustum ( l , r , b , t , near , far ) ;
}
2016-03-21 04:10:14 +00:00
2016-05-05 22:01:59 +00:00
// Lighting (note that sun vector points toward sun -- away from vertex)
vp - > lightingParams [ 0 ] = * ( float * ) & vpnode [ 0x05 ] ; // sun X
vp - > lightingParams [ 1 ] = * ( float * ) & vpnode [ 0x06 ] ; // sun Y
vp - > lightingParams [ 2 ] = * ( float * ) & vpnode [ 0x04 ] ; // sun Z
vp - > lightingParams [ 3 ] = * ( float * ) & vpnode [ 0x07 ] ; // sun intensity
vp - > lightingParams [ 4 ] = ( float ) ( ( vpnode [ 0x24 ] > > 8 ) & 0xFF ) * ( 1.0f / 255.0f ) ; // ambient intensity
vp - > lightingParams [ 5 ] = 0.0 ; // reserved
// Spotlight
2016-05-13 19:26:10 +00:00
int spotColorIdx = ( vpnode [ 0x20 ] > > 11 ) & 7 ; // spotlight color index
2016-05-05 22:01:59 +00:00
vp - > spotEllipse [ 0 ] = ( float ) ( ( vpnode [ 0x1E ] > > 3 ) & 0x1FFF ) ; // spotlight X position (fractional component?)
vp - > spotEllipse [ 1 ] = ( float ) ( ( vpnode [ 0x1D ] > > 3 ) & 0x1FFF ) ; // spotlight Y
vp - > spotEllipse [ 2 ] = ( float ) ( ( vpnode [ 0x1E ] > > 16 ) & 0xFFFF ) ; // spotlight X size (16-bit? May have fractional component below bit 16)
vp - > spotEllipse [ 3 ] = ( float ) ( ( vpnode [ 0x1D ] > > 16 ) & 0xFFFF ) ; // spotlight Y size
vp - > spotRange [ 0 ] = 1.0f / ( * ( float * ) & vpnode [ 0x21 ] ) ; // spotlight start
vp - > spotRange [ 1 ] = * ( float * ) & vpnode [ 0x1F ] ; // spotlight extent
vp - > spotColor [ 0 ] = color [ spotColorIdx ] [ 0 ] ; // spotlight color
vp - > spotColor [ 1 ] = color [ spotColorIdx ] [ 1 ] ;
vp - > spotColor [ 2 ] = color [ spotColorIdx ] [ 2 ] ;
// Spotlight is applied on a per pixel basis, must scale its position and size to screen
vp - > spotEllipse [ 1 ] = 384.0f - vp - > spotEllipse [ 1 ] ;
vp - > spotRange [ 1 ] + = vp - > spotRange [ 0 ] ; // limit
vp - > spotEllipse [ 2 ] = 496.0f / sqrt ( vp - > spotEllipse [ 2 ] ) ; // spotlight appears to be specified in terms of physical resolution (unconfirmed)
vp - > spotEllipse [ 3 ] = 384.0f / sqrt ( vp - > spotEllipse [ 3 ] ) ;
// Scale the spotlight to the OpenGL viewport
vp - > spotEllipse [ 0 ] = vp - > spotEllipse [ 0 ] * m_xRatio + m_xOffs ;
vp - > spotEllipse [ 1 ] = vp - > spotEllipse [ 1 ] * m_yRatio + m_yOffs ;
vp - > spotEllipse [ 2 ] * = m_xRatio ;
vp - > spotEllipse [ 3 ] * = m_yRatio ;
// Fog
vp - > fogParams [ 0 ] = ( float ) ( ( vpnode [ 0x22 ] > > 16 ) & 0xFF ) * ( 1.0f / 255.0f ) ; // fog color R
vp - > fogParams [ 1 ] = ( float ) ( ( vpnode [ 0x22 ] > > 8 ) & 0xFF ) * ( 1.0f / 255.0f ) ; // fog color G
vp - > fogParams [ 2 ] = ( float ) ( ( vpnode [ 0x22 ] > > 0 ) & 0xFF ) * ( 1.0f / 255.0f ) ; // fog color B
vp - > fogParams [ 3 ] = * ( float * ) & vpnode [ 0x23 ] ; // fog density
vp - > fogParams [ 4 ] = ( float ) ( INT16 ) ( vpnode [ 0x25 ] & 0xFFFF ) * ( 1.0f / 255.0f ) ; // fog start
if ( std : : isinf ( vp - > fogParams [ 3 ] ) | | std : : isnan ( vp - > fogParams [ 3 ] ) | | std : : isinf ( vp - > fogParams [ 4 ] ) | | std : : isnan ( vp - > fogParams [ 4 ] ) ) { // Star Wars Trilogy
vp - > fogParams [ 3 ] = vp - > fogParams [ 4 ] = 0.0f ;
}
2016-03-21 04:10:14 +00:00
2016-05-05 22:01:59 +00:00
// Unknown light/fog parameters
2016-05-18 23:06:41 +00:00
float scrollFog = ( float ) ( vpnode [ 0x20 ] & 0xFF ) * ( 1.0f / 255.0f ) ; // scroll fog
float scrollAtt = ( float ) ( vpnode [ 0x24 ] & 0xFF ) * ( 1.0f / 255.0f ) ; // scroll attenuation
2016-03-21 04:10:14 +00:00
2016-05-05 22:01:59 +00:00
// Clear texture offsets before proceeding
m_nodeAttribs . Reset ( ) ;
2016-03-21 04:10:14 +00:00
2016-05-05 22:01:59 +00:00
// Set up coordinate system and base matrix
InitMatrixStack ( matrixBase , m_modelMat ) ;
2016-03-21 04:10:14 +00:00
2016-05-05 22:01:59 +00:00
// Safeguard: weird coordinate system matrices usually indicate scenes that will choke the renderer
if ( NULL ! = m_matrixBasePtr )
{
float m21 , m32 , m13 ;
2016-03-21 04:10:14 +00:00
2016-05-05 22:01:59 +00:00
// Get the three elements that are usually set and see if their magnitudes are 1
m21 = m_matrixBasePtr [ 6 ] ;
m32 = m_matrixBasePtr [ 10 ] ;
m13 = m_matrixBasePtr [ 5 ] ;
2016-03-21 04:10:14 +00:00
2016-05-05 22:01:59 +00:00
m21 * = m21 ;
m32 * = m32 ;
m13 * = m13 ;
2016-03-21 04:10:14 +00:00
2016-05-05 22:01:59 +00:00
if ( ( m21 > 1.05 ) | | ( m21 < 0.95 ) )
return ;
if ( ( m32 > 1.05 ) | | ( m32 < 0.95 ) )
return ;
if ( ( m13 > 1.05 ) | | ( m13 < 0.95 ) )
return ;
}
2016-03-21 04:10:14 +00:00
2016-05-05 22:01:59 +00:00
// Descend down the node link: Use recursive traversal
DescendNodePtr ( nodeAddr ) ;
2016-03-21 04:10:14 +00:00
}
2016-04-19 22:05:12 +00:00
// render next viewport
if ( vpnode [ 0x01 ] ! = 0x01000000 ) {
RenderViewport ( vpnode [ 0x01 ] ) ;
}
2016-03-21 04:10:14 +00:00
}
2016-05-07 15:28:04 +00:00
void CNew3D : : CopyVertexData ( const R3DPoly & r3dPoly , std : : vector < Poly > & polyArray )
2016-03-21 04:10:14 +00:00
{
//====================
Poly p ;
V3 : : Vec3 normal ;
float dotProd ;
bool clockWise ;
//====================
V3 : : createNormal ( r3dPoly . v [ 0 ] . pos , r3dPoly . v [ 1 ] . pos , r3dPoly . v [ 2 ] . pos , normal ) ;
dotProd = V3 : : dotProduct ( normal , r3dPoly . faceNormal ) ;
2016-03-22 23:39:59 +00:00
clockWise = dotProd > = 0.0 ;
2016-03-21 04:10:14 +00:00
if ( clockWise ) {
p . p1 = r3dPoly . v [ 0 ] ;
p . p2 = r3dPoly . v [ 1 ] ;
p . p3 = r3dPoly . v [ 2 ] ;
}
else {
p . p1 = r3dPoly . v [ 2 ] ;
p . p2 = r3dPoly . v [ 1 ] ;
p . p3 = r3dPoly . v [ 0 ] ;
}
2016-05-07 15:28:04 +00:00
//multiply face attributes with vertex attributes if required
for ( int i = 0 ; i < 4 ; i + + ) {
p . p1 . color [ i ] = ( UINT8 ) ( p . p1 . color [ i ] * r3dPoly . faceColour [ i ] ) ;
p . p2 . color [ i ] = ( UINT8 ) ( p . p2 . color [ i ] * r3dPoly . faceColour [ i ] ) ;
p . p3 . color [ i ] = ( UINT8 ) ( p . p3 . color [ i ] * r3dPoly . faceColour [ i ] ) ;
}
2016-03-21 04:10:14 +00:00
polyArray . emplace_back ( p ) ;
if ( r3dPoly . number = = 4 ) {
if ( clockWise ) {
p . p1 = r3dPoly . v [ 0 ] ;
p . p2 = r3dPoly . v [ 2 ] ;
p . p3 = r3dPoly . v [ 3 ] ;
}
else {
p . p1 = r3dPoly . v [ 0 ] ;
p . p2 = r3dPoly . v [ 3 ] ;
p . p3 = r3dPoly . v [ 2 ] ;
}
2016-05-07 15:28:04 +00:00
//multiply face attributes with vertex attributes if required
for ( int i = 0 ; i < 4 ; i + + ) {
p . p1 . color [ i ] = ( UINT8 ) ( p . p1 . color [ i ] * r3dPoly . faceColour [ i ] ) ;
p . p2 . color [ i ] = ( UINT8 ) ( p . p2 . color [ i ] * r3dPoly . faceColour [ i ] ) ;
p . p3 . color [ i ] = ( UINT8 ) ( p . p3 . color [ i ] * r3dPoly . faceColour [ i ] ) ;
}
2016-03-21 04:10:14 +00:00
polyArray . emplace_back ( p ) ;
}
}
2016-05-09 16:26:34 +00:00
// non smooth texturing on the pro-1000 seems to sample like gl_nearest
// ie not outside of the texture coordinates, but with bilinear filtering
// this is about as close as we can emulate in hardware
// if we don't do this with gl_repeat enabled, it will wrap around and sample the
// other side of the texture which produces ugly seems
void CNew3D : : OffsetTexCoords ( R3DPoly & r3dPoly , float offset [ 2 ] )
{
for ( int i = 0 ; i < 2 ; i + + ) {
2016-05-10 03:08:36 +00:00
float min = std : : numeric_limits < float > : : max ( ) ;
float max = - std : : numeric_limits < float > : : max ( ) ;
2016-05-09 16:26:34 +00:00
if ( ! offset [ i ] ) continue ;
for ( int j = 0 ; j < r3dPoly . number ; j + + ) {
min = std : : min ( r3dPoly . v [ j ] . texcoords [ i ] , min ) ;
max = std : : max ( r3dPoly . v [ j ] . texcoords [ i ] , max ) ;
}
2016-05-28 13:21:35 +00:00
float fTemp ;
float iTemp ;
bool fractMin ;
bool fractMax ;
2016-05-28 20:54:25 +00:00
fTemp = std : : modf ( min , & iTemp ) ;
2016-05-28 13:21:35 +00:00
fractMin = fTemp > 0 ;
2016-05-28 20:54:25 +00:00
fTemp = std : : modf ( max , & iTemp ) ;
2016-05-28 13:21:35 +00:00
fractMax = fTemp > 0 ;
2016-05-09 16:26:34 +00:00
for ( int j = 0 ; j < r3dPoly . number & & min ! = max ; j + + ) {
2016-05-28 13:21:35 +00:00
if ( ! fractMin ) {
if ( r3dPoly . v [ j ] . texcoords [ i ] = = min ) {
r3dPoly . v [ j ] . texcoords [ i ] + = offset [ i ] ;
}
2016-05-09 16:26:34 +00:00
}
2016-05-28 13:21:35 +00:00
if ( ! fractMax ) {
if ( r3dPoly . v [ j ] . texcoords [ i ] = = max ) {
r3dPoly . v [ j ] . texcoords [ i ] - = offset [ i ] ;
}
2016-05-09 16:26:34 +00:00
}
}
}
}
2016-03-21 04:10:14 +00:00
void CNew3D : : CacheModel ( Model * m , const UINT32 * data )
{
Vertex prev [ 4 ] ;
PolyHeader ph ;
int numPolys = 0 ;
UINT64 lastHash = - 1 ;
SortingMesh * currentMesh = nullptr ;
std : : map < UINT64 , SortingMesh > sMap ;
if ( data = = NULL )
return ;
ph = data ;
int numTriangles = ph . NumTrianglesTotal ( ) ;
// Cache all polygons
2016-05-13 08:39:48 +00:00
do {
2016-03-21 04:10:14 +00:00
R3DPoly p ; // current polygon
GLfloat uvScale ;
int i , j ;
if ( ph . header [ 6 ] = = 0 ) {
break ;
}
2016-05-13 08:39:48 +00:00
if ( ph . Disabled ( ) | | ! numPolys & & ( ph . NumSharedVerts ( ) ! = 0 ) ) {
continue ;
2016-03-21 04:10:14 +00:00
}
// create a hash value based on poly attributes -todo add more attributes
2016-03-26 22:44:26 +00:00
auto hash = ph . Hash ( ) ;
2016-03-21 04:10:14 +00:00
2016-05-13 08:39:48 +00:00
if ( hash ! = lastHash ) {
2016-03-21 04:10:14 +00:00
if ( sMap . count ( hash ) = = 0 ) {
sMap [ hash ] = SortingMesh ( ) ;
currentMesh = & sMap [ hash ] ;
//make space for our vertices
currentMesh - > polys . reserve ( numTriangles ) ;
//copy attributes
2016-05-13 08:39:48 +00:00
currentMesh - > doubleSided = false ; // we will double up polys
2016-03-21 04:10:14 +00:00
currentMesh - > textured = ph . TexEnabled ( ) ;
currentMesh - > alphaTest = ph . AlphaTest ( ) ;
currentMesh - > textureAlpha = ph . TextureAlpha ( ) ;
currentMesh - > polyAlpha = ph . PolyAlpha ( ) ;
2016-04-30 20:32:44 +00:00
currentMesh - > lighting = ph . LightEnabled ( ) & & ! ph . FixedShading ( ) ;
2016-05-27 19:30:40 +00:00
currentMesh - > layered = ph . Layered ( ) ;
2016-05-15 16:24:49 +00:00
if ( currentMesh - > lighting ) {
if ( ph . SpecularEnabled ( ) ) {
currentMesh - > specular = true ;
currentMesh - > shininess = 0 ; // ph.Shininess();
currentMesh - > specularCoefficient = 0 ; // ph.SpecularValue();
}
}
2016-05-16 10:49:00 +00:00
currentMesh - > fogIntensity = ph . LightModifier ( ) ;
2016-03-21 04:10:14 +00:00
if ( ph . TexEnabled ( ) ) {
2016-05-21 15:50:54 +00:00
currentMesh - > format = m_texSheet . GetTexFormat ( ph . TexFormat ( ) , ph . AlphaTest ( ) ) ;
2016-05-22 19:17:15 +00:00
if ( currentMesh - > format = = 7 ) {
currentMesh - > alphaTest = false ; // alpha test is a 1 bit test, this format needs a lower threshold, since it has 16 levels of transparency
}
2016-05-04 00:35:07 +00:00
currentMesh - > x = ph . X ( ) ;
currentMesh - > y = ph . Y ( ) ;
currentMesh - > width = ph . TexWidth ( ) ;
currentMesh - > height = ph . TexHeight ( ) ;
currentMesh - > mirrorU = ph . TexUMirror ( ) ;
currentMesh - > mirrorV = ph . TexVMirror ( ) ;
2016-05-09 16:26:34 +00:00
currentMesh - > microTexture = ph . MicroTexture ( ) ;
2016-03-21 04:10:14 +00:00
}
}
currentMesh = & sMap [ hash ] ;
}
2016-05-13 08:39:48 +00:00
lastHash = hash ;
2016-03-21 04:10:14 +00:00
// Obtain basic polygon parameters
p . number = ph . NumVerts ( ) ;
uvScale = ph . UVScale ( ) ;
ph . FaceNormal ( p . faceNormal ) ;
// Fetch reused vertices according to bitfield, then new verts
i = 0 ;
j = 0 ;
for ( i = 0 ; i < 4 ; i + + ) // up to 4 reused vertices
{
if ( ph . SharedVertex ( i ) )
{
p . v [ j ] = prev [ i ] ;
+ + j ;
}
}
2016-05-07 15:28:04 +00:00
// copy face attributes
2016-05-07 15:45:02 +00:00
if ( ( ph . header [ 1 ] & 2 ) = = 0 ) {
2016-05-28 20:49:10 +00:00
int colorIdx = ph . ColorIndex ( ) ;
2016-05-18 23:06:41 +00:00
p . faceColour [ 2 ] = ( m_polyRAM [ m_colorTableAddr + colorIdx ] & 0xFF ) / 255.f ;
p . faceColour [ 1 ] = ( ( m_polyRAM [ m_colorTableAddr + colorIdx ] > > 8 ) & 0xFF ) / 255.f ;
p . faceColour [ 0 ] = ( ( m_polyRAM [ m_colorTableAddr + colorIdx ] > > 16 ) & 0xFF ) / 255.f ;
2016-05-07 15:45:02 +00:00
}
else {
if ( ph . ColorDisabled ( ) ) { // no colours were set
p . faceColour [ 0 ] = 1.0f ;
p . faceColour [ 1 ] = 1.0f ;
p . faceColour [ 2 ] = 1.0f ;
2016-05-07 15:28:04 +00:00
}
else {
2016-05-07 15:45:02 +00:00
p . faceColour [ 0 ] = ( ( ph . header [ 4 ] > > 24 ) ) / 255.f ;
p . faceColour [ 1 ] = ( ( ph . header [ 4 ] > > 16 ) & 0xFF ) / 255.f ;
p . faceColour [ 2 ] = ( ( ph . header [ 4 ] > > 8 ) & 0xFF ) / 255.f ;
2016-05-07 15:28:04 +00:00
}
}
2016-05-27 22:20:57 +00:00
p . faceColour [ 3 ] = ph . Transparency ( ) / 255.f ;
2016-05-05 08:06:06 +00:00
// if we have flat shading, we can't re-use normals from shared vertices
for ( i = 0 ; i < p . number & & ! ph . SmoothShading ( ) ; i + + ) {
p . v [ i ] . normal [ 0 ] = p . faceNormal [ 0 ] ;
p . v [ i ] . normal [ 1 ] = p . faceNormal [ 1 ] ;
p . v [ i ] . normal [ 2 ] = p . faceNormal [ 2 ] ;
}
2016-05-13 08:39:48 +00:00
UINT32 * vData = ph . StartOfData ( ) ; // vertex data starts here
2016-05-07 15:28:04 +00:00
// remaining vertices are new and defined here
for ( ; j < p . number ; j + + )
2016-03-21 04:10:14 +00:00
{
// Fetch vertices
2016-05-13 08:39:48 +00:00
UINT32 ix = vData [ 0 ] ;
UINT32 iy = vData [ 1 ] ;
UINT32 iz = vData [ 2 ] ;
UINT32 it = vData [ 3 ] ;
2016-03-21 04:10:14 +00:00
// Decode vertices
p . v [ j ] . pos [ 0 ] = ( GLfloat ) ( ( ( INT32 ) ix ) > > 8 ) * m_vertexFactor ;
p . v [ j ] . pos [ 1 ] = ( GLfloat ) ( ( ( INT32 ) iy ) > > 8 ) * m_vertexFactor ;
p . v [ j ] . pos [ 2 ] = ( GLfloat ) ( ( ( INT32 ) iz ) > > 8 ) * m_vertexFactor ;
2016-05-05 08:06:06 +00:00
// Per vertex normals
2016-05-05 00:01:17 +00:00
if ( ph . SmoothShading ( ) ) {
p . v [ j ] . normal [ 0 ] = ( INT8 ) ( ix & 0xFF ) / 128.f ;
p . v [ j ] . normal [ 1 ] = ( INT8 ) ( iy & 0xFF ) / 128.f ;
p . v [ j ] . normal [ 2 ] = ( INT8 ) ( iz & 0xFF ) / 128.f ;
}
2016-03-21 04:10:14 +00:00
2016-04-29 23:03:46 +00:00
if ( ph . FixedShading ( ) & & ph . LightEnabled ( ) ) {
2016-05-07 15:28:04 +00:00
UINT8 shade = ( UINT8 ) ( ( ix + 128 ) & 0xFF ) ;
p . v [ j ] . color [ 0 ] = shade ; // hardware doesn't really have per vertex colours, only per poly
p . v [ j ] . color [ 1 ] = shade ;
p . v [ j ] . color [ 2 ] = shade ;
2016-03-21 04:10:14 +00:00
p . v [ j ] . color [ 3 ] = 255 ;
}
else {
2016-05-07 15:28:04 +00:00
p . v [ j ] . color [ 0 ] = 255 ;
p . v [ j ] . color [ 1 ] = 255 ;
p . v [ j ] . color [ 2 ] = 255 ;
p . v [ j ] . color [ 3 ] = 255 ;
2016-03-21 04:10:14 +00:00
}
float texU , texV = 0 ;
// tex coords
2016-05-13 08:39:48 +00:00
if ( currentMesh - > textured ) {
2016-03-26 22:44:26 +00:00
Texture : : GetCoordinates ( currentMesh - > width , currentMesh - > height , ( UINT16 ) ( it > > 16 ) , ( UINT16 ) ( it & 0xFFFF ) , uvScale , texU , texV ) ;
2016-03-21 04:10:14 +00:00
}
p . v [ j ] . texcoords [ 0 ] = texU ;
p . v [ j ] . texcoords [ 1 ] = texV ;
2016-05-13 08:39:48 +00:00
vData + = 4 ;
2016-03-21 04:10:14 +00:00
}
2016-05-09 16:26:34 +00:00
// check if we need to modify the texture coordinates
{
float offset [ 2 ] = { 0 } ;
if ( ph . TexEnabled ( ) ) {
if ( ! ph . TexSmoothU ( ) & & ! ph . TexUMirror ( ) ) {
offset [ 0 ] = 0.5f / ph . TexWidth ( ) ; // half texel
}
if ( ! ph . TexSmoothV ( ) & & ! ph . TexVMirror ( ) ) {
offset [ 1 ] = 0.5f / ph . TexHeight ( ) ; // half texel
}
OffsetTexCoords ( p , offset ) ;
}
}
2016-05-13 08:39:48 +00:00
// check if we need double up vertices for two sided lighting
if ( ph . DoubleSided ( ) ) {
R3DPoly tempP = p ;
// flip normals
V3 : : inverse ( tempP . faceNormal ) ;
for ( int i = 0 ; i < tempP . number ; i + + ) {
V3 : : inverse ( tempP . v [ i ] . normal ) ;
}
CopyVertexData ( tempP , currentMesh - > polys ) ;
2016-03-21 04:10:14 +00:00
}
// Copy this polygon into the model buffer
2016-05-13 08:39:48 +00:00
CopyVertexData ( p , currentMesh - > polys ) ;
numPolys + + ;
// Copy current vertices into previous vertex array
for ( i = 0 ; i < 4 ; i + + ) {
prev [ i ] = p . v [ i ] ;
2016-03-21 04:10:14 +00:00
}
2016-05-13 08:39:48 +00:00
} while ( ph . NextPoly ( ) ) ;
2016-03-21 04:10:14 +00:00
//sorted the data, now copy to main data structures
// we know how many meshes we have so reserve appropriate space
2016-03-26 22:44:26 +00:00
m - > meshes - > reserve ( sMap . size ( ) ) ;
2016-03-21 04:10:14 +00:00
for ( auto & it : sMap ) {
2016-03-26 22:44:26 +00:00
if ( m - > dynamic ) {
2016-03-21 04:10:14 +00:00
2016-03-26 22:44:26 +00:00
// calculate VBO values for current mesh
it . second . vboOffset = m_polyBufferRam . size ( ) + MAX_ROM_POLYS ;
it . second . triangleCount = it . second . polys . size ( ) ;
// copy poly data to main buffer
m_polyBufferRam . insert ( m_polyBufferRam . end ( ) , it . second . polys . begin ( ) , it . second . polys . end ( ) ) ;
}
else {
// calculate VBO values for current mesh
it . second . vboOffset = m_polyBufferRom . size ( ) ;
it . second . triangleCount = it . second . polys . size ( ) ;
// copy poly data to main buffer
m_polyBufferRom . insert ( m_polyBufferRom . end ( ) , it . second . polys . begin ( ) , it . second . polys . end ( ) ) ;
}
2016-03-21 04:10:14 +00:00
//copy the temp mesh into the model structure
//this will lose the associated vertex data, which is now copied to the main buffer anyway
2016-03-26 22:44:26 +00:00
m - > meshes - > push_back ( it . second ) ;
2016-03-21 04:10:14 +00:00
}
}
2016-03-22 13:47:28 +00:00
float CNew3D : : Determinant3x3 ( const float m [ 16 ] ) {
/*
| A B C |
M = | D E F |
| G H I |
then the determinant is calculated as follows :
det M = A * ( EI - HF ) - B * ( DI - GF ) + C * ( DH - GE )
*/
return m [ 0 ] * ( ( m [ 5 ] * m [ 10 ] ) - ( m [ 6 ] * m [ 9 ] ) ) - m [ 4 ] * ( ( m [ 1 ] * m [ 10 ] ) - ( m [ 2 ] * m [ 9 ] ) ) + m [ 8 ] * ( ( m [ 1 ] * m [ 6 ] ) - ( m [ 2 ] * m [ 5 ] ) ) ;
}
2016-03-24 13:17:17 +00:00
bool CNew3D : : IsDynamicModel ( UINT32 * data )
{
if ( data = = NULL ) {
return false ;
}
PolyHeader p ( data ) ;
do {
if ( ( p . header [ 1 ] & 2 ) = = 0 ) { // model has rgb colour palette
return true ;
}
if ( p . header [ 6 ] = = 0 ) {
break ;
}
} while ( p . NextPoly ( ) ) ;
return false ;
}
2016-03-26 22:44:26 +00:00
bool CNew3D : : IsVROMModel ( UINT32 modelAddr )
{
return modelAddr > = 0x100000 ;
}
2016-05-30 13:25:00 +00:00
void CNew3D : : CalcTexOffset ( int offX , int offY , int page , int x , int y , int & newX , int & newY )
{
newX = ( x + offX ) & 2047 ; // wrap around 2048, shouldn't be required
int oldPage = y / 1024 ;
y - = ( oldPage * 1024 ) ; // remove page from tex y
// calc newY with wrap around, wraps around in the same sheet, not into another memory sheet
newY = ( y + offY ) & 1023 ;
// add page to Y
newY + = ( ( oldPage + page ) & 1 ) * 1024 ; // max page 0-1
}
2016-06-03 00:01:34 +00:00
UINT32 CNew3D : : ConvertProFloat ( UINT32 a1 )
{
2016-06-03 09:33:46 +00:00
int exponent = ( a1 & 0x7E000000 ) > > 25 ;
2016-06-03 00:01:34 +00:00
2016-06-03 09:33:46 +00:00
if ( exponent < = 31 ) { // positive
exponent + = 127 ;
}
else { // negative exponent
exponent - = 64 ;
exponent + = 127 ;
}
2016-06-03 00:01:34 +00:00
int mantissa = ( a1 & 0x1FFFFFF ) > > 2 ;
return ( a1 & 0x80000000 ) | ( exponent < < 23 ) | mantissa ;
}
UINT32 CNew3D : : Convert16BitProFloat ( UINT32 a1 )
{
return ConvertProFloat ( a1 < < 15 ) ;
}
float CNew3D : : ToFloat ( UINT32 a1 )
{
return * ( float * ) ( & a1 ) ;
}
2016-03-21 04:10:14 +00:00
} // New3D
2016-05-09 16:26:34 +00:00