2022-08-19 19:34:22 +00:00
# include "New3D.h"
2016-03-21 04:10:14 +00:00
# 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>
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-08-19 19:34:22 +00:00
CNew3D : : CNew3D ( const Util : : Config : : Node & config , const std : : string & gameName )
2017-10-05 19:15:00 +00:00
: m_r3dShader ( config ) ,
m_r3dScrollFog ( config ) ,
m_gameName ( gameName )
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 ;
}
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 ( ) ;
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
}
2018-09-13 12:50:34 +00:00
m_vbo . Create ( GL_ARRAY_BUFFER , GL_DYNAMIC_DRAW , sizeof ( FVertex ) * ( MAX_RAM_VERTS + MAX_ROM_VERTS ) ) ;
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
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 ;
2018-05-03 03:46:44 +00:00
m_xRes = xRes ;
m_yRes = yRes ;
2016-03-21 04:10:14 +00:00
m_totalXRes = totalXResParam ;
m_totalYRes = totalYResParam ;
m_r3dShader . LoadShader ( ) ;
2018-06-16 21:31:29 +00:00
m_r3dFrameBuffers . CreateFBO ( totalXResParam , totalYResParam ) ;
2016-03-21 04:10:14 +00:00
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 ( )
{
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.
// 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
2020-09-07 16:25:18 +00:00
float rgba [ 4 ] ;
2017-04-02 21:03:59 +00:00
2016-10-09 18:10:57 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
for ( auto & n : m_nodes ) {
2017-05-06 16:47:31 +00:00
if ( n . viewport . priority = = i ) {
2022-08-19 19:34:22 +00:00
if ( n . viewport . scrollFog ! = 0.f ) {
2020-09-07 16:25:18 +00:00
rgba [ 0 ] = n . viewport . fogParams [ 0 ] ;
rgba [ 1 ] = n . viewport . fogParams [ 1 ] ;
rgba [ 2 ] = n . viewport . fogParams [ 2 ] ;
rgba [ 3 ] = n . viewport . scrollFog ;
goto CheckScroll ;
2017-05-06 16:47:31 +00:00
}
}
}
2020-09-07 16:25:18 +00:00
}
return ;
CheckScroll :
for ( int i = 0 ; i < 4 ; i + + ) {
for ( auto & n : m_nodes ) {
if ( n . viewport . priority = = i ) {
2016-10-09 16:34:12 +00:00
2020-09-07 16:25:18 +00:00
//if we have a fog density value
2022-08-19 19:34:22 +00:00
if ( n . viewport . fogParams [ 3 ] ! = 0.f ) {
2017-04-02 21:03:59 +00:00
2020-09-07 16:25:18 +00:00
if ( rgba [ 0 ] = = n . viewport . fogParams [ 0 ] & &
rgba [ 1 ] = = n . viewport . fogParams [ 1 ] & &
rgba [ 2 ] = = n . viewport . fogParams [ 2 ] ) {
2016-12-04 22:37:49 +00:00
2020-09-08 09:10:24 +00:00
glViewport ( n . viewport . x , n . viewport . y , n . viewport . width , n . viewport . height ) ;
2020-09-07 16:25:18 +00:00
m_r3dScrollFog . DrawScrollFog ( rgba , n . viewport . scrollAtt , n . viewport . fogParams [ 6 ] , n . viewport . spotFogColor , n . viewport . spotEllipse ) ;
return ;
}
}
}
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
{
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 ;
}
2016-06-10 11:29:20 +00:00
std : : shared_ptr < Texture > tex1 ;
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
2018-06-16 21:31:29 +00:00
if ( ! mesh . Render ( layer ) ) 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
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 ) ) {
2018-10-13 13:29:45 +00:00
// texture already bound
2016-06-10 11:29:20 +00:00
}
else {
2018-10-13 13:29:45 +00:00
tex1 = m_texSheet . BindTexture ( m_textureRAM , mesh . format , x , y , mesh . width , mesh . height ) ;
2016-06-10 11:29:20 +00:00
if ( tex1 ) {
tex1 - > BindTexture ( ) ;
}
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 ) ;
2018-10-13 13:29:45 +00:00
auto tex2 = m_texSheet . BindTexture ( m_textureRAM , 0 , 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 ) ;
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 ) ;
m_r3dShader . SetShader ( true ) ;
glEnableVertexAttribArray ( 0 ) ;
glEnableVertexAttribArray ( 1 ) ;
glEnableVertexAttribArray ( 2 ) ;
glEnableVertexAttribArray ( 3 ) ;
glEnableVertexAttribArray ( 4 ) ;
glEnableVertexAttribArray ( 5 ) ;
// 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 ) ) ;
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
glDisable ( GL_BLEND ) ;
2018-06-16 21:31:29 +00:00
glStencilFunc ( GL_EQUAL , 0 , 0xFF ) ; // basically stencil test passes if the value is zero
2022-08-19 19:34:22 +00:00
glStencilOp ( GL_KEEP , GL_INCR , GL_INCR ) ; // if the stencil test passes, we increment the value
2018-06-16 21:31:29 +00:00
glStencilMask ( 0xFF ) ;
}
void CNew3D : : DisableRenderStates ( )
{
m_vbo . Bind ( false ) ;
m_r3dShader . SetShader ( false ) ;
glDisable ( GL_STENCIL_TEST ) ;
glDisableVertexAttribArray ( 0 ) ;
glDisableVertexAttribArray ( 1 ) ;
glDisableVertexAttribArray ( 2 ) ;
glDisableVertexAttribArray ( 3 ) ;
glDisableVertexAttribArray ( 4 ) ;
glDisableVertexAttribArray ( 5 ) ;
}
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
DrawScrollFog ( ) ; // fog layer if applicable must be drawn here
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
2019-01-21 14:30:42 +00:00
m_r3dFrameBuffers . SetFBO ( Layer : : trans12 ) ;
glClear ( GL_COLOR_BUFFER_BIT ) ; // wipe both trans layers
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
2019-01-21 14:30:42 +00:00
m_r3dFrameBuffers . SetFBO ( Layer : : colour ) ;
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ) ;
2022-08-19 19:34:22 +00:00
2018-06-16 21:31:29 +00:00
SetRenderStates ( ) ;
m_r3dShader . DiscardAlpha ( true ) ; // discard all translucent pixels in opaque pass
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
DisableRenderStates ( ) ;
m_r3dFrameBuffers . DrawOverTransLayers ( ) ; // mask trans layer with opaque pixels
m_r3dFrameBuffers . CompositeBaseLayer ( ) ; // copy opaque pixels to back buffer
SetRenderStates ( ) ;
2022-10-07 07:01:45 +00:00
2018-06-17 11:30:01 +00:00
glDepthFunc ( GL_LESS ) ; // alpha polys seem to use gl_less (ocean hunter)
2019-01-21 14:30:42 +00:00
m_r3dShader . DiscardAlpha ( false ) ; // render only translucent pixels
m_r3dFrameBuffers . StoreDepth ( ) ; // save depth buffer for 1st trans pass
m_r3dFrameBuffers . SetFBO ( Layer : : trans1 ) ;
RenderScene ( pri , renderOverlay , Layer : : trans1 ) ;
2018-06-16 21:31:29 +00:00
2019-01-21 14:30:42 +00:00
m_r3dFrameBuffers . RestoreDepth ( ) ; // restore depth buffer, trans layers don't seem to depth test against each other
m_r3dFrameBuffers . SetFBO ( Layer : : trans2 ) ;
RenderScene ( pri , renderOverlay , Layer : : trans2 ) ;
2018-06-16 21:31:29 +00:00
DisableRenderStates ( ) ;
if ( ! hasOverlay ) break ; // no high priority polys
}
}
2019-01-21 14:30:42 +00:00
m_r3dFrameBuffers . CompositeAlphaLayer ( ) ;
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
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 )
{
2019-07-28 12:14:13 +00:00
enum class NodeType { undefined = - 1 , viewport = 0 , rootNode = 1 , cullingNode = 2 } ;
2016-03-21 04:10:14 +00:00
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 ;
2022-08-19 19:34:22 +00:00
//float fBlendRadius;
//UINT8 lodTablePointer;
2019-07-28 12:14:13 +00:00
NodeType nodeType ;
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 ;
2022-08-19 19:34:22 +00:00
//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
}
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
float modelScale = Util : : Uint32AsFloat ( 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 ) {
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
2017-04-17 10:40:07 +00:00
uCullRadius = node [ 9 - m_offset ] & 0xFFFF ;
fCullRadius = R3DFloat : : GetFloat16 ( uCullRadius ) ;
uBlendRadius = node [ 9 - m_offset ] > > 16 ;
2022-08-19 19:34:22 +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
}
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 ;
}
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 ) ;
}
// 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
2022-08-19 19:34:22 +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
}
}
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 ( ) ;
2016-12-06 12:25:34 +00:00
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 - > 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 ) {
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-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 ) {
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
}
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 ;
}
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
}
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 ) ;
if ( depth < 0.99f | | depth = = 1.0f ) { // kinda guess work but when depth = 1, haven't drawn anything, when 0.99~ drawing sky somewhere far
return false ;
}
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 ) ) ;
2022-01-02 12:48:09 +00:00
m_losBack - > value [ priority ] = zVal ;
2018-10-19 20:59:46 +00:00
return true ;
}
}
}
return false ;
}
2017-10-05 19:15:00 +00:00
} // New3D