2022-08-19 19:34:22 +00:00
# include "New3D.h"
2016-03-21 04:10:14 +00:00
# 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>
2022-08-19 19:34:22 +00:00
# include <cstring>
# include <unordered_map>
2016-06-19 21:43:09 +00:00
# include "R3DFloat.h"
2022-10-01 01:44:22 +00:00
# include "Util/BitCast.h"
2016-03-21 04:10:14 +00:00
2022-08-19 19:34:22 +00:00
# define MAX_RAM_VERTS 300000
2018-09-13 12:50:34 +00:00
# define MAX_ROM_VERTS 1500000
2016-03-26 22:44:26 +00:00
2022-08-19 19:34:22 +00:00
# define BYTE_TO_FLOAT(B) ((2.0f * (B) + 1.0f) * (float)(1.0 / 255.0))
2017-08-19 20:06:31 +00:00
2016-03-21 04:10:14 +00:00
namespace New3D {
2022-11-07 21:33:01 +00:00
CNew3D : : CNew3D ( const Util : : Config : : Node & config , const std : : string & gameName ) :
m_r3dShader ( config ) ,
m_r3dScrollFog ( config ) ,
m_gameName ( gameName ) ,
m_textureBuffer ( 0 ) ,
m_vao ( 0 )
2016-03-21 04:10:14 +00:00
{
m_cullingRAMLo = nullptr ;
m_cullingRAMHi = nullptr ;
m_polyRAM = nullptr ;
m_vrom = nullptr ;
2018-09-02 21:36:24 +00:00
m_textureRAM = nullptr ;
m_sunClamp = true ;
m_shadeIsSigned = true ;
2022-08-19 19:34:22 +00:00
m_numPolyVerts = 3 ;
2018-09-13 12:50:34 +00:00
m_primType = GL_TRIANGLES ;
if ( config [ " QuadRendering " ] . ValueAs < bool > ( ) ) {
m_numPolyVerts = 4 ;
m_primType = GL_LINES_ADJACENCY ;
}
2022-12-17 23:46:02 +00:00
m_r3dShader . LoadShader ( ) ;
glUseProgram ( 0 ) ;
// setup our texture memory
glGenTextures ( 1 , & m_textureBuffer ) ;
glBindTexture ( GL_TEXTURE_2D , m_textureBuffer ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_REPEAT ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_REPEAT ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_NEAREST ) ;
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_R16UI , 2048 , 2048 , 0 , GL_RED_INTEGER , GL_UNSIGNED_SHORT , nullptr ) ; // allocate storage
// setup up our vertex buffer memory
glGenVertexArrays ( 1 , & m_vao ) ;
glBindVertexArray ( m_vao ) ;
m_vbo . Create ( GL_ARRAY_BUFFER , GL_DYNAMIC_DRAW , sizeof ( FVertex ) * ( MAX_RAM_VERTS + MAX_ROM_VERTS ) ) ;
m_vbo . Bind ( true ) ;
glEnableVertexAttribArray ( m_r3dShader . GetVertexAttribPos ( " inVertex " ) ) ;
glEnableVertexAttribArray ( m_r3dShader . GetVertexAttribPos ( " inNormal " ) ) ;
glEnableVertexAttribArray ( m_r3dShader . GetVertexAttribPos ( " inTexCoord " ) ) ;
glEnableVertexAttribArray ( m_r3dShader . GetVertexAttribPos ( " inColour " ) ) ;
glEnableVertexAttribArray ( m_r3dShader . GetVertexAttribPos ( " inFaceNormal " ) ) ;
glEnableVertexAttribArray ( m_r3dShader . GetVertexAttribPos ( " inFixedShade " ) ) ;
// before draw, specify vertex and index arrays with their offsets, offsetof is maybe evil ..
glVertexAttribPointer ( m_r3dShader . GetVertexAttribPos ( " inVertex " ) , 4 , GL_FLOAT , GL_FALSE , sizeof ( FVertex ) , 0 ) ;
glVertexAttribPointer ( m_r3dShader . GetVertexAttribPos ( " inNormal " ) , 3 , GL_FLOAT , GL_FALSE , sizeof ( FVertex ) , ( void * ) offsetof ( FVertex , normal ) ) ;
glVertexAttribPointer ( m_r3dShader . GetVertexAttribPos ( " inTexCoord " ) , 2 , GL_FLOAT , GL_FALSE , sizeof ( FVertex ) , ( void * ) offsetof ( FVertex , texcoords ) ) ;
glVertexAttribPointer ( m_r3dShader . GetVertexAttribPos ( " inColour " ) , 4 , GL_UNSIGNED_BYTE , GL_TRUE , sizeof ( FVertex ) , ( void * ) offsetof ( FVertex , faceColour ) ) ;
glVertexAttribPointer ( m_r3dShader . GetVertexAttribPos ( " inFaceNormal " ) , 3 , GL_FLOAT , GL_FALSE , sizeof ( FVertex ) , ( void * ) offsetof ( FVertex , faceNormal ) ) ;
glVertexAttribPointer ( m_r3dShader . GetVertexAttribPos ( " inFixedShade " ) , 1 , GL_FLOAT , GL_FALSE , sizeof ( FVertex ) , ( void * ) offsetof ( FVertex , fixedShade ) ) ;
glBindVertexArray ( 0 ) ;
m_vbo . Bind ( false ) ;
2018-09-02 21:36:24 +00:00
}
CNew3D : : ~ CNew3D ( )
2016-03-21 04:10:14 +00:00
{
2016-03-26 22:44:26 +00:00
m_vbo . Destroy ( ) ;
2022-11-07 21:33:01 +00:00
if ( m_vao ) {
glDeleteVertexArrays ( 1 , & m_vao ) ;
m_vao = 0 ;
}
if ( m_textureBuffer ) {
glDeleteTextures ( 1 , & m_textureBuffer ) ;
m_textureBuffer = 0 ;
}
m_r3dShader . UnloadShader ( ) ;
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
}
}
bool CNew3D : : Init ( unsigned xOffset , unsigned yOffset , unsigned xRes , unsigned yRes , unsigned totalXResParam , unsigned totalYResParam )
{
// Resolution and offset within physical display area
2022-08-19 19:34:22 +00:00
m_xRatio = xRes * ( float ) ( 1.0 / 496.0 ) ;
m_yRatio = yRes * ( float ) ( 1.0 / 384.0 ) ;
2016-03-21 04:10:14 +00:00
m_xOffs = xOffset ;
m_yOffs = yOffset ;
2022-12-17 23:46:02 +00:00
m_xRes = xRes ;
m_yRes = yRes ;
2016-03-21 04:10:14 +00:00
m_totalXRes = totalXResParam ;
m_totalYRes = totalYResParam ;
2022-12-17 23:46:02 +00:00
m_r3dFrameBuffers . DestroyFBO ( ) ; // remove any old ones if created
2018-06-16 21:31:29 +00:00
m_r3dFrameBuffers . CreateFBO ( totalXResParam , totalYResParam ) ;
2022-11-07 21:33:01 +00:00
return OKAY ;
}
2017-03-25 00:06:24 +00:00
2022-11-07 21:33:01 +00:00
void CNew3D : : UploadTextures ( unsigned level , unsigned x , unsigned y , unsigned width , unsigned height )
{
glBindTexture ( GL_TEXTURE_2D , m_textureBuffer ) ;
glPixelStorei ( GL_UNPACK_ALIGNMENT , 2 ) ;
for ( unsigned i = 0 ; i < height ; i + + ) {
glTexSubImage2D ( GL_TEXTURE_2D , 0 , x , y + i , width , 1 , GL_RED_INTEGER , GL_UNSIGNED_SHORT , m_textureRAM + ( ( y + i ) * 2048 ) + x ) ;
2017-03-25 00:06:24 +00:00
}
2016-03-21 04:10:14 +00:00
}
2016-10-09 16:34:12 +00:00
void CNew3D : : DrawScrollFog ( )
{
2020-09-07 16:25:18 +00:00
// this is my best guess at the logic based upon what games are doing
2017-05-06 16:47:31 +00:00
//
// 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.
2023-10-17 16:30:14 +00:00
// 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. End credits have scroll fog, but constrained to the viewport
2017-05-06 16:47:31 +00:00
// 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
2023-10-17 16:30:14 +00:00
// I think the basic logic is this: the real3d picks the highest scroll fog value, starting from the lowest priority layer.
// If it finds a value for priority layer 0 for example, it then bails out looking for any more.
// Fogging seems to be constrained to whatever the viewport is that is set.
2023-10-22 20:07:35 +00:00
// Scroll fog needs a density or start value to work, but these can come from another viewport if the fog colour is the same
2017-04-02 21:03:59 +00:00
2023-10-17 16:30:14 +00:00
Node * nodePtr = nullptr ;
2020-09-07 16:25:18 +00:00
2023-10-17 16:30:14 +00:00
for ( int i = 0 ; i < 4 & & ! nodePtr ; i + + ) {
2020-09-07 16:25:18 +00:00
for ( auto & n : m_nodes ) {
if ( n . viewport . priority = = i ) {
2023-10-17 16:30:14 +00:00
if ( n . viewport . scrollFog > 0.f ) {
2016-10-09 16:34:12 +00:00
2023-10-17 16:30:14 +00:00
// check to see if we have a higher scroll fog value
if ( nodePtr ) {
if ( nodePtr - > viewport . scrollFog < n . viewport . scrollFog ) {
nodePtr = & n ;
}
2016-12-04 22:37:49 +00:00
2023-10-17 16:30:14 +00:00
continue ;
2020-09-07 16:25:18 +00:00
}
2023-10-17 16:30:14 +00:00
nodePtr = & n ;
}
2020-09-07 16:25:18 +00:00
}
2016-10-09 16:34:12 +00:00
}
}
2023-10-17 16:30:14 +00:00
if ( nodePtr ) {
2023-10-22 20:07:35 +00:00
// interate nodes to see if any viewports with that fog colour actually set a fog density or start value
// if both of these are zero fogging is effectively disabled
for ( auto & n : m_nodes ) {
if ( nodePtr - > viewport . fogParams [ 0 ] = = n . viewport . fogParams [ 0 ] & &
nodePtr - > viewport . fogParams [ 1 ] = = n . viewport . fogParams [ 1 ] & &
nodePtr - > viewport . fogParams [ 2 ] = = n . viewport . fogParams [ 2 ] )
{
// check to see if we have a fog start or density value
2023-11-05 17:39:23 +00:00
if ( n . viewport . fogParams [ 3 ] > 0.0f | | n . viewport . fogParams [ 4 ] > 0.0f | | n . viewport . scrollAtt > 0.0f ) {
2023-10-22 20:07:35 +00:00
float rgba [ 4 ] ;
auto & vp = nodePtr - > viewport ;
rgba [ 0 ] = vp . fogParams [ 0 ] ;
rgba [ 1 ] = vp . fogParams [ 1 ] ;
rgba [ 2 ] = vp . fogParams [ 2 ] ;
rgba [ 3 ] = vp . scrollFog ;
glViewport ( vp . x , vp . y , vp . width , vp . height ) ;
2023-11-05 23:44:27 +00:00
m_r3dScrollFog . DrawScrollFog ( rgba , n . viewport . scrollAtt , n . viewport . fogParams [ 6 ] , n . viewport . spotFogColor , n . viewport . spotEllipse ) ;
2023-11-05 17:39:23 +00:00
break ;
2023-10-22 20:07:35 +00:00
}
}
}
}
}
void CNew3D : : DrawAmbientFog ( )
{
// logic here is still not totally understood
// some games are setting fog ambient which seems to darken the 2d background layer too when scroll fogging is not set
// The logic is something like tileGenColour * fogAmbient
// If fogAmbient = 1.0 it's a no-op. Lower values darken the image
// Does this work with scroll fog? Well technically scroll fog already takes into account the fog ambient as it darkens the fog colour
// Let's pick the lowest fog ambient value
2023-10-24 09:17:15 +00:00
// Check for fog density or a fog start value, otherwise the effect seems to be disabled (lost world)
2023-10-22 20:07:35 +00:00
float fogAmbient = 1.0f ;
Node * nodePtr = nullptr ;
for ( auto & n : m_nodes ) {
2023-10-24 09:17:15 +00:00
// check to see if we have a fog density or fog start
if ( n . viewport . fogParams [ 3 ] < = 0.0f & & n . viewport . fogParams [ 4 ] < = 0.0f ) {
continue ;
}
2023-11-09 18:41:57 +00:00
if ( n . viewport . scrollAtt > 0.0f ) {
continue ; // scroll attenuation indicates scroll fog layer
}
2023-10-22 20:07:35 +00:00
if ( n . viewport . fogParams [ 6 ] < fogAmbient ) {
nodePtr = & n ;
fogAmbient = n . viewport . fogParams [ 6 ] ;
}
}
2023-10-24 09:17:15 +00:00
if ( nodePtr ) {
2023-10-17 16:30:14 +00:00
auto & vp = nodePtr - > viewport ;
2023-10-22 20:07:35 +00:00
float rgba [ ] = { 0.0f , 0.0f , 0.0f , 1.0f - fogAmbient } ;
2023-10-17 16:30:14 +00:00
glViewport ( vp . x , vp . y , vp . width , vp . height ) ;
2023-11-05 23:44:27 +00:00
m_r3dScrollFog . DrawScrollFog ( rgba , 0.0f , 1.0f , vp . spotFogColor , vp . spotEllipse ) ; // we assume spot light is not used
2023-10-17 16:30:14 +00:00
}
2016-10-09 16:34:12 +00:00
}
2018-06-16 21:31:29 +00:00
bool CNew3D : : RenderScene ( int priority , bool renderOverlay , Layer layer )
2016-03-21 04:10:14 +00:00
{
2022-11-07 21:33:01 +00:00
glActiveTexture ( GL_TEXTURE0 ) ;
glBindTexture ( GL_TEXTURE_2D , m_textureBuffer ) ;
2017-02-20 17:22:32 +00:00
bool hasOverlay = false ; // (high priority polys)
2016-03-21 04:10:14 +00:00
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 ;
}
2020-01-04 18:15:30 +00:00
CalcViewport ( & n . viewport , std : : abs ( m_nfPairs [ priority ] . zNear * 0.96f ) , std : : abs ( m_nfPairs [ priority ] . zFar * 1.05f ) ) ; // make planes 5% bigger
2018-09-13 12:50:34 +00:00
glViewport ( n . viewport . x , n . viewport . y , n . viewport . width , n . viewport . height ) ;
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-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
2023-10-14 19:05:00 +00:00
if ( ! mesh . Render ( layer , m . alpha ) ) continue ;
2017-02-20 17:22:32 +00:00
if ( mesh . highPriority ! = renderOverlay ) continue ;
2016-03-22 23:39:59 +00:00
if ( ! matrixLoaded ) {
2018-09-16 08:43:13 +00:00
m_r3dShader . SetModelStates ( & m ) ;
2016-03-22 23:39:59 +00:00
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
m_r3dShader . SetMeshUniforms ( & mesh ) ;
2018-09-13 12:50:34 +00:00
glDrawArrays ( m_primType , mesh . vboOffset , mesh . vertexCount ) ;
2016-03-21 04:10:14 +00:00
}
}
}
2017-02-20 17:22:32 +00:00
return hasOverlay ;
2016-03-21 04:10:14 +00:00
}
2018-06-16 21:31:29 +00:00
bool CNew3D : : SkipLayer ( int layer )
{
for ( const auto & n : m_nodes ) {
if ( n . viewport . priority = = layer ) {
2022-08-19 19:34:22 +00:00
if ( ! n . models . empty ( ) ) {
2018-06-16 21:31:29 +00:00
return false ;
}
}
}
return true ;
}
void CNew3D : : SetRenderStates ( )
{
m_vbo . Bind ( true ) ;
2022-11-07 21:33:01 +00:00
glBindVertexArray ( m_vao ) ;
2018-06-16 21:31:29 +00:00
2022-11-07 21:33:01 +00:00
m_r3dShader . SetShader ( true ) ;
2018-06-16 21:31:29 +00:00
2018-06-17 11:30:01 +00:00
glDepthFunc ( GL_LEQUAL ) ;
2018-06-16 21:31:29 +00:00
glEnable ( GL_DEPTH_TEST ) ;
glDepthMask ( GL_TRUE ) ;
glActiveTexture ( GL_TEXTURE0 ) ;
2019-01-21 14:30:42 +00:00
glDisable ( GL_CULL_FACE ) ; // we'll emulate this in the shader
2018-06-16 21:31:29 +00:00
2023-11-03 13:24:59 +00:00
glEnable ( GL_STENCIL_TEST ) ;
2023-11-13 21:17:06 +00:00
glStencilMask ( 0xFF ) ;
2023-10-14 19:05:00 +00:00
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
glDisable ( GL_BLEND ) ;
2018-06-16 21:31:29 +00:00
}
void CNew3D : : DisableRenderStates ( )
{
m_vbo . Bind ( false ) ;
2022-11-07 21:33:01 +00:00
glBindVertexArray ( 0 ) ;
2018-06-16 21:31:29 +00:00
m_r3dShader . SetShader ( false ) ;
glDisable ( GL_STENCIL_TEST ) ;
}
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 ( ) ;
}
2022-01-02 12:48:09 +00:00
{
std : : lock_guard < std : : mutex > guard ( m_losMutex ) ;
std : : swap ( m_losBack , m_losFront ) ;
for ( int i = 0 ; i < 4 ; i + + ) {
m_losBack - > value [ i ] = 0 ;
}
2018-10-19 20:59:46 +00:00
}
2016-03-21 04:10:14 +00:00
// release any resources from last frame
2022-08-19 19:34:22 +00:00
m_polyBufferRam . clear ( ) ; // clear dynamic model memory buffer
2017-02-07 14:05:03 +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
2016-03-21 04:10:14 +00:00
m_nodeAttribs . Reset ( ) ;
2017-08-29 10:27:29 +00:00
RenderViewport ( 0x800000 ) ; // build model structure
2016-04-19 22:05:12 +00:00
2016-03-26 22:44:26 +00:00
m_vbo . Bind ( true ) ;
2018-09-13 12:50:34 +00:00
m_vbo . BufferSubData ( MAX_ROM_VERTS * sizeof ( FVertex ) , m_polyBufferRam . size ( ) * sizeof ( FVertex ) , m_polyBufferRam . data ( ) ) ; // upload all the dynamic data to GPU in one go
2016-03-26 22:44:26 +00:00
2022-08-19 19:34:22 +00:00
if ( ! m_polyBufferRom . empty ( ) ) {
2016-03-21 04:10:14 +00:00
2016-03-26 22:44:26 +00:00
// sync rom memory with vbo
2018-09-13 12:50:34 +00:00
int romBytes = ( int ) m_polyBufferRom . size ( ) * sizeof ( FVertex ) ;
2016-03-26 22:44:26 +00:00
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
2018-09-13 12:50:34 +00:00
if ( m_polyBufferRom . size ( ) > = MAX_ROM_VERTS ) {
2016-03-26 22:44:26 +00:00
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 {
2018-09-13 12:50:34 +00:00
m_vbo . AppendData ( size , & m_polyBufferRom [ vboBytes / sizeof ( FVertex ) ] ) ;
2016-03-26 22:44:26 +00:00
}
}
}
2016-03-21 04:10:14 +00:00
2023-10-14 19:05:00 +00:00
m_r3dFrameBuffers . SetFBO ( Layer : : colour ) ; // colour will draw to all 3 buffers. For regular opaque pixels the transparent layers will be essentially masked
glClear ( GL_COLOR_BUFFER_BIT ) ;
2023-10-22 20:07:35 +00:00
DrawAmbientFog ( ) ;
2023-10-14 19:05:00 +00:00
DrawScrollFog ( ) ; // fog layer if applicable must be drawn here
2019-01-21 14:30:42 +00:00
2016-03-21 04:10:14 +00:00
for ( int pri = 0 ; pri < = 3 ; pri + + ) {
2017-02-20 17:22:32 +00:00
2018-06-16 21:31:29 +00:00
if ( SkipLayer ( pri ) ) continue ;
2016-03-21 04:10:14 +00:00
2018-06-16 21:31:29 +00:00
for ( int i = 0 ; i < 2 ; i + + ) {
2016-03-21 04:10:14 +00:00
2018-06-16 21:31:29 +00:00
bool renderOverlay = ( i = = 1 ) ;
2017-08-29 10:27:29 +00:00
2023-10-14 19:05:00 +00:00
SetRenderStates ( ) ;
2019-01-21 14:30:42 +00:00
m_r3dFrameBuffers . SetFBO ( Layer : : colour ) ;
2022-08-19 19:34:22 +00:00
2023-10-14 19:05:00 +00:00
glClear ( GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ) ;
2018-06-16 21:31:29 +00:00
2023-10-14 19:05:00 +00:00
m_r3dShader . DiscardAlpha ( true ) ;
m_r3dShader . SetLayer ( Layer : : colour ) ;
2022-08-19 19:34:22 +00:00
bool hasOverlay = RenderScene ( pri , renderOverlay , Layer : : colour ) ;
2018-06-16 21:31:29 +00:00
2022-10-07 07:01:45 +00:00
if ( ! renderOverlay ) {
ProcessLos ( pri ) ;
2018-10-19 20:59:46 +00:00
}
2019-01-21 14:30:42 +00:00
2023-10-14 19:05:00 +00:00
glDepthFunc ( GL_LESS ) ;
2019-01-21 14:30:42 +00:00
2023-10-14 19:05:00 +00:00
m_r3dShader . DiscardAlpha ( false ) ;
2022-10-07 07:01:45 +00:00
2023-11-17 16:07:20 +00:00
m_r3dFrameBuffers . StoreDepth ( ) ;
2023-10-14 19:05:00 +00:00
m_r3dShader . SetLayer ( Layer : : trans1 ) ;
m_r3dFrameBuffers . SetFBO ( Layer : : trans1 ) ;
RenderScene ( pri , renderOverlay , Layer : : trans1 ) ;
2018-06-17 11:30:01 +00:00
2023-11-17 16:07:20 +00:00
m_r3dFrameBuffers . RestoreDepth ( ) ;
2023-10-14 19:05:00 +00:00
m_r3dShader . SetLayer ( Layer : : trans2 ) ;
m_r3dFrameBuffers . SetFBO ( Layer : : trans2 ) ;
RenderScene ( pri , renderOverlay , Layer : : trans2 ) ;
2023-11-13 21:17:06 +00:00
2018-06-16 21:31:29 +00:00
DisableRenderStates ( ) ;
if ( ! hasOverlay ) break ; // no high priority polys
}
}
2023-10-14 19:05:00 +00:00
m_r3dFrameBuffers . SetFBO ( Layer : : none ) ;
m_r3dFrameBuffers . Draw ( ) ;
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
2018-09-13 12:50:34 +00:00
m_nodes . back ( ) . models . emplace_back ( ) ;
2016-03-21 04:10:14 +00:00
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-26 22:44:26 +00:00
// update texture offsets
2023-10-14 19:05:00 +00:00
m - > textureOffsetX = m_nodeAttribs . currentTexOffsetX ;
m - > textureOffsetY = m_nodeAttribs . currentTexOffsetY ;
m - > page = m_nodeAttribs . currentPage ;
m - > scale = m_nodeAttribs . currentModelScale ;
m - > alpha = m_nodeAttribs . currentModelAlpha ;
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 ;
}
2023-09-27 15:33:53 +00:00
/*
0x00 : x - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Is UF ref
- x - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Is 3 D model
- - x - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Is point
- - - x - - - - - - - - - - - - - - - - - - - - - - - - - - - - Is point ref
- - - - x - - - - - - - - - - - - - - - - - - - - - - - - - - - Is animation
- - - - - x - - - - - - - - - - - - - - - - - - - - - - - - - - Is billboard
- - - - - - x - - - - - - - - - - - - - - - - - - - - - - - - - Child is billboard
- - - - - - - x - - - - - - - - - - - - - - - - - - - - - - - - Extra child pointer needed
2023-11-06 10:56:39 +00:00
- - - - - - - - xxxxx - - - - - - - - - - - - - - - - - - - Spare ( unknown if used )
2023-09-27 15:33:53 +00:00
- - - - - - - - - - - - - xxx xxxxxx - - - - - - - - - - Node ID
2023-11-05 17:39:23 +00:00
- - - - - - - - - - - - - - - - - - - - - - x - - - - - - - - - Discard 1
- - - - - - - - - - - - - - - - - - - - - - - x - - - - - - - - Discard 2
2023-09-27 15:33:53 +00:00
- - - - - - - - - - - - - - - - - - - - - - - - x - - - - - - - Reset matrix
- - - - - - - - - - - - - - - - - - - - - - - - - x - - - - - - Use child pointer
- - - - - - - - - - - - - - - - - - - - - - - - - - x - - - - - Use sibling pointer
- - - - - - - - - - - - - - - - - - - - - - - - - - - x - - - - No matrix
- - - - - - - - - - - - - - - - - - - - - - - - - - - - x - - - Indirect child
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - x - - Valid color table
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - xx Node type ( 0 = viewport , 1 = root node , 2 = culling node )
2023-11-02 00:25:52 +00:00
0x01 , 0x02 only present on Step 1.5 +
2023-09-27 15:33:53 +00:00
2023-11-01 16:01:13 +00:00
0x01 : xxxxxxxx xxxxxxxx xxxxxxxx xxxxxx - - Model scale ( float32 ) last 2 bits are control words
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - x - Disable culling
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - x Valid model scale
2023-11-02 00:25:52 +00:00
2023-09-27 15:33:53 +00:00
0x02 : - - - - - - - - - - - - - - - - x - - - - - - - - - - - - - - - Texture replace
- - - - - - - - - - - - - - - - - x - - - - - - - - - - - - - - Switch bank
- - - - - - - - - - - - - - - - - - xxxxxx x - - - - - - - X offset
- - - - - - - - - - - - - - - - - - - - - - - - - xxxxxxx Y offset
0x03 : xxxxxxxx xxxxx - - - - - - - - - - - - - - - - - - - Color table address 1
- - - - - - - - - - - - - xxx xxxx - - - - - - - - - - - - LOD table pointer
- - - - - - - - - - - - - - - - - - - - xxxx xxxxxxxx Node matrix
2023-11-01 16:01:13 +00:00
0x04 : Translation X coordinate
0x05 : Translation Y coordinate
0x06 : Translation Z coordinate
0x07 : xxxx - - - - - - - - - - - - - - - - - - - - - - - - - - - - Color table address 2
- - - - - x - - - - - - - - - - - - - - - - - - - - - - - - - - Sibling table
- - - - - - x - - - - - - - - - - - - - - - - - - - - - - - - - Point
- - - - - - - x - - - - - - - - - - - - - - - - - - - - - - - - Leaf node
- - - - - - - - xxxxxxxx xxxxxxxx xxxxxxxx Child pointer
0x08 : xxxxxxx - - - - - - - - - - - - - - - - - - - - - - - - - Color table address 3
- - - - - - - x - - - - - - - - - - - - - - - - - - - - - - - - Null sibling
- - - - - - - - xxxxxxxx xxxxxxxx xxxxxxxx Sibling pointer
0x09 : xxxxxxxx xxxxxxxx - - - - - - - - - - - - - - - - Blend radius
- - - - - - - - - - - - - - - - xxxxxxxx xxxxxxxx Culling radius
2023-09-27 15:33:53 +00:00
*/
2016-03-21 04:10:14 +00:00
void CNew3D : : DescendCullingNode ( UINT32 addr )
{
2019-07-28 12:14:13 +00:00
enum class NodeType { undefined = - 1 , viewport = 0 , rootNode = 1 , cullingNode = 2 } ;
2023-11-02 00:25:52 +00:00
const UINT32 * node , * lodPtr ;
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 ;
2023-09-27 15:33:53 +00:00
float fBlendRadius ;
UINT8 lodTablePointer ;
2019-07-28 12:14:13 +00:00
NodeType nodeType ;
2023-09-27 15:33:53 +00:00
bool resetMatrix ;
2016-03-21 04:10:14 +00:00
if ( m_nodeAttribs . StackLimit ( ) ) {
return ;
}
node = TranslateCullingAddress ( addr ) ;
if ( NULL = = node ) {
return ;
}
// Extract known fields
2019-07-28 12:14:13 +00:00
nodeType = ( NodeType ) ( node [ 0x00 ] & 3 ) ;
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 ;
2023-09-27 15:33:53 +00:00
resetMatrix = ( node [ 0x0 ] & 0x80 ) > 0 ;
lodTablePointer = ( node [ 0x03 - m_offset ] > > 12 ) & 0x7F ;
2016-03-21 04:10:14 +00:00
2019-07-28 12:14:13 +00:00
// check our node type
if ( nodeType = = NodeType : : viewport ) {
return ; // viewport nodes aren't rendered
}
2023-11-05 17:39:23 +00:00
// node discard
if ( ( 0x300 & node [ 0 ] ) = = 0x300 ) { // why 2 bits for node discard? Sega rally uses this
return ;
}
2018-06-16 21:31:29 +00:00
// parse siblings
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-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+
2022-10-03 02:30:59 +00:00
2023-11-02 00:25:52 +00:00
if ( node [ 0x01 ] & 1 )
m_nodeAttribs . currentModelScale = Util : : Uint32AsFloat ( node [ 0x01 ] & ~ 3 ) ; // mask out control bits
if ( node [ 0x01 ] & 2 )
m_nodeAttribs . currentDisableCulling = true ;
2017-07-29 16:30:30 +00:00
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 ) {
2022-10-03 02:30:59 +00:00
float x = Util : : Uint32AsFloat ( node [ 0x04 - m_offset ] ) ;
float y = Util : : Uint32AsFloat ( node [ 0x05 - m_offset ] ) ;
float z = Util : : Uint32AsFloat ( 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
2023-09-27 15:33:53 +00:00
if ( resetMatrix ) {
ResetMatrix ( m_modelMat ) ;
}
2017-04-17 10:40:07 +00:00
uCullRadius = node [ 9 - m_offset ] & 0xFFFF ;
fCullRadius = R3DFloat : : GetFloat16 ( uCullRadius ) ;
uBlendRadius = node [ 9 - m_offset ] > > 16 ;
2023-09-27 15:33:53 +00:00
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
}
2023-11-16 23:03:21 +00:00
float LODscale ;
2023-11-02 00:25:52 +00:00
if ( m_nodeAttribs . currentDisableCulling )
2023-11-16 23:03:21 +00:00
LODscale = FLT_MAX ;
2023-11-02 00:25:52 +00:00
else
{
2023-11-16 23:03:21 +00:00
float distance = std : : hypot ( m_modelMat . currentMatrix [ 12 ] , m_modelMat . currentMatrix [ 13 ] , m_modelMat . currentMatrix [ 14 ] ) ;
LODscale = fBlendRadius * m_nodeAttribs . currentModelScale / distance ;
2023-11-02 00:25:52 +00:00
}
2023-11-16 23:03:21 +00:00
const LODFeatureType & lodTableEntry = m_LODBlendTable - > table [ lodTablePointer ] ;
if ( m_nodeAttribs . currentClipStatus ! = Clip : : OUTSIDE & & LODscale > = lodTableEntry . lod [ 3 ] . deleteSize ) {
2016-06-16 20:05:29 +00:00
// Descend down first link
if ( ( node [ 0x00 ] & 0x08 ) ) // 4-element LOD table
{
2023-11-02 00:25:52 +00:00
lodPtr = TranslateCullingAddress ( child1Ptr ) ;
2023-11-16 23:03:21 +00:00
if ( NULL ! = lodPtr )
2023-11-02 00:25:52 +00:00
{
2023-11-16 23:03:21 +00:00
int modelLOD ;
for ( modelLOD = 0 ; modelLOD < 3 ; modelLOD + + )
{
if ( LODscale > = lodTableEntry . lod [ modelLOD ] . deleteSize & & lodPtr [ modelLOD ] & 0x1000000 )
break ;
}
float tempAlpha = m_nodeAttribs . currentModelAlpha ;
2016-06-16 20:05:29 +00:00
2023-11-16 23:03:21 +00:00
float nodeAlpha = lodTableEntry . lod [ modelLOD ] . blendFactor * ( LODscale - lodTableEntry . lod [ modelLOD ] . deleteSize ) ;
nodeAlpha = std : : clamp ( nodeAlpha , 0.0f , 1.0f ) ;
if ( nodeAlpha > 15.0f / 16.0f ) // shader discards pixels below 1/16 alpha
nodeAlpha = 1.0f ;
else if ( nodeAlpha < 1.0f / 16.0f )
nodeAlpha = 0.0f ;
m_nodeAttribs . currentModelAlpha * = nodeAlpha ; // alpha of each node multiples by the alpha of its parent
2016-06-16 20:05:29 +00:00
if ( ( node [ 0x03 - m_offset ] & 0x20000000 ) ) {
2023-11-02 00:25:52 +00:00
DescendCullingNode ( lodPtr [ modelLOD ] & 0xFFFFFF ) ;
2023-11-16 23:03:21 +00:00
if ( nodeAlpha < 1.0f & & modelLOD ! = 3 )
{
m_nodeAttribs . currentModelAlpha = ( 1.0f - nodeAlpha ) * tempAlpha ;
DescendCullingNode ( lodPtr [ modelLOD + 1 ] & 0xFFFFFF ) ;
}
2016-06-16 20:05:29 +00:00
}
else {
2023-11-02 00:25:52 +00:00
DrawModel ( lodPtr [ modelLOD ] & 0xFFFFFF ) ;
2023-11-16 23:03:21 +00:00
if ( nodeAlpha < 1.0f & & modelLOD ! = 3 )
{
m_nodeAttribs . currentModelAlpha = ( 1.0f - nodeAlpha ) * tempAlpha ;
DrawModel ( lodPtr [ modelLOD + 1 ] & 0xFFFFFF ) ;
}
2016-06-16 20:05:29 +00:00
}
2016-03-21 04:10:14 +00:00
}
}
2016-06-16 20:05:29 +00:00
else {
2023-11-16 23:03:21 +00:00
float nodeAlpha = lodTableEntry . lod [ 3 ] . blendFactor * ( LODscale - lodTableEntry . lod [ 3 ] . deleteSize ) ;
nodeAlpha = std : : clamp ( nodeAlpha , 0.0f , 1.0f ) ;
m_nodeAttribs . currentModelAlpha * = nodeAlpha ; // alpha of each node multiples by the alpha of its parent
2016-06-16 20:05:29 +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 ;
}
2019-07-26 16:39:58 +00:00
switch ( ( nodeAddr > > 24 ) & 0x5 ) // pointer type encoded in upper 8 bits
2016-03-21 04:10:14 +00:00
{
2019-07-26 16:39:58 +00:00
case 0x00 :
2016-03-21 04:10:14 +00:00
DescendCullingNode ( nodeAddr & 0xFFFFFF ) ;
break ;
2019-07-26 16:39:58 +00:00
case 0x01 :
2016-03-21 04:10:14 +00:00
DrawModel ( nodeAddr & 0xFFFFFF ) ;
break ;
2019-07-26 16:39:58 +00:00
case 0x04 :
2016-03-21 04:10:14 +00:00
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 ) ;
}
2023-09-27 15:33:53 +00:00
// what this does is to set the rotation back to zero, whilst keeping the position and scale of the current matrix
void CNew3D : : ResetMatrix ( Mat4 & mat )
{
float m [ 16 ] ;
memcpy ( m , mat . currentMatrix , 16 * 4 ) ;
// transpose the top 3x3 of the matrix (this effectively inverts the rotation). When we multiply our new matrix it'll effectively cancel out the rotations.
std : : swap ( m [ 1 ] , m [ 4 ] ) ;
std : : swap ( m [ 2 ] , m [ 8 ] ) ;
std : : swap ( m [ 6 ] , m [ 9 ] ) ;
// set position to zero
m [ 12 ] = 0 ;
m [ 13 ] = 0 ;
m [ 14 ] = 0 ;
m [ 15 ] = 1 ;
// normalise columns, this removes the scaling, otherwise we'll apply it twice
float s1 = std : : sqrt ( ( m [ 0 ] * m [ 0 ] ) + ( m [ 1 ] * m [ 1 ] ) + ( m [ 2 ] * m [ 2 ] ) ) ;
float s2 = std : : sqrt ( ( m [ 4 ] * m [ 4 ] ) + ( m [ 5 ] * m [ 5 ] ) + ( m [ 6 ] * m [ 6 ] ) ) ;
float s3 = std : : sqrt ( ( m [ 8 ] * m [ 8 ] ) + ( m [ 9 ] * m [ 9 ] ) + ( m [ 10 ] * m [ 10 ] ) ) ;
m [ 0 ] / = s1 ; m [ 4 ] / = s2 ; m [ 8 ] / = s3 ;
m [ 1 ] / = s1 ; m [ 5 ] / = s2 ; m [ 9 ] / = s3 ;
m [ 2 ] / = s1 ; m [ 6 ] / = s2 ; m [ 10 ] / = s3 ;
mat . MultMatrix ( m ) ;
}
2016-03-21 04:10:14 +00:00
// 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
2022-08-19 19:34:22 +00:00
{ 0.0f , 0.0f , 0.0f } , // off
{ 0.0f , 0.0f , 1.0f } , // blue
{ 0.0f , 1.0f , 0.0f } , // green
{ 0.0f , 1.0f , 1.0f } , // cyan
{ 1.0f , 0.0f , 0.0f } , // red
{ 1.0f , 0.0f , 1.0f } , // purple
{ 1.0f , 1.0f , 0.0f } , // yellow
{ 1.0f , 1.0f , 1.0f } // white
2016-03-21 04:10:14 +00:00
} ;
2020-09-07 16:25:18 +00:00
if ( ( addr & 0x00FFFFFF ) = = 0 ) {
return ;
}
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 [ 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?)
2022-08-19 19:34:22 +00:00
vp - > vpX = ( int ) ( ( ( vpnode [ 0x1A ] & 0xFFFF ) * ( float ) ( 1.0 / 16.0 ) ) + 0.5f ) ; // viewport X (12.4 fixed point)
vp - > vpY = ( int ) ( ( ( vpnode [ 0x1A ] > > 16 ) * ( float ) ( 1.0 / 16.0 ) ) + 0.5f ) ; // viewport Y (12.4)
vp - > vpWidth = ( int ) ( ( ( vpnode [ 0x14 ] & 0xFFFF ) * ( float ) ( 1.0 / 4.0 ) ) + 0.5f ) ; // width (14.2)
vp - > vpHeight = ( int ) ( ( ( vpnode [ 0x14 ] > > 16 ) * ( float ) ( 1.0 / 4.0 ) ) + 0.5f ) ; // height (14.2)
2017-02-07 14:05:03 +00:00
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
2018-01-10 16:12:33 +00:00
/*
2022-10-03 02:30:59 +00:00
vp - > angle_left = - atan2f ( Util : : Uint32AsFloat ( vpnode [ 12 ] ) , Util : : Uint32AsFloat ( vpnode [ 13 ] ) ) ; // These values work out as the normals for the clipping planes.
vp - > angle_right = atan2f ( Util : : Uint32AsFloat ( vpnode [ 16 ] ) , - Util : : Uint32AsFloat ( vpnode [ 17 ] ) ) ; // Sometimes these values (dirt devils,lost world) are totally wrong
vp - > angle_top = atan2f ( Util : : Uint32AsFloat ( vpnode [ 14 ] ) , Util : : Uint32AsFloat ( vpnode [ 15 ] ) ) ; // and don't work for the frustum values exactly.
vp - > angle_bottom = - atan2f ( Util : : Uint32AsFloat ( vpnode [ 18 ] ) , - Util : : Uint32AsFloat ( vpnode [ 19 ] ) ) ; // Perhaps they are just used for culling and not rendering.
2018-01-10 16:12:33 +00:00
*/
2018-01-08 18:35:42 +00:00
2022-10-03 02:30:59 +00:00
float cv = Util : : Uint32AsFloat ( vpnode [ 0x8 ] ) ; // 1/(left-right)
float cw = Util : : Uint32AsFloat ( vpnode [ 0x9 ] ) ; // 1/(top-bottom)
float io = Util : : Uint32AsFloat ( vpnode [ 0xa ] ) ; // top / bottom (ratio) - ish
float jo = Util : : Uint32AsFloat ( vpnode [ 0xb ] ) ; // left / right (ratio)
2018-01-08 18:35:42 +00:00
2022-08-19 19:34:22 +00:00
vp - > angle_left = ( 0.0f - jo ) / cv ;
vp - > angle_right = ( 1.0f - jo ) / cv ;
vp - > angle_bottom = - ( 1.0f - io ) / cw ;
vp - > angle_top = - ( 0.0f - io ) / cw ;
2017-03-07 01:29:07 +00:00
2017-02-07 14:05:03 +00:00
// calculate the frustum shape, near/far pair are dummy values
2022-08-19 19:34:22 +00:00
CalcViewport ( vp , 1.f , 1000.f ) ;
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)
2022-10-03 02:30:59 +00:00
vp - > lightingParams [ 0 ] = Util : : Uint32AsFloat ( vpnode [ 0x05 ] ) ; // sun X
vp - > lightingParams [ 1 ] = - Util : : Uint32AsFloat ( vpnode [ 0x06 ] ) ; // sun Y (- to convert to ogl cordinate system)
vp - > lightingParams [ 2 ] = - Util : : Uint32AsFloat ( vpnode [ 0x04 ] ) ; // sun Z (- to convert to ogl cordinate system)
vp - > lightingParams [ 3 ] = std : : max ( 0.f , std : : min ( Util : : Uint32AsFloat ( vpnode [ 0x07 ] ) , 1.0f ) ) ; // sun intensity (clamp to 0-1)
2022-08-19 19:34:22 +00:00
vp - > lightingParams [ 4 ] = ( float ) ( ( vpnode [ 0x24 ] > > 8 ) & 0xFF ) * ( float ) ( 1.0 / 255.0 ) ; // ambient intensity
vp - > lightingParams [ 5 ] = 0.0f ; // reserved
2017-08-15 23:21:57 +00:00
2017-09-02 14:54:16 +00:00
vp - > sunClamp = m_sunClamp ;
2017-08-14 09:14:06 +00:00
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
2022-10-01 01:44:22 +00:00
vp - > spotEllipse [ 0 ] = ( float ) ( INT16 ) ( vpnode [ 0x1E ] & 0xFFFF ) * ( float ) ( 1.0 / 8.0 ) ; // spotlight X position (13.3 fixed point)
vp - > spotEllipse [ 1 ] = ( float ) ( INT16 ) ( vpnode [ 0x1D ] & 0xFFFF ) * ( float ) ( 1.0 / 8.0 ) ; // spotlight Y
2017-04-02 21:03:59 +00:00
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
2022-10-03 02:30:59 +00:00
vp - > spotRange [ 0 ] = 1.0f / Util : : Uint32AsFloat ( vpnode [ 0x21 ] ) ; // spotlight start
vp - > spotRange [ 1 ] = Util : : Uint32AsFloat ( vpnode [ 0x1F ] ) ; // spotlight extent
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
2022-08-19 19:34:22 +00:00
vp - > spotEllipse [ 0 ] = vp - > spotEllipse [ 0 ] * m_xRatio + ( float ) m_xOffs ;
vp - > spotEllipse [ 1 ] = vp - > spotEllipse [ 1 ] * m_yRatio + ( float ) m_yOffs ;
2016-05-05 22:01:59 +00:00
vp - > spotEllipse [ 2 ] * = m_xRatio ;
vp - > spotEllipse [ 3 ] * = m_yRatio ;
2018-01-15 21:27:21 +00:00
// Line of sight position
vp - > losPosX = ( int ) ( ( ( vpnode [ 0x1c ] & 0xFFFF ) / 16.0f ) + 0.5f ) ; // x position
vp - > losPosY = ( int ) ( ( ( vpnode [ 0x1c ] > > 16 ) / 16.0f ) + 0.5f ) ; // y position 0 starts from the top
2016-05-05 22:01:59 +00:00
// Fog
2023-10-17 16:33:26 +00:00
vp - > fogParams [ 0 ] = ( float ) ( ( vpnode [ 0x22 ] > > 16 ) & 0xFF ) * ( float ) ( 1.0 / 255.0 ) ; // fog color R
vp - > fogParams [ 1 ] = ( float ) ( ( vpnode [ 0x22 ] > > 8 ) & 0xFF ) * ( float ) ( 1.0 / 255.0 ) ; // fog color G
vp - > fogParams [ 2 ] = ( float ) ( ( vpnode [ 0x22 ] > > 0 ) & 0xFF ) * ( float ) ( 1.0 / 255.0 ) ; // fog color B
2022-10-03 02:30:59 +00:00
vp - > fogParams [ 3 ] = std : : abs ( Util : : Uint32AsFloat ( vpnode [ 0x23 ] ) ) ; // fog density - ocean hunter uses negative values, but looks the same
2022-08-19 19:34:22 +00:00
vp - > fogParams [ 4 ] = ( float ) ( INT16 ) ( vpnode [ 0x25 ] & 0xFFFF ) * ( float ) ( 1.0 / 255.0 ) ; // fog start
2016-05-05 22:01:59 +00:00
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
2022-08-19 19:34:22 +00:00
vp - > fogParams [ 5 ] = ( float ) ( ( vpnode [ 0x24 ] > > 16 ) & 0xFF ) * ( float ) ( 1.0 / 255.0 ) ; // fog attenuation
vp - > fogParams [ 6 ] = ( float ) ( ( vpnode [ 0x25 ] > > 16 ) & 0xFF ) * ( float ) ( 1.0 / 255.0 ) ; // fog ambient
2016-06-27 17:35:27 +00:00
2022-08-19 19:34:22 +00:00
vp - > scrollFog = ( float ) ( vpnode [ 0x20 ] & 0xFF ) * ( float ) ( 1.0 / 255.0 ) ; // scroll fog
vp - > scrollAtt = ( float ) ( vpnode [ 0x24 ] & 0xFF ) * ( float ) ( 1.0 / 255.0 ) ; // 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
2019-07-26 16:39:58 +00:00
// Descend down the node link. Need to start with a culling node because that defines our culling radius.
auto childptr = vpnode [ 0x02 ] ;
if ( ( ( childptr > > 24 ) & 0x5 ) = = 0 ) {
DescendNodePtr ( vpnode [ 0x02 ] ) ;
2016-05-05 22:01:59 +00:00
}
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
}
2018-09-13 12:50:34 +00:00
void CNew3D : : CopyVertexData ( const R3DPoly & r3dPoly , std : : vector < FVertex > & vertexArray )
2016-03-21 04:10:14 +00:00
{
2018-09-19 20:43:41 +00:00
// both lemans 24 and dirt devils are rendering some totally transparent polys as the first object in each viewport
2022-08-19 19:34:22 +00:00
// in dirt devils it's parallel to the camera so is completely invisible, but breaks our depth calculation
2018-09-19 20:43:41 +00:00
// in lemans 24 its a sort of diamond shape, but never leaves a hole in the transparent geometry so must be being skipped by the h/w
if ( r3dPoly . faceColour [ 3 ] = = 0 ) {
return ;
}
2018-09-13 12:50:34 +00:00
if ( m_numPolyVerts = = 4 ) {
if ( r3dPoly . number = = 4 ) {
vertexArray . emplace_back ( r3dPoly , 0 ) ; // construct directly inside container without copy
vertexArray . emplace_back ( r3dPoly , 1 ) ;
vertexArray . emplace_back ( r3dPoly , 2 ) ;
vertexArray . emplace_back ( r3dPoly , 3 ) ;
// check for identical points (ie forced triangle) and replace with average point
// if we don't do this our quad code falls apart
FVertex * v = ( & vertexArray . back ( ) ) - 3 ;
for ( int i = 0 ; i < 4 ; i + + ) {
2016-03-21 04:10:14 +00:00
2018-09-13 12:50:34 +00:00
int next1 = ( i + 1 ) % 4 ;
int next2 = ( i + 2 ) % 4 ;
if ( FVertex : : Equal ( v [ i ] , v [ next1 ] ) ) {
2020-09-14 20:01:52 +00:00
FVertex : : Average ( v [ next1 ] , v [ next2 ] , v [ next1 ] ) ;
break ;
2018-09-13 12:50:34 +00:00
}
}
}
else {
vertexArray . emplace_back ( r3dPoly , 0 ) ;
vertexArray . emplace_back ( r3dPoly , 1 ) ;
vertexArray . emplace_back ( r3dPoly , 2 ) ;
vertexArray . emplace_back ( r3dPoly , 0 , 2 ) ; // last point is an average of 0 and 2
}
}
else {
vertexArray . emplace_back ( r3dPoly , 0 ) ;
vertexArray . emplace_back ( r3dPoly , 1 ) ;
vertexArray . emplace_back ( r3dPoly , 2 ) ;
if ( r3dPoly . number = = 4 ) {
vertexArray . emplace_back ( r3dPoly , 0 ) ;
vertexArray . emplace_back ( r3dPoly , 2 ) ;
vertexArray . emplace_back ( r3dPoly , 3 ) ;
}
2016-03-21 04:10:14 +00:00
}
}
2022-11-07 21:33:01 +00:00
void CNew3D : : GetCoordinates ( int width , int height , UINT16 uIn , UINT16 vIn , float uvScale , float & uOut , float & vOut )
{
uOut = ( uIn * uvScale ) / width ;
vOut = ( vIn * uvScale ) / height ;
}
int CNew3D : : GetTexFormat ( int originalFormat , bool contour )
{
if ( ! contour ) {
return originalFormat ; // the same
}
switch ( originalFormat )
{
case 1 :
case 2 :
case 3 :
case 4 :
return originalFormat + 7 ; // these formats are identical to 1-4, except they lose the 4 bit alpha part when contour is enabled
default :
return originalFormat ;
}
}
2016-12-06 12:25:34 +00:00
void CNew3D : : SetMeshValues ( SortingMesh * currentMesh , PolyHeader & ph )
{
//copy attributes
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 ( ) ;
2017-11-07 00:15:57 +00:00
currentMesh - > fixedShading = ph . FixedShading ( ) & & ! ph . SmoothShading ( ) ;
2017-02-20 17:22:32 +00:00
currentMesh - > highPriority = ph . HighPriority ( ) ;
2018-05-28 12:55:13 +00:00
currentMesh - > transLSelect = ph . TranslucencyPatternSelect ( ) ;
2018-06-16 21:31:29 +00:00
currentMesh - > layered = ph . Layered ( ) ;
currentMesh - > specular = ph . SpecularEnabled ( ) ;
currentMesh - > shininess = ph . Shininess ( ) ;
currentMesh - > specularValue = ph . SpecularValue ( ) ;
currentMesh - > fogIntensity = ph . LightModifier ( ) ;
2020-05-11 09:05:46 +00:00
currentMesh - > translatorMap = ph . TranslatorMap ( ) ;
2023-11-03 13:24:59 +00:00
currentMesh - > noLosReturn = ph . NoLosReturn ( ) ;
2016-12-06 12:25:34 +00:00
2016-12-09 14:13:46 +00:00
if ( currentMesh - > textured ) {
2022-11-07 21:33:01 +00:00
currentMesh - > format = GetTexFormat ( ph . TexFormat ( ) , ph . AlphaTest ( ) ) ;
2016-12-06 12:25:34 +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
}
currentMesh - > x = ph . X ( ) ;
currentMesh - > y = ph . Y ( ) ;
currentMesh - > width = ph . TexWidth ( ) ;
currentMesh - > height = ph . TexHeight ( ) ;
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
2018-10-13 13:29:45 +00:00
{
bool smoothU = ph . TexSmoothU ( ) ;
2018-10-14 10:05:24 +00:00
bool smoothV = ph . TexSmoothV ( ) ;
2018-10-13 13:29:45 +00:00
2018-10-19 20:59:46 +00:00
if ( ph . AlphaTest ( ) ) {
smoothU = false ; // smooth wrap makes no sense for alpha tested polys with pixel dilate
smoothV = false ;
}
2018-10-13 13:29:45 +00:00
if ( ph . TexUMirror ( ) ) {
if ( smoothU ) currentMesh - > wrapModeU = Mesh : : TexWrapMode : : mirror ;
else currentMesh - > wrapModeU = Mesh : : TexWrapMode : : mirrorClamp ;
}
else {
if ( smoothU ) currentMesh - > wrapModeU = Mesh : : TexWrapMode : : repeat ;
else currentMesh - > wrapModeU = Mesh : : TexWrapMode : : repeatClamp ;
}
if ( ph . TexVMirror ( ) ) {
if ( smoothV ) currentMesh - > wrapModeV = Mesh : : TexWrapMode : : mirror ;
else currentMesh - > wrapModeV = Mesh : : TexWrapMode : : mirrorClamp ;
}
else {
if ( smoothV ) currentMesh - > wrapModeV = Mesh : : TexWrapMode : : repeat ;
else currentMesh - > wrapModeV = Mesh : : TexWrapMode : : repeatClamp ;
}
}
2016-12-09 14:13:46 +00:00
if ( currentMesh - > microTexture ) {
2022-08-19 19:34:22 +00:00
static const float microTexScale [ ] = { 2.f , 4.f , 16.f , 256.f } ;
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 )
{
2022-10-01 01:44:22 +00:00
if ( data = = NULL )
return ;
2016-12-15 12:10:52 +00:00
UINT16 texCoords [ 4 ] [ 2 ] ;
2016-03-21 04:10:14 +00:00
PolyHeader ph ;
UINT64 lastHash = - 1 ;
SortingMesh * currentMesh = nullptr ;
2022-08-19 19:34:22 +00:00
std : : unordered_map < UINT64 , SortingMesh > sMap ;
2016-03-21 04:10:14 +00:00
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
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 ) {
2022-08-19 19:34:22 +00:00
currentMesh = & sMap . insert ( { hash , SortingMesh ( ) } ) . first - > second ;
2016-03-21 04:10:14 +00:00
//make space for our vertices
2018-09-13 12:50:34 +00:00
currentMesh - > verts . reserve ( numTriangles * 3 ) ;
2016-03-21 04:10:14 +00:00
2016-12-06 12:25:34 +00:00
//set mesh values
SetMeshValues ( currentMesh , ph ) ;
2016-03-21 04:10:14 +00:00
}
2022-08-19 19:34:22 +00:00
else
currentMesh = & sMap [ 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
2022-08-19 19:34:22 +00:00
int j = 0 ;
for ( int i = 0 ; i < 4 ; i + + ) // up to 4 reused vertices
2016-03-21 04:10:14 +00:00
{
if ( ph . SharedVertex ( i ) )
{
2018-09-02 21:36:24 +00:00
p . v [ j ] = m_prev [ i ] ;
2016-12-06 14:39:46 +00:00
2018-09-02 21:36:24 +00:00
texCoords [ j ] [ 0 ] = m_prevTexCoords [ i ] [ 0 ] ;
texCoords [ j ] [ 1 ] = m_prevTexCoords [ i ] [ 1 ] ;
2016-12-15 12:10:52 +00:00
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 ) {
2022-11-07 21:33:01 +00:00
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-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 ) ;
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
}
2018-06-16 21:31:29 +00:00
2016-05-05 08:06:06 +00:00
// if we have flat shading, we can't re-use normals from shared vertices
2022-08-19 19:34:22 +00:00
for ( int i = 0 ; i < p . number & & ! ph . SmoothShading ( ) ; i + + ) {
2016-05-05 08:06:06 +00:00
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 ;
2017-08-19 20:06:31 +00:00
p . v [ j ] . pos [ 3 ] = 1.0f ;
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 ( ) ) {
2017-08-19 20:06:31 +00:00
p . v [ j ] . normal [ 0 ] = BYTE_TO_FLOAT ( ( INT8 ) ( ix & 0xFF ) ) ;
p . v [ j ] . normal [ 1 ] = BYTE_TO_FLOAT ( ( INT8 ) ( iy & 0xFF ) ) ;
p . v [ j ] . normal [ 2 ] = BYTE_TO_FLOAT ( ( INT8 ) ( iz & 0xFF ) ) ;
2016-05-05 00:01:17 +00:00
}
2016-03-21 04:10:14 +00:00
2017-11-07 00:15:57 +00:00
if ( ph . FixedShading ( ) & & ! 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
//==========
2017-08-19 20:06:31 +00:00
float shade ;
2017-08-14 09:14:06 +00:00
//==========
2017-06-17 16:05:54 +00:00
2017-10-05 19:15:00 +00:00
if ( ! m_shadeIsSigned ) {
2022-08-19 19:34:22 +00:00
shade = ( ix & 0xFF ) * ( float ) ( 1.0 / 255.0 ) ;
2017-06-17 16:05:54 +00:00
}
else {
2017-08-19 20:06:31 +00:00
shade = BYTE_TO_FLOAT ( ( INT8 ) ( ix & 0xFF ) ) ;
2017-06-17 16:05:54 +00:00
}
2017-08-19 20:06:31 +00:00
p . v [ j ] . fixedShade = shade ;
2016-03-21 04:10:14 +00:00
}
2018-09-02 21:36:24 +00:00
float texU = 0 ;
float texV = 0 ;
2016-03-21 04:10:14 +00:00
// tex coords
2016-05-13 08:39:48 +00:00
if ( currentMesh - > textured ) {
2022-11-07 21:33:01 +00:00
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
}
2017-08-21 23:00:29 +00:00
// check if we need to 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 ) ;
2022-08-19 19:34:22 +00:00
for ( int i2 = 0 ; i2 < tempP . number ; i2 + + ) {
V3 : : inverse ( tempP . v [ i2 ] . normal ) ;
2016-05-13 08:39:48 +00:00
}
2018-09-13 12:50:34 +00:00
CopyVertexData ( tempP , currentMesh - > verts ) ;
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 ( ) ) {
2018-09-13 12:50:34 +00:00
CopyVertexData ( p , currentMesh - > verts ) ;
2017-04-14 23:41:11 +00:00
}
2016-05-13 08:39:48 +00:00
// Copy current vertices into previous vertex array
2022-08-19 19:34:22 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
2018-09-02 21:36:24 +00:00
m_prev [ i ] = p . v [ i ] ;
m_prevTexCoords [ i ] [ 0 ] = texCoords [ i ] [ 0 ] ;
m_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
2022-10-01 01:44:22 +00:00
// we know how many meshes we have to 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
2018-09-13 12:50:34 +00:00
it . second . vboOffset = ( int ) m_polyBufferRam . size ( ) + MAX_ROM_VERTS ;
it . second . vertexCount = ( int ) it . second . verts . size ( ) ;
2016-03-26 22:44:26 +00:00
// copy poly data to main buffer
2018-09-13 12:50:34 +00:00
m_polyBufferRam . insert ( m_polyBufferRam . end ( ) , it . second . verts . begin ( ) , it . second . verts . end ( ) ) ;
2016-03-26 22:44:26 +00:00
}
else {
// calculate VBO values for current mesh
2018-09-13 12:50:34 +00:00
it . second . vboOffset = ( int ) m_polyBufferRom . size ( ) ;
it . second . vertexCount = ( int ) it . second . verts . size ( ) ;
2016-03-26 22:44:26 +00:00
// copy poly data to main buffer
2018-09-13 12:50:34 +00:00
m_polyBufferRom . insert ( m_polyBufferRom . end ( ) , it . second . verts . begin ( ) , it . second . verts . end ( ) ) ;
2016-03-26 22:44:26 +00:00
}
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-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 ;
}
2019-11-15 20:00:25 +00:00
void CNew3D : : CalcFrustumPlanes ( Plane p [ 5 ] , 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 ( ) ;
2019-11-15 20:00:25 +00:00
// Front Plane
2022-08-19 19:34:22 +00:00
p [ 4 ] . a = 0.f ;
p [ 4 ] . b = 0.f ;
p [ 4 ] . c = - 1.f ;
p [ 4 ] . d = 0.f ;
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 ;
2022-08-19 19:34:22 +00:00
box . points [ 0 ] [ 3 ] = 1.f ;
2016-06-16 20:05:29 +00:00
//bottom left back
box . points [ 1 ] [ 0 ] = - distance ;
box . points [ 1 ] [ 1 ] = - distance ;
box . points [ 1 ] [ 2 ] = - distance ;
2022-08-19 19:34:22 +00:00
box . points [ 1 ] [ 3 ] = 1.f ;
2016-06-16 20:05:29 +00:00
//bottom right back
box . points [ 2 ] [ 0 ] = distance ;
box . points [ 2 ] [ 1 ] = - distance ;
box . points [ 2 ] [ 2 ] = - distance ;
2022-08-19 19:34:22 +00:00
box . points [ 2 ] [ 3 ] = 1.f ;
2016-06-16 20:05:29 +00:00
//bottom right front
box . points [ 3 ] [ 0 ] = distance ;
box . points [ 3 ] [ 1 ] = - distance ;
box . points [ 3 ] [ 2 ] = distance ;
2022-08-19 19:34:22 +00:00
box . points [ 3 ] [ 3 ] = 1.f ;
2016-06-16 20:05:29 +00:00
//top left front
box . points [ 4 ] [ 0 ] = - distance ;
box . points [ 4 ] [ 1 ] = distance ;
box . points [ 4 ] [ 2 ] = distance ;
2022-08-19 19:34:22 +00:00
box . points [ 4 ] [ 3 ] = 1.f ;
2016-06-16 20:05:29 +00:00
//top left back
box . points [ 5 ] [ 0 ] = - distance ;
box . points [ 5 ] [ 1 ] = distance ;
box . points [ 5 ] [ 2 ] = - distance ;
2022-08-19 19:34:22 +00:00
box . points [ 5 ] [ 3 ] = 1.f ;
2016-06-16 20:05:29 +00:00
//top right back
box . points [ 6 ] [ 0 ] = distance ;
box . points [ 6 ] [ 1 ] = distance ;
box . points [ 6 ] [ 2 ] = - distance ;
2022-08-19 19:34:22 +00:00
box . points [ 6 ] [ 3 ] = 1.f ;
2016-06-16 20:05:29 +00:00
//top right front
box . points [ 7 ] [ 0 ] = distance ;
box . points [ 7 ] [ 1 ] = distance ;
box . points [ 7 ] [ 2 ] = distance ;
2022-08-19 19:34:22 +00:00
box . points [ 7 ] [ 3 ] = 1.f ;
2016-06-16 20:05:29 +00:00
}
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 ] ;
}
}
2022-08-19 19:34:22 +00:00
Clip CNew3D : : ClipBox ( const BBox & box , Plane planes [ 5 ] )
2016-06-16 20:05:29 +00:00
{
int count = 0 ;
for ( int i = 0 ; i < 8 ; i + + ) {
int temp = 0 ;
2019-11-15 20:00:25 +00:00
for ( int j = 0 ; j < 5 ; j + + ) {
2022-08-19 19:34:22 +00:00
if ( planes [ j ] . DistanceToPoint ( box . points [ i ] ) > = 0.f ) {
2016-06-16 20:05:29 +00:00
temp + + ;
}
}
2019-11-15 20:00:25 +00:00
if ( temp = = 5 ) 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
2019-11-15 20:00:25 +00:00
for ( int i = 0 ; i < 5 ; i + + ) {
2016-06-16 20:05:29 +00:00
int temp = 0 ;
for ( int j = 0 ; j < 8 ; j + + ) {
2022-08-19 19:34:22 +00:00
if ( planes [ i ] . DistanceToPoint ( box . points [ j ] ) > = 0.f ) {
2016-06-16 20:05:29 +00:00
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 + + ) {
2022-08-19 19:34:22 +00:00
if ( box . points [ i ] [ 2 ] < 0.f ) {
2017-02-07 14:05:03 +00:00
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 ) ;
}
}
}
2019-11-15 20:00:25 +00:00
void CNew3D : : ClipPolygon ( ClipPoly & clipPoly , Plane planes [ 5 ] )
2017-02-07 14:05:03 +00:00
{
//============
ClipPoly temp ;
ClipPoly * in ;
ClipPoly * out ;
//============
in = & clipPoly ;
out = & temp ;
2020-01-04 13:45:55 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
2017-02-07 14:05:03 +00:00
//=================
bool currentIn ;
float currentDot ;
//=================
currentDot = planes [ i ] . DotProduct ( in - > list [ 0 ] . pos ) ;
2022-08-19 19:34:22 +00:00
currentIn = ( currentDot + planes [ i ] . d ) > = 0.f ;
2019-11-15 20:00:25 +00:00
out - > count = 0 ;
2017-02-07 14:05:03 +00:00
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 ;
}
2022-08-19 19:34:22 +00:00
float nextDot = planes [ i ] . DotProduct ( in - > list [ nextIndex ] . pos ) ;
bool nextIn = ( nextDot + planes [ i ] . d ) > = 0.f ;
2017-02-07 14:05:03 +00:00
// we have an intersection
if ( currentIn ! = nextIn ) {
float u = ( currentDot + planes [ i ] . d ) / ( currentDot - nextDot ) ;
2022-08-19 19:34:22 +00:00
const float * p1 = in - > list [ j ] . pos ;
const float * p2 = in - > list [ nextIndex ] . pos ;
2017-02-07 14:05:03 +00:00
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 ) ;
}
}
void CNew3D : : ClipModel ( const Model * m )
{
2018-09-13 12:50:34 +00:00
//===============================
ClipPoly clipPoly ;
std : : vector < FVertex > * vertices ;
int offset ;
//===============================
2017-02-07 14:05:03 +00:00
if ( m - > dynamic ) {
2018-09-13 12:50:34 +00:00
vertices = & m_polyBufferRam ;
offset = MAX_ROM_VERTS ;
2017-02-07 14:05:03 +00:00
}
else {
2018-09-13 12:50:34 +00:00
vertices = & m_polyBufferRom ;
2017-02-07 14:05:03 +00:00
offset = 0 ;
}
for ( const auto & mesh : * m - > meshes ) {
int start = mesh . vboOffset - offset ;
2018-09-13 12:50:34 +00:00
for ( int i = 0 ; i < mesh . vertexCount ; i + = m_numPolyVerts ) { // inc to next poly
2017-02-07 14:05:03 +00:00
2018-09-13 12:50:34 +00:00
for ( int j = 0 ; j < m_numPolyVerts ; j + + ) {
MultVec ( m - > modelMat , ( * vertices ) [ start + i + j ] . pos , clipPoly . list [ j ] . pos ) ; // copy all 3 of 4 our transformed vertices into our clip poly struct
}
2017-02-07 14:05:03 +00:00
2018-09-13 12:50:34 +00:00
clipPoly . count = m_numPolyVerts ;
2017-02-07 14:05:03 +00:00
ClipPolygon ( clipPoly , m_planes ) ;
for ( int j = 0 ; j < clipPoly . count ; j + + ) {
2022-08-19 19:34:22 +00:00
if ( clipPoly . list [ j ] . pos [ 2 ] < 0.f ) {
2017-02-07 14:05:03 +00:00
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 )
{
2022-08-19 19:34:22 +00:00
if ( far > 1e30 f ) {
far = near * 1000000.f ; // fix for ocean hunter which passes some FLT_MAX for a few matrices. HW must have some safe guard for these
2019-11-02 20:11:48 +00:00
}
2022-08-19 19:34:22 +00:00
if ( near < far / 1000000.f ) {
near = far / 1000000.f ; // if we get really close to zero somehow, we will have almost no depth precision
2017-02-08 16:14:50 +00:00
}
2018-01-10 20:37:21 +00:00
float l = near * vp - > angle_left ; // we need to calc the shape of the projection frustum for culling
float r = near * vp - > angle_right ;
float t = near * vp - > angle_top ;
float b = near * vp - > angle_bottom ;
2017-02-07 14:05:03 +00:00
vp - > projectionMatrix . LoadIdentity ( ) ; // reset matrix
if ( ( vp - > vpX = = 0 ) & & ( vp - > vpWidth > = 495 ) & & ( vp - > vpY = = 0 ) & & ( vp - > vpHeight > = 383 ) ) {
2018-05-03 03:46:44 +00:00
/*
* Compute aspect ratio correction factor . " Window " refers to the full GL
* viewport ( i . e . , totalXRes x totalYRes ) . " Viewable area " is the effective
* Model 3 screen ( xRes x yRes ) . In non - wide - screen , non - stretch mode , this
* is intended to replicate the 496 x384 display and may in general be
* smaller than the window . The rest of the window appears to have a
* border , which is created by a scissor box .
*
* In wide - screen mode , we want to expand the frustum horizontally to fill
* the window . We want the aspect ratio to be correct . To accomplish this ,
* the viewable area is set * the same * as in non - wide - screen mode ( e . g . ,
* often smaller than the window ) but glScissor ( ) is set by the OSD layer ' s
* screen setup code to reveal the entire window .
*
* In stretch mode , the window and viewable area are both set the same ,
* which means there will be no aspect ratio correction and the display
* will stretch to fill the entire window while keeping the view frustum
* the same as a 496 x384 Model 3 display . The display will be distorted .
*/
2017-02-07 14:05:03 +00:00
float windowAR = ( float ) m_totalXRes / ( float ) m_totalYRes ;
2018-06-16 21:31:29 +00:00
float viewableAreaAR = ( float ) m_xRes / ( float ) m_yRes ;
2018-05-03 03:46:44 +00:00
// Will expand horizontal frustum planes only in non-stretch mode (wide-
// screen and non-wide-screen modes have identical resolution parameters
// and only their scissor box differs)
float correction = windowAR / viewableAreaAR ;
2017-02-07 14:05:03 +00:00
vp - > x = 0 ;
2022-08-19 19:34:22 +00:00
vp - > y = m_yOffs + ( int ) ( ( float ) ( 384 - ( vp - > vpY + vp - > vpHeight ) ) * m_yRatio ) ;
2017-02-07 14:05:03 +00:00
vp - > width = m_totalXRes ;
2022-08-19 19:34:22 +00:00
vp - > height = ( int ) ( ( float ) vp - > vpHeight * m_yRatio ) ;
2017-02-07 14:05:03 +00:00
vp - > projectionMatrix . Frustum ( l * correction , r * correction , b , t , near , far ) ;
}
else {
2022-08-19 19:34:22 +00:00
vp - > x = m_xOffs + ( int ) ( ( float ) vp - > vpX * m_xRatio ) ;
vp - > y = m_yOffs + ( int ) ( ( float ) ( 384 - ( vp - > vpY + vp - > vpHeight ) ) * m_yRatio ) ;
vp - > width = ( int ) ( ( float ) vp - > vpWidth * m_xRatio ) ;
vp - > height = ( int ) ( ( float ) vp - > vpHeight * m_yRatio ) ;
2017-02-07 14:05:03 +00:00
vp - > projectionMatrix . Frustum ( l , r , b , t , near , far ) ;
}
}
2017-09-02 14:54:16 +00:00
void CNew3D : : SetSunClamp ( bool enable )
{
m_sunClamp = enable ;
}
2018-09-02 21:36:24 +00:00
void CNew3D : : SetSignedShade ( bool enable )
{
m_shadeIsSigned = enable ;
}
2018-10-19 20:59:46 +00:00
float CNew3D : : GetLosValue ( int layer )
{
2022-01-02 12:48:09 +00:00
// we always write to the 'back' buffer, and the software reads from the front
// then they get swapped
std : : lock_guard < std : : mutex > guard ( m_losMutex ) ;
return m_losFront - > value [ layer ] ;
2018-10-19 20:59:46 +00:00
}
void CNew3D : : TranslateLosPosition ( int inX , int inY , int & outX , int & outY )
{
// remap real3d 496x384 to our new viewport
inY = 384 - inY ;
outX = m_xOffs + int ( inX * m_xRatio ) ;
outY = m_yOffs + int ( inY * m_yRatio ) ;
}
bool CNew3D : : ProcessLos ( int priority )
{
for ( const auto & n : m_nodes ) {
if ( n . viewport . priority = = priority ) {
if ( n . viewport . losPosX | | n . viewport . losPosY ) {
int losX , losY ;
TranslateLosPosition ( n . viewport . losPosX , n . viewport . losPosY , losX , losY ) ;
float depth ;
glReadPixels ( losX , losY , 1 , 1 , GL_DEPTH_COMPONENT , GL_FLOAT , & depth ) ;
depth = 2.0f * depth - 1.0f ;
float zNear = m_nfPairs [ priority ] . zNear ;
float zFar = m_nfPairs [ priority ] . zFar ;
float zVal = 2.0f * zNear * zFar / ( zFar + zNear - depth * ( zFar - zNear ) ) ;
2023-11-03 13:24:59 +00:00
// real3d test program indicates that return values are 1/zVal
zVal = 1.0f / zVal ;
GLubyte stencilVal ;
glReadPixels ( losX , losY , 1 , 1 , GL_STENCIL_INDEX , GL_UNSIGNED_BYTE , & stencilVal ) ;
2023-11-10 19:13:37 +00:00
// apply our mask to stencil, because layered poly attributes use the lower bits
stencilVal & = 0x80 ;
2023-11-03 13:24:59 +00:00
// if the stencil val is zero that means we've hit sky or whatever, if it hits a 1 we've hit geometry
// the real3d returns 1 in the top bit of the float if the line of sight test passes (ie doesn't hit geometry)
auto zValP = reinterpret_cast < unsigned char * > ( & zVal ) ; // this is legal in c++, casting to int technically isn't
if ( stencilVal = = 0 ) {
zValP [ 0 ] | = 1 ; // set first bit to 1
}
else {
zValP [ 0 ] & = 0xFE ; // set first bit to zero
}
2022-01-02 12:48:09 +00:00
m_losBack - > value [ priority ] = zVal ;
2023-11-03 13:24:59 +00:00
2018-10-19 20:59:46 +00:00
return true ;
}
}
}
return false ;
}
2017-10-05 19:15:00 +00:00
} // New3D