2016-03-21 04:10:14 +00:00
# include " New3D.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>
2017-02-20 17:22:32 +00:00
# include <string.h>
2016-06-19 21:43:09 +00:00
# include "R3DFloat.h"
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
namespace New3D {
2017-08-11 00:41:10 +00:00
CNew3D : : CNew3D ( const Util : : Config : : Node & config )
: m_r3dShader ( config ) ,
m_r3dScrollFog ( config )
2016-03-21 04:10:14 +00:00
{
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 ;
}
2017-03-27 03:19:15 +00:00
void CNew3D : : SetStepping ( int stepping )
2016-03-21 04:10:14 +00:00
{
2017-03-27 03:19:15 +00:00
m_step = stepping ;
2016-03-21 04:10:14 +00:00
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 ..
}
2017-03-25 00:06:24 +00:00
void CNew3D : : UploadTextures ( unsigned level , unsigned x , unsigned y , unsigned width , unsigned height )
2016-03-21 04:10:14 +00:00
{
2017-03-25 00:06:24 +00:00
if ( level = = 0 ) {
m_texSheet . Invalidate ( x , y , width , height ) ; // base textures only
}
else if ( level = = 1 ) {
// we want to work out what the base level is, and invalidate the entire texture
// the mipmap data in some cases is being sent later
int page = y / 1024 ;
y - = ( page * 1024 ) ; // remove page from tex y
int xPos = ( x - 1024 ) * 2 ;
int yPos = ( y - 512 ) * 2 ;
2016-11-21 09:04:56 +00:00
2017-03-25 00:06:24 +00:00
yPos + = page * 1024 ;
width * = 2 ;
height * = 2 ;
m_texSheet . Invalidate ( xPos , yPos , width , height ) ;
}
2016-03-21 04:10:14 +00:00
}
2016-10-09 16:34:12 +00:00
void CNew3D : : DrawScrollFog ( )
{
2017-05-06 16:47:31 +00:00
// ths is my best guess at the logic based upon what games are doing
//
// ocean hunter - every viewport has scroll fog values set. Must start with lowest priority layers as the higher ones sometimes are garbage
// scud race - first viewports in priority layer missing scroll values. The latter ones all contain valid scroll values.
// daytona - doesn't seem to use scroll fog at all. Will set scroll values for the first viewports, the end ones contain no scroll values
// vf3 - first viewport only has it set. But set with highest select value ?? Rest of the viewports in priority layer contain a lower select value
// sega bassfishing - first viewport in priority 1 sets scroll value. The rest all contain the wrong value + a higher select value ..
2017-06-07 23:16:22 +00:00
// spikeout final - 2nd viewport in the priority layer has scroll values set, none of the others do. It also uses the highest select value
2017-04-02 21:03:59 +00:00
2017-05-06 16:47:31 +00:00
// known bug (vf3) - if you complete the game on a stage that has fogging, that fogging gets carried over into the credits. I did a binary diff on the viewport, it's never updated from the previous stage, neither is the data it's pointing at. Either a game or emulation bug.
2017-04-02 21:03:59 +00:00
2016-10-09 18:10:57 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
2017-05-06 16:47:31 +00:00
Viewport * vp = nullptr ;
2016-10-09 18:10:57 +00:00
for ( auto & n : m_nodes ) {
2016-10-09 16:34:12 +00:00
2017-05-06 16:47:31 +00:00
if ( n . viewport . priority = = i ) {
2017-06-07 23:14:01 +00:00
if ( ! vp & & n . viewport . scrollFog ) {
vp = & n . viewport ; // only start grabbing viewports if there is a scroll value
}
else if ( vp & & n . viewport . select = = vp - > select ) {
2017-05-06 16:47:31 +00:00
vp = & n . viewport ; // grab the last viewport with the same select value ??
}
}
}
2016-10-09 16:34:12 +00:00
2017-05-06 16:47:31 +00:00
if ( vp & & vp - > scrollFog ) {
2017-04-02 21:03:59 +00:00
2017-05-06 16:47:31 +00:00
float rgba [ 4 ] = { vp - > fogParams [ 0 ] , vp - > fogParams [ 1 ] , vp - > fogParams [ 2 ] , vp - > scrollFog } ;
2016-12-04 22:37:49 +00:00
2017-05-06 16:47:31 +00:00
glViewport ( 0 , 0 , m_totalXRes , m_totalYRes ) ; // fill the whole viewport
m_r3dScrollFog . DrawScrollFog ( rgba , vp - > scrollAtt , vp - > fogParams [ 6 ] , vp - > spotFogColor , vp - > spotEllipse ) ;
return ;
2016-10-09 16:34:12 +00:00
}
}
}
2017-02-20 17:22:32 +00:00
bool CNew3D : : RenderScene ( int priority , bool renderOverlay , bool alpha )
2016-03-21 04:10:14 +00:00
{
2017-02-20 17:22:32 +00:00
bool hasOverlay = false ; // (high priority polys)
2016-03-21 04:10:14 +00:00
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-06-10 11:29:20 +00:00
std : : shared_ptr < Texture > tex1 ;
2017-02-07 14:05:03 +00:00
CalcViewport ( & n . viewport , std : : abs ( m_nfPairs [ priority ] . zNear * 0.95f ) , std : : abs ( m_nfPairs [ priority ] . zFar * 1.05f ) ) ; // make planes 5% bigger
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
2017-02-20 17:22:32 +00:00
if ( mesh . highPriority ) {
hasOverlay = true ;
2016-03-21 04:10:14 +00:00
}
2016-03-22 23:39:59 +00:00
2017-02-20 17:22:32 +00:00
if ( ! mesh . Render ( alpha ) ) continue ;
if ( mesh . highPriority ! = renderOverlay ) 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 ) ;
2016-06-10 11:29:20 +00:00
if ( tex1 & & tex1 - > Compare ( x , y , mesh . width , mesh . height , mesh . format ) ) {
tex1 - > SetWrapMode ( mesh . mirrorU , mesh . mirrorV ) ;
}
else {
tex1 = m_texSheet . BindTexture ( m_textureRAM , mesh . format , mesh . mirrorU , mesh . mirrorV , x , y , mesh . width , mesh . height ) ;
if ( tex1 ) {
tex1 - > BindTexture ( ) ;
tex1 - > SetWrapMode ( mesh . mirrorU , mesh . mirrorV ) ;
}
2016-05-04 00:35:07 +00:00
}
if ( mesh . microTexture ) {
2016-10-06 01:37:29 +00:00
int mX , mY ;
2016-05-04 00:35:07 +00:00
glActiveTexture ( GL_TEXTURE1 ) ;
2016-10-06 01:37:29 +00:00
m_texSheet . GetMicrotexPos ( y / 1024 , mesh . microTextureID , mX , mY ) ;
auto tex2 = m_texSheet . BindTexture ( m_textureRAM , 0 , false , false , mX , mY , 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 ) ;
2017-02-20 17:22:32 +00:00
return hasOverlay ;
2016-03-21 04:10:14 +00:00
}
void CNew3D : : RenderFrame ( void )
{
2017-02-07 14:05:03 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
m_nfPairs [ i ] . zNear = - std : : numeric_limits < float > : : max ( ) ;
m_nfPairs [ i ] . zFar = std : : numeric_limits < float > : : max ( ) ;
}
2016-03-21 04:10:14 +00:00
// release any resources from last frame
2017-02-07 14:05:03 +00:00
m_polyBufferRam . clear ( ) ; // clear dyanmic model memory buffer
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
2016-03-21 04:10:14 +00:00
m_nodeAttribs . Reset ( ) ;
2016-10-09 16:34:12 +00:00
RenderViewport ( 0x800000 ) ; // build model structure
DrawScrollFog ( ) ; // fog layer if applicable must be drawn here
2016-03-21 04:10:14 +00:00
glDepthFunc ( GL_LEQUAL ) ;
glEnable ( GL_DEPTH_TEST ) ;
2017-04-20 19:43:41 +00:00
glDepthMask ( GL_TRUE ) ;
2016-03-21 04:10:14 +00:00
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
2016-10-09 16:34:12 +00:00
glStencilOp ( GL_KEEP , GL_INCR , GL_INCR ) ; // if the stencil test passes, we incriment the value
2016-05-27 19:30:40 +00:00
glStencilMask ( 0xFF ) ;
2016-04-19 22:05:12 +00:00
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 ) ] ) ;
}
}
}
2017-08-10 15:43:03 +00:00
glEnableVertexAttribArray ( 0 ) ;
glEnableVertexAttribArray ( 1 ) ;
glEnableVertexAttribArray ( 2 ) ;
glEnableVertexAttribArray ( 3 ) ;
2017-08-14 09:14:06 +00:00
glEnableVertexAttribArray ( 4 ) ;
2016-03-21 04:10:14 +00:00
// before draw, specify vertex and index arrays with their offsets, offsetof is maybe evil ..
2017-08-14 09:14:06 +00:00
glVertexAttribPointer ( m_r3dShader . GetVertexAttribPos ( " inVertex " ) , 3 , GL_FLOAT , GL_FALSE , sizeof ( Vertex ) , 0 ) ;
glVertexAttribPointer ( m_r3dShader . GetVertexAttribPos ( " inNormal " ) , 3 , GL_FLOAT , GL_FALSE , sizeof ( Vertex ) , ( void * ) offsetof ( Vertex , normal ) ) ;
glVertexAttribPointer ( m_r3dShader . GetVertexAttribPos ( " inTexCoord " ) , 2 , GL_FLOAT , GL_FALSE , sizeof ( Vertex ) , ( void * ) offsetof ( Vertex , texcoords ) ) ;
glVertexAttribPointer ( m_r3dShader . GetVertexAttribPos ( " inColour " ) , 4 , GL_UNSIGNED_BYTE , GL_TRUE , sizeof ( Vertex ) , ( void * ) offsetof ( Vertex , color ) ) ;
2017-08-14 23:30:34 +00:00
glVertexAttribPointer ( m_r3dShader . GetVertexAttribPos ( " inFixedShade " ) , 1 , GL_UNSIGNED_BYTE , GL_TRUE , sizeof ( Vertex ) , ( void * ) offsetof ( Vertex , fixedShade ) ) ;
2016-10-09 16:34:12 +00:00
2016-03-21 04:10:14 +00:00
m_r3dShader . SetShader ( true ) ;
for ( int pri = 0 ; pri < = 3 ; pri + + ) {
2017-02-20 17:22:32 +00:00
//==============
bool hasOverlay ;
//==============
glViewport ( 0 , 0 , m_totalXRes , m_totalYRes ) ; // clear whole viewport
2016-05-27 19:30:40 +00:00
glClear ( GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ) ;
2017-02-20 17:22:32 +00:00
hasOverlay = RenderScene ( pri , false , false ) ;
hasOverlay = RenderScene ( pri , false , true ) ;
if ( hasOverlay ) {
//clear depth buffer and render high priority polys
glViewport ( 0 , 0 , m_totalXRes , m_totalYRes ) ; // clear whole viewport
glClear ( GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ) ;
RenderScene ( pri , true , false ) ;
RenderScene ( pri , true , true ) ;
}
2016-03-21 04:10:14 +00:00
}
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
2017-04-20 19:43:41 +00:00
glDisable ( GL_STENCIL_TEST ) ;
2016-03-21 04:10:14 +00:00
glDisable ( GL_CULL_FACE ) ;
2017-08-10 15:43:03 +00:00
glDisableVertexAttribArray ( 0 ) ;
glDisableVertexAttribArray ( 1 ) ;
glDisableVertexAttribArray ( 2 ) ;
glDisableVertexAttribArray ( 3 ) ;
2017-08-14 09:14:06 +00:00
glDisableVertexAttribArray ( 4 ) ;
2016-03-21 04:10:14 +00:00
}
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-06-10 10:18:47 +00:00
m - > page = m_nodeAttribs . currentPage ;
2017-07-29 16:30:30 +00:00
m - > scale = m_nodeAttribs . currentModelScale ;
2016-03-26 22:44:26 +00:00
if ( ! cached ) {
CacheModel ( m , modelAddress ) ;
}
2016-03-21 04:10:14 +00:00
2017-02-07 14:05:03 +00:00
if ( m_nodeAttribs . currentClipStatus ! = Clip : : INSIDE ) {
ClipModel ( m ) ; // not storing clipped values, only working out the Z range
}
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-06-16 20:05:29 +00:00
BBox bbox ;
2017-04-17 10:40:07 +00:00
UINT16 uCullRadius ;
float fCullRadius ;
UINT16 uBlendRadius ;
float fBlendRadius ;
UINT8 lodTablePointer ;
2016-03-21 04:10:14 +00:00
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 ;
2017-04-17 10:40:07 +00:00
lodTablePointer = ( node [ 0x03 - m_offset ] > > 12 ) & 0x7F ;
2016-03-21 04:10:14 +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-03-21 04:10:14 +00:00
m_nodeAttribs . Push ( ) ; // save current attribs
2017-04-25 00:10:55 +00:00
if ( ! m_offset ) { // Step 1.5+
2017-07-29 16:30:30 +00:00
float modelScale = * ( float * ) & node [ 1 ] ;
2017-08-01 23:48:53 +00:00
if ( modelScale > std : : numeric_limits < float > : : min ( ) ) {
2017-07-29 16:30:30 +00:00
m_nodeAttribs . currentModelScale = modelScale ;
}
2016-03-21 04:10:14 +00:00
// apply texture offsets, else retain current ones
if ( ( node [ 0x02 ] & 0x8000 ) ) {
2017-04-25 00:10:55 +00:00
int tx = 32 * ( ( node [ 0x02 ] > > 7 ) & 0x3F ) ;
int ty = 32 * ( node [ 0x02 ] & 0x1F ) ;
m_nodeAttribs . currentTexOffsetX = tx ;
2016-03-21 04:10:14 +00:00
m_nodeAttribs . currentTexOffsetY = ty ;
2016-06-10 10:18:47 +00:00
m_nodeAttribs . currentPage = ( node [ 0x02 ] & 0x4000 ) > > 14 ;
2016-03-21 04:10:14 +00:00
}
}
// Apply matrix and translation
m_modelMat . PushMatrix ( ) ;
// apply translation vector
2017-07-29 16:30:30 +00:00
if ( node [ 0x00 ] & 0x10 ) {
2017-04-25 00:10:55 +00:00
float x = * ( float * ) & node [ 0x04 - m_offset ] ;
float y = * ( float * ) & node [ 0x05 - m_offset ] ;
float z = * ( float * ) & node [ 0x06 - m_offset ] ;
2016-03-21 04:10:14 +00:00
m_modelMat . Translate ( x , y , z ) ;
}
// multiply matrix, if specified
else if ( matrixOffset ) {
MultMatrix ( matrixOffset , m_modelMat ) ;
2016-06-16 20:05:29 +00:00
}
2016-03-21 04:10:14 +00:00
2017-04-17 10:40:07 +00:00
uCullRadius = node [ 9 - m_offset ] & 0xFFFF ;
fCullRadius = R3DFloat : : GetFloat16 ( uCullRadius ) ;
uBlendRadius = node [ 9 - m_offset ] > > 16 ;
fBlendRadius = R3DFloat : : GetFloat16 ( uBlendRadius ) ;
2016-06-16 20:05:29 +00:00
2017-04-12 15:47:57 +00:00
if ( m_nodeAttribs . currentClipStatus ! = Clip : : INSIDE ) {
2016-06-16 20:05:29 +00:00
2017-04-17 10:40:07 +00:00
if ( uCullRadius ! = R3DFloat : : Pro16BitMax ) {
2016-11-27 19:30:46 +00:00
2017-04-17 10:40:07 +00:00
CalcBox ( fCullRadius , bbox ) ;
2016-11-27 19:30:46 +00:00
TransformBox ( m_modelMat , bbox ) ;
m_nodeAttribs . currentClipStatus = ClipBox ( bbox , m_planes ) ;
2017-02-07 14:05:03 +00:00
if ( m_nodeAttribs . currentClipStatus = = Clip : : INSIDE ) {
CalcBoxExtents ( bbox ) ;
}
2016-11-27 19:30:46 +00:00
}
2017-02-05 15:36:05 +00:00
else {
m_nodeAttribs . currentClipStatus = Clip : : NOT_SET ;
}
2016-06-16 20:05:29 +00:00
}
2017-04-17 10:40:07 +00:00
if ( m_nodeAttribs . currentClipStatus ! = Clip : : OUTSIDE & & fCullRadius > R3DFloat : : Pro16BitFltMin ) {
2016-06-16 20:05:29 +00:00
// Descend down first link
if ( ( node [ 0x00 ] & 0x08 ) ) // 4-element LOD table
{
lodTable = TranslateCullingAddress ( child1Ptr ) ;
if ( NULL ! = lodTable ) {
if ( ( node [ 0x03 - m_offset ] & 0x20000000 ) ) {
DescendCullingNode ( lodTable [ 0 ] & 0xFFFFFF ) ;
}
else {
DrawModel ( lodTable [ 0 ] & 0xFFFFFF ) ; //TODO
}
2016-03-21 04:10:14 +00:00
}
}
2016-06-16 20:05:29 +00:00
else {
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
2016-05-03 21:17:37 +00:00
2016-05-05 22:01:59 +00:00
// create node object
m_nodes . emplace_back ( Node ( ) ) ;
2017-02-07 14:05:03 +00:00
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
2017-05-06 16:47:31 +00:00
vp - > priority = ( vpnode [ 0 ] > > 3 ) & 0x3 ;
vp - > select = ( vpnode [ 0 ] > > 8 ) & 0x3 ;
vp - > number = ( vpnode [ 0 ] > > 10 ) ;
m_currentPriority = vp - > priority ;
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?)
2017-02-07 14:05:03 +00:00
vp - > vpX = ( int ) ( ( ( vpnode [ 0x1A ] & 0xFFFF ) / 16.0f ) + 0.5f ) ; // viewport X (12.4 fixed point)
vp - > vpY = ( int ) ( ( ( vpnode [ 0x1A ] > > 16 ) / 16.0f ) + 0.5f ) ; // viewport Y (12.4)
vp - > vpWidth = ( int ) ( ( ( vpnode [ 0x14 ] & 0xFFFF ) / 4.0f ) + 0.5f ) ; // width (14.2)
vp - > 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
2017-05-06 16:47:31 +00:00
m_LODBlendTable = ( LODBlendTable * ) TranslateCullingAddress ( vpnode [ 0x17 ] & 0xFFFFFF ) ;
2016-04-02 22:13:59 +00:00
2017-02-07 14:05:03 +00:00
vp - > angle_left = - atan2 ( * ( float * ) & vpnode [ 12 ] , * ( float * ) & vpnode [ 13 ] ) ;
vp - > angle_right = atan2 ( * ( float * ) & vpnode [ 16 ] , - * ( float * ) & vpnode [ 17 ] ) ;
vp - > angle_top = atan2 ( * ( float * ) & vpnode [ 14 ] , * ( float * ) & vpnode [ 15 ] ) ;
vp - > angle_bottom = - atan2 ( * ( float * ) & vpnode [ 18 ] , - * ( float * ) & vpnode [ 19 ] ) ;
2016-05-05 22:01:59 +00:00
2017-03-07 01:29:07 +00:00
// I don't really know what these paramaters are for. They are derived from the view angles somehow.
// In the sdk it's they are someting like (1/tan(left)-tan(right)) * tan(left)
// But we know when left=right the value is always 0.5. Any other value and we have off-axis projection
2017-05-06 16:47:31 +00:00
// My theory is, the h/w is using these values to actually set the frustum planes angles
2017-03-07 01:29:07 +00:00
// The above vpnode[12], vpnode[13] actually work as a normal vector for the clipping planes (used for culling)
// I think in lost world and dirt devils, there is a missmatch between the computed plane normals, and the view angles
if ( * ( float * ) & vpnode [ 0xa ] = = 0.5f ) {
vp - > angle_bottom = - vp - > angle_top ;
}
if ( * ( float * ) & vpnode [ 0xb ] = = 0.5f ) {
vp - > angle_right = - vp - > angle_left ;
}
2017-02-07 14:05:03 +00:00
// calculate the frustum shape, near/far pair are dummy values
CalcViewport ( vp , 1 , 1000 ) ;
2016-03-21 04:10:14 +00:00
2016-06-16 20:05:29 +00:00
// calculate frustum planes
2017-02-07 14:05:03 +00:00
CalcFrustumPlanes ( m_planes , vp - > projectionMatrix ) ; // we need to calc a 'projection matrix' to get the correct frustum planes for clipping
2016-06-16 20:05:29 +00:00
2016-05-05 22:01:59 +00:00
// Lighting (note that sun vector points toward sun -- away from vertex)
2017-06-25 19:50:02 +00:00
vp - > lightingParams [ 0 ] = * ( float * ) & vpnode [ 0x05 ] ; // sun X
vp - > lightingParams [ 1 ] = - * ( float * ) & vpnode [ 0x06 ] ; // sun Y (- to convert to ogl cordinate system)
vp - > lightingParams [ 2 ] = - * ( float * ) & vpnode [ 0x04 ] ; // sun Z (- to convert to ogl cordinate system)
2017-06-20 20:13:42 +00:00
vp - > lightingParams [ 3 ] = std : : max ( 0.f , std : : min ( * ( float * ) & vpnode [ 0x07 ] , 1.0f ) ) ; // sun intensity (clamp to 0-1)
2016-05-05 22:01:59 +00:00
vp - > lightingParams [ 4 ] = ( float ) ( ( vpnode [ 0x24 ] > > 8 ) & 0xFF ) * ( 1.0f / 255.0f ) ; // ambient intensity
vp - > lightingParams [ 5 ] = 0.0 ; // reserved
2017-08-14 09:14:06 +00:00
vp - > sunClamp = 1 ; // TODO work out how this is passed, doesn't appear to be in the viewport .. or in the model data
vp - > intensityClamp = ( m_step = = 0x10 ) ; // just step 1.0 ?
vp - > hardwareStep = m_step ;
2017-06-20 20:13:42 +00:00
2016-05-05 22:01:59 +00:00
// Spotlight
2016-06-29 20:53:19 +00:00
int spotColorIdx = ( vpnode [ 0x20 ] > > 11 ) & 7 ; // spotlight color index
2017-04-02 21:03:59 +00:00
int spotFogColorIdx = ( vpnode [ 0x20 ] > > 8 ) & 7 ; // spotlight on fog color index
vp - > spotEllipse [ 0 ] = ( float ) ( INT16 ) ( vpnode [ 0x1E ] & 0xFFFF ) / 8.0f ; // spotlight X position (13.3 fixed point)
vp - > spotEllipse [ 1 ] = ( float ) ( INT16 ) ( vpnode [ 0x1D ] & 0xFFFF ) / 8.0f ; // spotlight Y
vp - > spotEllipse [ 2 ] = ( float ) ( ( vpnode [ 0x1E ] > > 16 ) & 0xFFFF ) ; // spotlight X size (16-bit)
2016-06-29 20:53:19 +00:00
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
2017-04-02 21:03:59 +00:00
// Star Wars Trilogy needs this
vp - > spotRange [ 0 ] = std : : min ( vp - > spotRange [ 0 ] , std : : numeric_limits < float > : : max ( ) ) ;
vp - > spotRange [ 0 ] = std : : max ( vp - > spotRange [ 0 ] , std : : numeric_limits < float > : : lowest ( ) ) ;
2016-06-29 20:53:19 +00:00
vp - > spotColor [ 0 ] = color [ spotColorIdx ] [ 0 ] ; // spotlight color
2016-05-05 22:01:59 +00:00
vp - > spotColor [ 1 ] = color [ spotColorIdx ] [ 1 ] ;
vp - > spotColor [ 2 ] = color [ spotColorIdx ] [ 2 ] ;
2017-04-02 21:03:59 +00:00
vp - > spotFogColor [ 0 ] = color [ spotFogColorIdx ] [ 0 ] ; // spotlight color on fog
vp - > spotFogColor [ 1 ] = color [ spotFogColorIdx ] [ 1 ] ;
vp - > spotFogColor [ 2 ] = color [ spotFogColorIdx ] [ 2 ] ;
// spotlight is specified in terms of physical resolution
vp - > spotEllipse [ 1 ] = 384.0f - vp - > spotEllipse [ 1 ] ; // flip Y position
// Avoid division by zero
vp - > spotEllipse [ 2 ] = std : : max ( 1.0f , vp - > spotEllipse [ 2 ] ) ;
vp - > spotEllipse [ 3 ] = std : : max ( 1.0f , vp - > spotEllipse [ 3 ] ) ;
vp - > spotEllipse [ 2 ] = std : : roundf ( 2047.0f / vp - > spotEllipse [ 2 ] ) ;
vp - > spotEllipse [ 3 ] = std : : roundf ( 2047.0f / vp - > spotEllipse [ 3 ] ) ;
2016-05-05 22:01:59 +00:00
// 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
2016-10-08 15:15:25 +00:00
vp - > fogParams [ 3 ] = std : : abs ( * ( float * ) & vpnode [ 0x23 ] ) ; // fog density - ocean hunter uses negative values, but looks the same
2016-05-05 22:01:59 +00:00
vp - > fogParams [ 4 ] = ( float ) ( INT16 ) ( vpnode [ 0x25 ] & 0xFFFF ) * ( 1.0f / 255.0f ) ; // fog start
2017-04-02 21:03:59 +00:00
// Avoid Infinite and NaN values for Star Wars Trilogy
if ( std : : isinf ( vp - > fogParams [ 3 ] ) | | std : : isnan ( vp - > fogParams [ 3 ] ) ) {
for ( int i = 0 ; i < 7 ; i + + ) vp - > fogParams [ i ] = 0.0f ;
}
2016-06-27 17:35:27 +00:00
2017-04-02 21:03:59 +00:00
vp - > fogParams [ 5 ] = ( float ) ( ( vpnode [ 0x24 ] > > 16 ) & 0xFF ) * ( 1.0f / 255.0f ) ; // fog attenuation
vp - > fogParams [ 6 ] = ( float ) ( ( vpnode [ 0x25 ] > > 16 ) & 0xFF ) * ( 1.0f / 255.0f ) ; // fog ambient
2016-06-27 17:35:27 +00:00
2017-04-02 21:03:59 +00:00
vp - > scrollFog = ( float ) ( vpnode [ 0x20 ] & 0xFF ) * ( 1.0f / 255.0f ) ; // scroll fog
vp - > 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
2017-05-06 16:47:31 +00:00
DescendNodePtr ( vpnode [ 0x02 ] ) ;
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 ) ;
2017-02-20 17:22:32 +00:00
clockWise = dotProd > = 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
2017-08-14 09:14:06 +00:00
// Copy face colour to vertices
2016-05-07 15:28:04 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
2017-08-14 09:14:06 +00:00
p . p1 . color [ i ] = r3dPoly . faceColour [ i ] ;
p . p2 . color [ i ] = r3dPoly . faceColour [ i ] ;
p . p3 . color [ i ] = r3dPoly . faceColour [ i ] ;
2016-05-07 15:28:04 +00:00
}
2016-03-21 04:10:14 +00:00
polyArray . emplace_back ( p ) ;
if ( r3dPoly . number = = 4 ) {
2017-02-21 23:35:40 +00:00
V3 : : createNormal ( r3dPoly . v [ 0 ] . pos , r3dPoly . v [ 2 ] . pos , r3dPoly . v [ 3 ] . pos , normal ) ;
dotProd = V3 : : dotProduct ( normal , r3dPoly . faceNormal ) ;
clockWise = dotProd > = 0 ;
2016-03-21 04:10:14 +00:00
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 ] ;
}
2017-08-14 09:14:06 +00:00
// Copy face colour to vertices
2016-05-07 15:28:04 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
2017-08-14 09:14:06 +00:00
p . p1 . color [ i ] = r3dPoly . faceColour [ i ] ;
p . p2 . color [ i ] = r3dPoly . faceColour [ i ] ;
p . p3 . color [ i ] = r3dPoly . faceColour [ i ] ;
2016-05-07 15:28:04 +00:00
}
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-12-06 12:25:34 +00:00
void CNew3D : : SetMeshValues ( SortingMesh * currentMesh , PolyHeader & ph )
{
//copy attributes
currentMesh - > doubleSided = false ; // we will double up polys
currentMesh - > textured = ph . TexEnabled ( ) ;
currentMesh - > alphaTest = ph . AlphaTest ( ) ;
currentMesh - > textureAlpha = ph . TextureAlpha ( ) ;
currentMesh - > polyAlpha = ph . PolyAlpha ( ) ;
2017-08-14 09:14:06 +00:00
currentMesh - > lighting = ph . LightEnabled ( ) ;
currentMesh - > fixedShading = ph . FixedShading ( ) & & ph . TexEnabled ( ) & & ! ph . SmoothShading ( ) ;
2017-02-20 17:22:32 +00:00
currentMesh - > highPriority = ph . HighPriority ( ) ;
2016-12-06 12:25:34 +00:00
if ( ph . Layered ( ) | | ( ! ph . TexEnabled ( ) & & ph . PolyAlpha ( ) ) ) {
currentMesh - > layered = true ;
}
2017-08-09 16:56:56 +00:00
currentMesh - > specular = ph . SpecularEnabled ( ) ;
currentMesh - > shininess = ph . Shininess ( ) ;
currentMesh - > specularValue = ph . SpecularValue ( ) ;
2016-12-06 12:25:34 +00:00
currentMesh - > fogIntensity = ph . LightModifier ( ) ;
2016-12-09 14:13:46 +00:00
if ( currentMesh - > textured ) {
2016-12-06 12:25:34 +00:00
currentMesh - > format = m_texSheet . GetTexFormat ( ph . TexFormat ( ) , ph . AlphaTest ( ) ) ;
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
}
currentMesh - > x = ph . X ( ) ;
currentMesh - > y = ph . Y ( ) ;
currentMesh - > width = ph . TexWidth ( ) ;
currentMesh - > height = ph . TexHeight ( ) ;
currentMesh - > mirrorU = ph . TexUMirror ( ) ;
currentMesh - > mirrorV = ph . TexVMirror ( ) ;
currentMesh - > microTexture = ph . MicroTexture ( ) ;
2017-04-05 17:57:38 +00:00
currentMesh - > inverted = ph . TranslatorMapOffset ( ) = = 2 ;
2016-12-09 14:13:46 +00:00
if ( currentMesh - > microTexture ) {
2017-03-12 19:00:00 +00:00
float microTexScale [ ] = { 4 , 8 , 16 , 32 } ;
2016-12-09 14:13:46 +00:00
currentMesh - > microTextureID = ph . MicroTextureID ( ) ;
currentMesh - > microTextureScale = microTexScale [ ph . MicroTextureMinLOD ( ) ] ;
}
2016-12-06 12:25:34 +00:00
}
}
2016-03-21 04:10:14 +00:00
void CNew3D : : CacheModel ( Model * m , const UINT32 * data )
{
Vertex prev [ 4 ] ;
2016-12-15 12:10:52 +00:00
UINT16 texCoords [ 4 ] [ 2 ] ;
2016-12-06 14:39:46 +00:00
UINT16 prevTexCoords [ 4 ] [ 2 ] ;
2016-03-21 04:10:14 +00:00
PolyHeader ph ;
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
2016-12-06 14:39:46 +00:00
float uvScale ;
2016-03-21 04:10:14 +00:00
int i , j ;
if ( ph . header [ 6 ] = = 0 ) {
break ;
}
// 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 ) ;
2016-12-06 12:25:34 +00:00
//set mesh values
SetMeshValues ( currentMesh , ph ) ;
2016-03-21 04:10:14 +00:00
}
currentMesh = & sMap [ hash ] ;
}
// 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 ] ;
2016-12-06 14:39:46 +00:00
2016-12-15 12:10:52 +00:00
texCoords [ j ] [ 0 ] = prevTexCoords [ i ] [ 0 ] ;
texCoords [ j ] [ 1 ] = prevTexCoords [ i ] [ 1 ] ;
2016-12-06 14:39:46 +00:00
//check if we need to recalc tex coords - will only happen if tex tiles are different + sharing vertices
if ( hash ! = lastHash ) {
if ( currentMesh - > textured ) {
2016-12-15 12:10:52 +00:00
Texture : : GetCoordinates ( currentMesh - > width , currentMesh - > height , texCoords [ j ] [ 0 ] , texCoords [ j ] [ 1 ] , uvScale , p . v [ j ] . texcoords [ 0 ] , p . v [ j ] . texcoords [ 1 ] ) ;
2016-12-06 14:39:46 +00:00
}
}
j + + ;
2016-03-21 04:10:14 +00:00
}
}
2016-12-06 14:39:46 +00:00
lastHash = hash ;
2016-05-07 15:28:04 +00:00
// copy face attributes
2017-08-14 09:14:06 +00:00
if ( ! ph . PolyColor ( ) ) {
2016-05-28 20:49:10 +00:00
int colorIdx = ph . ColorIndex ( ) ;
2017-08-14 09:14:06 +00:00
p . faceColour [ 2 ] = ( m_polyRAM [ m_colorTableAddr + colorIdx ] & 0xFF ) ;
p . faceColour [ 1 ] = ( ( m_polyRAM [ m_colorTableAddr + colorIdx ] > > 8 ) & 0xFF ) ;
p . faceColour [ 0 ] = ( ( m_polyRAM [ m_colorTableAddr + colorIdx ] > > 16 ) & 0xFF ) ;
2016-05-07 15:45:02 +00:00
}
else {
2017-02-16 17:20:42 +00:00
2017-08-14 09:14:06 +00:00
p . faceColour [ 0 ] = ( ( ph . header [ 4 ] > > 24 ) ) ;
p . faceColour [ 1 ] = ( ( ph . header [ 4 ] > > 16 ) & 0xFF ) ;
p . faceColour [ 2 ] = ( ( ph . header [ 4 ] > > 8 ) & 0xFF ) ;
2017-02-16 17:20:42 +00:00
if ( ph . TranslatorMap ( ) ) {
2017-08-14 09:14:06 +00:00
p . faceColour [ 0 ] = ( p . faceColour [ 0 ] * 255 ) / 16 ; // When the translator map is enabled, max colour seems
p . faceColour [ 1 ] = ( p . faceColour [ 1 ] * 255 ) / 16 ; // to be 16. Scaling these up gives the correct colours.
p . faceColour [ 2 ] = ( p . faceColour [ 2 ] * 255 ) / 16 ; // Not sure why didn't allow 32 colours with 4 bits?
2016-05-07 15:28:04 +00:00
}
}
2017-08-14 09:14:06 +00:00
p . faceColour [ 3 ] = ph . Transparency ( ) ;
2017-04-15 19:00:46 +00:00
if ( ph . Discard1 ( ) & & ! ph . Discard2 ( ) ) {
2017-08-14 09:14:06 +00:00
p . faceColour [ 3 ] / = 2 ;
2017-04-15 19:00:46 +00:00
}
2016-05-27 22:20:57 +00:00
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
2017-04-20 19:46:40 +00:00
p . v [ j ] . pos [ 0 ] = ( ( ( INT32 ) ix ) > > 8 ) * m_vertexFactor ;
p . v [ j ] . pos [ 1 ] = ( ( ( INT32 ) iy ) > > 8 ) * m_vertexFactor ;
p . v [ j ] . pos [ 2 ] = ( ( ( INT32 ) iz ) > > 8 ) * m_vertexFactor ;
2016-03-21 04:10:14 +00:00
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-11-11 17:50:27 +00:00
if ( ph . FixedShading ( ) & & ph . TexEnabled ( ) & & ! ph . SmoothShading ( ) ) { // fixed shading seems to be disabled if actual normals are set
2017-06-17 16:05:54 +00:00
2017-08-14 09:14:06 +00:00
//==========
UINT8 shade ;
//==========
2017-06-17 16:05:54 +00:00
2017-06-20 20:13:42 +00:00
if ( m_step < = 0x15 ) {
2017-08-14 09:14:06 +00:00
shade = ( ( ix & 0x7F ) * 255 ) / 127 ; // this matches the sdk (values are from 0-127 only) and the intensity is clamped to to 0-1
2017-06-17 16:05:54 +00:00
}
else {
2017-06-21 17:04:53 +00:00
if ( ph . SpecularEnabled ( ) ) {
2017-08-14 09:14:06 +00:00
shade = ix & 0xFF ; // Star wars is the only game to use unsigned fixed shaded values. It's also the only game to set the specular flag on these polys
2017-06-21 17:04:53 +00:00
}
else {
2017-08-14 09:14:06 +00:00
shade = ( ix + 128 ) & 0xFF ; // Step 2+ uses signed or unsigned values for lighting 0-255. Todo finish this logic
2017-06-21 17:04:53 +00:00
}
2017-06-17 16:05:54 +00:00
}
2017-08-14 23:30:34 +00:00
p . v [ j ] . fixedShade = shade ; // hardware doesn't really have per vertex colours, only per poly
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-12-06 14:39:46 +00:00
//cache un-normalised tex coordinates
2016-12-15 12:10:52 +00:00
texCoords [ j ] [ 0 ] = ( UINT16 ) ( it > > 16 ) ;
texCoords [ j ] [ 1 ] = ( UINT16 ) ( it & 0xFFFF ) ;
2016-12-06 14:39:46 +00:00
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
2017-04-15 19:00:46 +00:00
if ( ph . DoubleSided ( ) & & ! ph . Discard ( ) ) {
2016-05-13 08:39:48 +00:00
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
2017-04-15 19:00:46 +00:00
if ( ! ph . Discard ( ) ) {
2017-04-14 23:41:11 +00:00
CopyVertexData ( p , currentMesh - > polys ) ;
}
2016-05-13 08:39:48 +00:00
// Copy current vertices into previous vertex array
for ( i = 0 ; i < 4 ; i + + ) {
prev [ i ] = p . v [ i ] ;
2016-12-15 12:10:52 +00:00
prevTexCoords [ i ] [ 0 ] = texCoords [ i ] [ 0 ] ;
prevTexCoords [ i ] [ 1 ] = texCoords [ i ] [ 1 ] ;
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-12-22 22:11:19 +00:00
void CNew3D : : CalcFrustumPlanes ( Plane p [ 4 ] , const float * matrix )
2016-06-16 20:05:29 +00:00
{
// Left Plane
2016-11-26 01:19:51 +00:00
p [ 0 ] . a = matrix [ 3 ] + matrix [ 0 ] ;
p [ 0 ] . b = matrix [ 7 ] + matrix [ 4 ] ;
p [ 0 ] . c = matrix [ 11 ] + matrix [ 8 ] ;
p [ 0 ] . d = matrix [ 15 ] + matrix [ 12 ] ;
p [ 0 ] . Normalise ( ) ;
2016-06-16 20:05:29 +00:00
// Right Plane
2016-11-26 01:19:51 +00:00
p [ 1 ] . a = matrix [ 3 ] - matrix [ 0 ] ;
p [ 1 ] . b = matrix [ 7 ] - matrix [ 4 ] ;
p [ 1 ] . c = matrix [ 11 ] - matrix [ 8 ] ;
p [ 1 ] . d = matrix [ 15 ] - matrix [ 12 ] ;
p [ 1 ] . Normalise ( ) ;
2016-06-16 20:05:29 +00:00
// Bottom Plane
2016-11-26 01:19:51 +00:00
p [ 2 ] . a = matrix [ 3 ] + matrix [ 1 ] ;
p [ 2 ] . b = matrix [ 7 ] + matrix [ 5 ] ;
p [ 2 ] . c = matrix [ 11 ] + matrix [ 9 ] ;
p [ 2 ] . d = matrix [ 15 ] + matrix [ 13 ] ;
p [ 2 ] . Normalise ( ) ;
2016-06-16 20:05:29 +00:00
// Top Plane
2016-11-26 01:19:51 +00:00
p [ 3 ] . a = matrix [ 3 ] - matrix [ 1 ] ;
p [ 3 ] . b = matrix [ 7 ] - matrix [ 5 ] ;
p [ 3 ] . c = matrix [ 11 ] - matrix [ 9 ] ;
p [ 3 ] . d = matrix [ 15 ] - matrix [ 13 ] ;
p [ 3 ] . Normalise ( ) ;
2016-06-16 20:05:29 +00:00
}
void CNew3D : : CalcBox ( float distance , BBox & box )
{
//bottom left front
box . points [ 0 ] [ 0 ] = - distance ;
box . points [ 0 ] [ 1 ] = - distance ;
box . points [ 0 ] [ 2 ] = distance ;
box . points [ 0 ] [ 3 ] = 1 ;
//bottom left back
box . points [ 1 ] [ 0 ] = - distance ;
box . points [ 1 ] [ 1 ] = - distance ;
box . points [ 1 ] [ 2 ] = - distance ;
box . points [ 1 ] [ 3 ] = 1 ;
//bottom right back
box . points [ 2 ] [ 0 ] = distance ;
box . points [ 2 ] [ 1 ] = - distance ;
box . points [ 2 ] [ 2 ] = - distance ;
box . points [ 2 ] [ 3 ] = 1 ;
//bottom right front
box . points [ 3 ] [ 0 ] = distance ;
box . points [ 3 ] [ 1 ] = - distance ;
box . points [ 3 ] [ 2 ] = distance ;
box . points [ 3 ] [ 3 ] = 1 ;
//top left front
box . points [ 4 ] [ 0 ] = - distance ;
box . points [ 4 ] [ 1 ] = distance ;
box . points [ 4 ] [ 2 ] = distance ;
box . points [ 4 ] [ 3 ] = 1 ;
//top left back
box . points [ 5 ] [ 0 ] = - distance ;
box . points [ 5 ] [ 1 ] = distance ;
box . points [ 5 ] [ 2 ] = - distance ;
box . points [ 5 ] [ 3 ] = 1 ;
//top right back
box . points [ 6 ] [ 0 ] = distance ;
box . points [ 6 ] [ 1 ] = distance ;
box . points [ 6 ] [ 2 ] = - distance ;
box . points [ 6 ] [ 3 ] = 1 ;
//top right front
box . points [ 7 ] [ 0 ] = distance ;
box . points [ 7 ] [ 1 ] = distance ;
box . points [ 7 ] [ 2 ] = distance ;
box . points [ 7 ] [ 3 ] = 1 ;
}
void CNew3D : : MultVec ( const float matrix [ 16 ] , const float in [ 4 ] , float out [ 4 ] )
{
for ( int i = 0 ; i < 4 ; i + + ) {
out [ i ] =
in [ 0 ] * matrix [ 0 * 4 + i ] +
in [ 1 ] * matrix [ 1 * 4 + i ] +
in [ 2 ] * matrix [ 2 * 4 + i ] +
in [ 3 ] * matrix [ 3 * 4 + i ] ;
}
}
void CNew3D : : TransformBox ( const float * m , BBox & box )
{
for ( int i = 0 ; i < 8 ; i + + ) {
float v [ 4 ] ;
MultVec ( m , box . points [ i ] , v ) ;
box . points [ i ] [ 0 ] = v [ 0 ] ;
box . points [ i ] [ 1 ] = v [ 1 ] ;
box . points [ i ] [ 2 ] = v [ 2 ] ;
}
}
2016-12-22 22:09:13 +00:00
Clip CNew3D : : ClipBox ( BBox & box , Plane planes [ 4 ] )
2016-06-16 20:05:29 +00:00
{
int count = 0 ;
for ( int i = 0 ; i < 8 ; i + + ) {
int temp = 0 ;
2016-12-22 22:09:13 +00:00
for ( int j = 0 ; j < 4 ; j + + ) {
2016-06-16 20:05:29 +00:00
if ( planes [ j ] . DistanceToPoint ( box . points [ i ] ) > = 0 ) {
temp + + ;
}
}
2016-12-22 22:09:13 +00:00
if ( temp = = 4 ) count + + ; // point is inside all 4 frustum planes
2016-06-16 20:05:29 +00:00
}
if ( count = = 8 ) return Clip : : INSIDE ;
if ( count > 0 ) return Clip : : INTERCEPT ;
//if we got here all points are outside of the view frustum
//check for all points being side same of any plane, means box outside of view
2016-12-22 22:09:13 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
2016-06-16 20:05:29 +00:00
int temp = 0 ;
for ( int j = 0 ; j < 8 ; j + + ) {
if ( planes [ i ] . DistanceToPoint ( box . points [ j ] ) > = 0 ) {
float distance = planes [ i ] . DistanceToPoint ( box . points [ j ] ) ;
temp + + ;
}
}
2016-12-22 22:09:13 +00:00
if ( temp = = 0 ) {
return Clip : : OUTSIDE ;
}
2016-06-16 20:05:29 +00:00
}
//if we got here, box is traversing view frustum
return Clip : : INTERCEPT ;
}
2017-02-07 14:05:03 +00:00
void CNew3D : : CalcBoxExtents ( const BBox & box )
{
for ( int i = 0 ; i < 8 ; i + + ) {
if ( box . points [ i ] [ 2 ] < 0 ) {
m_nfPairs [ m_currentPriority ] . zNear = std : : max ( box . points [ i ] [ 2 ] , m_nfPairs [ m_currentPriority ] . zNear ) ;
m_nfPairs [ m_currentPriority ] . zFar = std : : min ( box . points [ i ] [ 2 ] , m_nfPairs [ m_currentPriority ] . zFar ) ;
}
}
}
void CNew3D : : ClipPolygon ( ClipPoly & clipPoly , Plane planes [ 4 ] )
{
//============
ClipPoly temp ;
ClipPoly * in ;
ClipPoly * out ;
//============
in = & clipPoly ;
out = & temp ;
for ( int i = 0 ; i < 4 ; i + + ) {
//=================
bool currentIn ;
bool nextIn ;
float currentDot ;
float nextDot ;
//=================
currentDot = planes [ i ] . DotProduct ( in - > list [ 0 ] . pos ) ;
currentIn = ( currentDot + planes [ i ] . d ) > = 0 ;
for ( int j = 0 ; j < in - > count ; j + + ) {
if ( currentIn ) {
out - > list [ out - > count ] = in - > list [ j ] ;
out - > count + + ;
}
int nextIndex = j + 1 ;
if ( nextIndex > = in - > count ) {
nextIndex = 0 ;
}
nextDot = planes [ i ] . DotProduct ( in - > list [ nextIndex ] . pos ) ;
nextIn = ( nextDot + planes [ i ] . d ) > = 0 ;
// we have an intersection
if ( currentIn ! = nextIn ) {
float u = ( currentDot + planes [ i ] . d ) / ( currentDot - nextDot ) ;
float * p1 = in - > list [ j ] . pos ;
float * p2 = in - > list [ nextIndex ] . pos ;
out - > list [ out - > count ] . pos [ 0 ] = p1 [ 0 ] + ( ( p2 [ 0 ] - p1 [ 0 ] ) * u ) ;
out - > list [ out - > count ] . pos [ 1 ] = p1 [ 1 ] + ( ( p2 [ 1 ] - p1 [ 1 ] ) * u ) ;
out - > list [ out - > count ] . pos [ 2 ] = p1 [ 2 ] + ( ( p2 [ 2 ] - p1 [ 2 ] ) * u ) ;
out - > count + + ;
}
currentDot = nextDot ;
currentIn = nextIn ;
}
std : : swap ( in , out ) ;
out - > count = 0 ;
}
}
void CNew3D : : ClipModel ( const Model * m )
{
//================
ClipPoly clipPoly ;
std : : vector < Poly > * polys ;
int offset ;
//================
if ( m - > dynamic ) {
polys = & m_polyBufferRam ;
offset = MAX_ROM_POLYS ;
}
else {
polys = & m_polyBufferRom ;
offset = 0 ;
}
for ( const auto & mesh : * m - > meshes ) {
int start = mesh . vboOffset - offset ;
for ( int i = 0 ; i < mesh . triangleCount ; i + + ) {
//==================================
Poly & poly = ( * polys ) [ start + i ] ;
float in [ 4 ] , out [ 4 ] ;
//==================================
memcpy ( in , poly . p1 . pos , sizeof ( float ) * 3 ) ;
in [ 3 ] = 1 ;
MultVec ( m - > modelMat , in , out ) ;
memcpy ( clipPoly . list [ 0 ] . pos , out , sizeof ( float ) * 3 ) ;
memcpy ( in , poly . p2 . pos , sizeof ( float ) * 3 ) ;
in [ 3 ] = 1 ;
MultVec ( m - > modelMat , in , out ) ;
memcpy ( clipPoly . list [ 1 ] . pos , out , sizeof ( float ) * 3 ) ;
memcpy ( in , poly . p3 . pos , sizeof ( float ) * 3 ) ;
in [ 3 ] = 1 ;
MultVec ( m - > modelMat , in , out ) ;
memcpy ( clipPoly . list [ 2 ] . pos , out , sizeof ( float ) * 3 ) ;
clipPoly . count = 3 ;
ClipPolygon ( clipPoly , m_planes ) ;
for ( int j = 0 ; j < clipPoly . count ; j + + ) {
if ( clipPoly . list [ j ] . pos [ 2 ] < 0 ) {
m_nfPairs [ m_currentPriority ] . zNear = std : : max ( clipPoly . list [ j ] . pos [ 2 ] , m_nfPairs [ m_currentPriority ] . zNear ) ;
m_nfPairs [ m_currentPriority ] . zFar = std : : min ( clipPoly . list [ j ] . pos [ 2 ] , m_nfPairs [ m_currentPriority ] . zFar ) ;
}
}
}
}
}
void CNew3D : : CalcViewport ( Viewport * vp , float near , float far )
{
2017-02-08 16:14:50 +00:00
if ( near < far / 1000000 ) {
near = far / 1000000 ; // if we get really close to zero somehow, we will have almost no depth precision
}
2017-02-07 14:05:03 +00:00
float l = near * tanf ( vp - > angle_left ) ; // we need to calc the shape of the projection frustum for culling
float r = near * tanf ( vp - > angle_right ) ;
float t = near * tanf ( vp - > angle_top ) ;
float b = near * tanf ( vp - > angle_bottom ) ;
vp - > projectionMatrix . LoadIdentity ( ) ; // reset matrix
if ( ( vp - > vpX = = 0 ) & & ( vp - > vpWidth > = 495 ) & & ( vp - > vpY = = 0 ) & & ( vp - > vpHeight > = 383 ) ) {
float windowAR = ( float ) m_totalXRes / ( float ) m_totalYRes ;
float originalAR = 496 / 384.f ;
float correction = windowAR / originalAR ; // expand horizontal frustum planes
vp - > x = 0 ;
vp - > y = m_yOffs + ( int ) ( ( 384 - ( vp - > vpY + vp - > vpHeight ) ) * m_yRatio ) ;
vp - > width = m_totalXRes ;
vp - > height = ( int ) ( vp - > vpHeight * m_yRatio ) ;
vp - > projectionMatrix . Frustum ( l * correction , r * correction , b , t , near , far ) ;
}
else {
vp - > x = m_xOffs + ( int ) ( vp - > vpX * m_xRatio ) ;
vp - > y = m_yOffs + ( int ) ( ( 384 - ( vp - > vpY + vp - > vpHeight ) ) * m_yRatio ) ;
vp - > width = ( int ) ( vp - > vpWidth * m_xRatio ) ;
vp - > height = ( int ) ( vp - > vpHeight * m_yRatio ) ;
vp - > projectionMatrix . Frustum ( l , r , b , t , near , far ) ;
}
}
2016-03-21 04:10:14 +00:00
} // New3D
2016-05-09 16:26:34 +00:00