mirror of
https://github.com/DaedalusX64/daedalus.git
synced 2025-04-02 10:21:48 -04:00
1245 lines
39 KiB
C++
1245 lines
39 KiB
C++
|
|
#include "Base/Types.h"
|
|
|
|
#include <pspgu.h>
|
|
|
|
#include "Combiner/BlendConstant.h"
|
|
#include "Combiner/CombinerTree.h"
|
|
#include "Combiner/RenderSettings.h"
|
|
#include "Core/ROM.h"
|
|
#include "Debug/Dump.h"
|
|
#include "Graphics/GraphicsContext.h"
|
|
#include "Graphics/NativeTexture.h"
|
|
#include "HLEGraphics/CachedTexture.h"
|
|
#include "HLEGraphics/DLDebug.h"
|
|
#include "HLEGraphics/RDPStateManager.h"
|
|
#include "HLEGraphics/TextureCache.h"
|
|
#include "Utility/MathUtil.h"
|
|
#include "SysPSP/HLEGraphics/RendererPSP.h"
|
|
#include "Ultra/ultra_gbi.h"
|
|
|
|
#include "Utility/Profiler.h"
|
|
|
|
|
|
//Draw normal filled triangles
|
|
#define DRAW_MODE GU_TRIANGLES
|
|
//Draw lines
|
|
//Also enable clean scene in advanced menu //Corn
|
|
//#define DRAW_MODE GU_LINE_STRIP
|
|
|
|
// FIXME - surely these should be defined by a system header? Or GU_TRUE etc?
|
|
#define GL_TRUE 1
|
|
#define GL_FALSE 0
|
|
|
|
BaseRenderer * gRenderer = nullptr;
|
|
RendererPSP * gRendererPSP = nullptr;
|
|
|
|
extern void InitBlenderMode( u32 blender );
|
|
|
|
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
|
|
|
|
// General blender used for Blend Explorer when debuging Dlists //Corn
|
|
DebugBlendSettings gDBlend;
|
|
|
|
static const u32 kPlaceholderTextureWidth = 16;
|
|
static const u32 kPlaceholderTextureHeight = 16;
|
|
static const u32 kPlaceholderSize = kPlaceholderTextureWidth * kPlaceholderTextureHeight;
|
|
|
|
u32 alignas(DATA_ALIGN) gWhiteTexture[kPlaceholderSize];
|
|
u32 alignas(DATA_ALIGN) gPlaceholderTexture[kPlaceholderSize];
|
|
u32 alignas(DATA_ALIGN) gSelectedTexture[kPlaceholderSize];
|
|
|
|
#define BLEND_MODE_MAKER \
|
|
{ \
|
|
const u32 PSPtxtFunc[5] = \
|
|
{ \
|
|
GU_TFX_MODULATE, \
|
|
GU_TFX_BLEND, \
|
|
GU_TFX_ADD, \
|
|
GU_TFX_REPLACE, \
|
|
GU_TFX_DECAL \
|
|
}; \
|
|
const u32 PSPtxtA[2] = \
|
|
{ \
|
|
GU_TCC_RGB, \
|
|
GU_TCC_RGBA \
|
|
}; \
|
|
switch( gDBlend.ForceRGB ) \
|
|
{ \
|
|
case 1: details.ColourAdjuster.SetRGB( c32::White ); break; \
|
|
case 2: details.ColourAdjuster.SetRGB( c32::Black ); break; \
|
|
case 3: details.ColourAdjuster.SetRGB( c32::Red ); break; \
|
|
case 4: details.ColourAdjuster.SetRGB( c32::Green ); break; \
|
|
case 5: details.ColourAdjuster.SetRGB( c32::Blue ); break; \
|
|
case 6: details.ColourAdjuster.SetRGB( c32::Magenta ); break; \
|
|
case 7: details.ColourAdjuster.SetRGB( c32::Gold ); break; \
|
|
} \
|
|
switch( gDBlend.SetRGB ) \
|
|
{ \
|
|
case 1: details.ColourAdjuster.SetRGB( details.PrimColour ); break; \
|
|
case 2: details.ColourAdjuster.SetRGB( details.PrimColour.ReplicateAlpha() ); break; \
|
|
case 3: details.ColourAdjuster.SetRGB( details.EnvColour ); break; \
|
|
case 4: details.ColourAdjuster.SetRGB( details.EnvColour.ReplicateAlpha() ); break; \
|
|
} \
|
|
switch( gDBlend.SetA ) \
|
|
{ \
|
|
case 1: details.ColourAdjuster.SetA( details.PrimColour ); break; \
|
|
case 2: details.ColourAdjuster.SetA( details.PrimColour.ReplicateAlpha() ); break; \
|
|
case 3: details.ColourAdjuster.SetA( details.EnvColour ); break; \
|
|
case 4: details.ColourAdjuster.SetA( details.EnvColour.ReplicateAlpha() ); break; \
|
|
} \
|
|
switch( gDBlend.SetRGBA ) \
|
|
{ \
|
|
case 1: details.ColourAdjuster.SetRGBA( details.PrimColour ); break; \
|
|
case 2: details.ColourAdjuster.SetRGBA( details.PrimColour.ReplicateAlpha() ); break; \
|
|
case 3: details.ColourAdjuster.SetRGBA( details.EnvColour ); break; \
|
|
case 4: details.ColourAdjuster.SetRGBA( details.EnvColour.ReplicateAlpha() ); break; \
|
|
} \
|
|
switch( gDBlend.ModRGB ) \
|
|
{ \
|
|
case 1: details.ColourAdjuster.ModulateRGB( details.PrimColour ); break; \
|
|
case 2: details.ColourAdjuster.ModulateRGB( details.PrimColour.ReplicateAlpha() ); break; \
|
|
case 3: details.ColourAdjuster.ModulateRGB( details.EnvColour ); break; \
|
|
case 4: details.ColourAdjuster.ModulateRGB( details.EnvColour.ReplicateAlpha() ); break; \
|
|
} \
|
|
switch( gDBlend.ModA ) \
|
|
{ \
|
|
case 1: details.ColourAdjuster.ModulateA( details.PrimColour ); break; \
|
|
case 2: details.ColourAdjuster.ModulateA( details.PrimColour.ReplicateAlpha() ); break; \
|
|
case 3: details.ColourAdjuster.ModulateA( details.EnvColour ); break; \
|
|
case 4: details.ColourAdjuster.ModulateA( details.EnvColour.ReplicateAlpha() ); break; \
|
|
} \
|
|
switch( gDBlend.ModRGBA ) \
|
|
{ \
|
|
case 1: details.ColourAdjuster.ModulateRGBA( details.PrimColour ); break; \
|
|
case 2: details.ColourAdjuster.ModulateRGBA( details.PrimColour.ReplicateAlpha() ); break; \
|
|
case 3: details.ColourAdjuster.ModulateRGBA( details.EnvColour ); break; \
|
|
case 4: details.ColourAdjuster.ModulateRGBA( details.EnvColour.ReplicateAlpha() ); break; \
|
|
} \
|
|
switch( gDBlend.SubRGB ) \
|
|
{ \
|
|
case 1: details.ColourAdjuster.SubtractRGB( details.PrimColour ); break; \
|
|
case 2: details.ColourAdjuster.SubtractRGB( details.PrimColour.ReplicateAlpha() ); break; \
|
|
case 3: details.ColourAdjuster.SubtractRGB( details.EnvColour ); break; \
|
|
case 4: details.ColourAdjuster.SubtractRGB( details.EnvColour.ReplicateAlpha() ); break; \
|
|
} \
|
|
switch( gDBlend.SubA ) \
|
|
{ \
|
|
case 1: details.ColourAdjuster.SubtractA( details.PrimColour ); break; \
|
|
case 2: details.ColourAdjuster.SubtractA( details.PrimColour.ReplicateAlpha() ); break; \
|
|
case 3: details.ColourAdjuster.SubtractA( details.EnvColour ); break; \
|
|
case 4: details.ColourAdjuster.SubtractA( details.EnvColour.ReplicateAlpha() ); break; \
|
|
} \
|
|
switch( gDBlend.SubRGBA ) \
|
|
{ \
|
|
case 1: details.ColourAdjuster.SubtractRGBA( details.PrimColour ); break; \
|
|
case 2: details.ColourAdjuster.SubtractRGBA( details.PrimColour.ReplicateAlpha() ); break; \
|
|
case 3: details.ColourAdjuster.SubtractRGBA( details.EnvColour ); break; \
|
|
case 4: details.ColourAdjuster.SubtractRGBA( details.EnvColour.ReplicateAlpha() ); break; \
|
|
} \
|
|
if( gDBlend.AOpaque ) details.ColourAdjuster.SetAOpaque(); \
|
|
switch( gDBlend.sceENV ) \
|
|
{ \
|
|
case 1: sceGuTexEnvColor( details.EnvColour.GetColour() ); break; \
|
|
case 2: sceGuTexEnvColor( details.PrimColour.GetColour() ); break; \
|
|
} \
|
|
details.InstallTexture = gDBlend.TexInstall; \
|
|
sceGuTexFunc( PSPtxtFunc[ (gDBlend.TXTFUNC >> 1) % 6 ], PSPtxtA[ gDBlend.TXTFUNC & 1 ] ); \
|
|
}
|
|
|
|
#endif // DAEDALUS_DEBUG_DISPLAYLIST
|
|
|
|
RendererPSP::RendererPSP()
|
|
{
|
|
//
|
|
// Set up RGB = T0, A = T0
|
|
//
|
|
mCopyBlendStates = new CBlendStates;
|
|
{
|
|
CAlphaRenderSettings * alpha_settings( new CAlphaRenderSettings( "Copy" ) );
|
|
CRenderSettingsModulate * colour_settings( new CRenderSettingsModulate( "Copy" ) );
|
|
|
|
alpha_settings->AddTermTexel0();
|
|
colour_settings->AddTermTexel0();
|
|
|
|
mCopyBlendStates->SetAlphaSettings( alpha_settings );
|
|
mCopyBlendStates->AddColourSettings( colour_settings );
|
|
}
|
|
|
|
//
|
|
// Set up RGB = Diffuse, A = Diffuse
|
|
//
|
|
mFillBlendStates = new CBlendStates;
|
|
{
|
|
CAlphaRenderSettings * alpha_settings( new CAlphaRenderSettings( "Fill" ) );
|
|
CRenderSettingsModulate * colour_settings( new CRenderSettingsModulate( "Fill" ) );
|
|
|
|
alpha_settings->AddTermConstant( new CBlendConstantExpressionValue( BC_SHADE ) );
|
|
colour_settings->AddTermConstant( new CBlendConstantExpressionValue( BC_SHADE ) );
|
|
|
|
mFillBlendStates->SetAlphaSettings( alpha_settings );
|
|
mFillBlendStates->AddColourSettings( colour_settings );
|
|
}
|
|
|
|
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
|
|
memset( gWhiteTexture, 0xff, sizeof(gWhiteTexture) );
|
|
|
|
memset( &gDBlend.TexInstall, 0, sizeof(gDBlend) );
|
|
gDBlend.TexInstall = 1;
|
|
|
|
u32 texel_idx = 0;
|
|
const u32 COL_MAGENTA = c32::Magenta.GetColour();
|
|
const u32 COL_GREEN = c32::Green.GetColour();
|
|
const u32 COL_BLACK = c32::Black.GetColour();
|
|
for(u32 y = 0; y < kPlaceholderTextureHeight; ++y)
|
|
{
|
|
for(u32 x = 0; x < kPlaceholderTextureWidth; ++x)
|
|
{
|
|
gPlaceholderTexture[ texel_idx ] = ((x&1) == (y&1)) ? COL_MAGENTA : COL_BLACK;
|
|
gSelectedTexture[ texel_idx ] = ((x&1) == (y&1)) ? COL_GREEN : COL_BLACK;
|
|
|
|
texel_idx++;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
RendererPSP::~RendererPSP()
|
|
{
|
|
delete mFillBlendStates;
|
|
delete mCopyBlendStates;
|
|
}
|
|
|
|
void RendererPSP::RestoreRenderStates()
|
|
{
|
|
// Initialise the device to our default state
|
|
|
|
// No fog
|
|
sceGuDisable(GU_FOG);
|
|
|
|
// We do our own culling
|
|
sceGuDisable(GU_CULL_FACE);
|
|
|
|
// But clip our tris please (looks better in far field see Aerogauge)
|
|
sceGuEnable(GU_CLIP_PLANES);
|
|
//sceGuDisable(GU_CLIP_PLANES);
|
|
|
|
//u32 width, height;
|
|
//CGraphicsContext::Get()->GetScreenSize(&width, &height);
|
|
|
|
//This was breaking Glover's sky and Rocket Robot's right/left sides of the screen when in un/scaled mode
|
|
//I think the problem was that GetScreenSize for PSP only returns 480x240, we should get the current screen size?
|
|
//Or get scissor.left/right/top/bottom, then pass them trough ConvertN64ToScreen and then use those to set PSP scissors
|
|
//sceGuScissor(0,0, width,height);
|
|
|
|
sceGuEnable(GU_SCISSOR_TEST);
|
|
|
|
// We do our own lighting
|
|
sceGuDisable(GU_LIGHTING);
|
|
|
|
sceGuAlphaFunc(GU_GEQUAL, 0x04, 0xff );
|
|
sceGuEnable(GU_ALPHA_TEST);
|
|
|
|
sceGuDisable( GU_BLEND );
|
|
|
|
// Default is ZBuffer disabled
|
|
sceGuDepthMask(GL_TRUE); // GL_TRUE to disable z-writes
|
|
sceGuDepthFunc(GU_GEQUAL); // GEQUAL?
|
|
sceGuDisable(GU_DEPTH_TEST);
|
|
|
|
// Initialise all the renderstate to our defaults.
|
|
sceGuShadeModel(GU_SMOOTH);
|
|
|
|
sceGuTexEnvColor( c32::White.GetColour() );
|
|
sceGuTexOffset(0.0f,0.0f);
|
|
|
|
//sceGuFog(near,far,mFogColour);
|
|
// Texturing stuff
|
|
sceGuTexFunc(GU_TFX_REPLACE,GU_TCC_RGB);
|
|
//sceGuTexFilter(GU_LINEAR,GU_LINEAR);
|
|
sceGuTexWrap(GU_REPEAT,GU_REPEAT);
|
|
|
|
//sceGuSetMatrix( GU_PROJECTION, reinterpret_cast< const ScePspFMatrix4 * >( &gMatrixIdentity ) );
|
|
sceGuSetMatrix( GU_VIEW, reinterpret_cast< const ScePspFMatrix4 * >( &gMatrixIdentity ) );
|
|
sceGuSetMatrix( GU_MODEL, reinterpret_cast< const ScePspFMatrix4 * >( &gMatrixIdentity ) );
|
|
}
|
|
|
|
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
|
|
void RendererPSP::ResetDebugState()
|
|
{
|
|
BaseRenderer::ResetDebugState();
|
|
mRecordedCombinerStates.clear();
|
|
}
|
|
#endif
|
|
|
|
RendererPSP::SBlendStateEntry RendererPSP::LookupBlendState( u64 mux, bool two_cycles )
|
|
{
|
|
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
|
|
DAEDALUS_PROFILE( "RendererPSP::LookupBlendState" );
|
|
mRecordedCombinerStates.insert( mux );
|
|
#endif
|
|
|
|
REG64 key;
|
|
key._u64 = mux;
|
|
|
|
// Top 8 bits are never set - use the very top one to differentiate between 1/2 cycles
|
|
key._u32_1 |= (two_cycles << 31);
|
|
|
|
BlendStatesMap::const_iterator it( mBlendStatesMap.find( key._u64 ) );
|
|
if( it != mBlendStatesMap.end() )
|
|
{
|
|
return it->second;
|
|
}
|
|
|
|
// Blendmodes with Inexact blends either get an Override blend or a Default blend (GU_TFX_MODULATE)
|
|
// If its not an Inexact blend then we check if we need to Force a blend mode none the less// Salvy
|
|
//
|
|
SBlendStateEntry entry;
|
|
CCombinerTree tree( mux, two_cycles );
|
|
entry.States = tree.GetBlendStates();
|
|
|
|
if( entry.States->IsInexact() )
|
|
{
|
|
entry.OverrideFunction = LookupOverrideBlendModeInexact( mux );
|
|
}
|
|
else
|
|
{
|
|
// This is for non-inexact blends, errg hacks and such to be more precise
|
|
entry.OverrideFunction = LookupOverrideBlendModeForced( mux );
|
|
}
|
|
|
|
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
|
|
printf( "Adding %08x%08x - %d cycles - %s\n", u32(mux>>32), u32(mux), two_cycles ? 2 : 1, entry.States->IsInexact() ? IsCombinerStateDefault(mux) ? "Inexact(Default)" : "Inexact(Override)" : entry.OverrideFunction==nullptr ? "Auto" : "Forced");
|
|
#endif
|
|
|
|
//Add blend mode to the Blend States Map
|
|
mBlendStatesMap[ key._u64 ] = entry;
|
|
|
|
return entry;
|
|
}
|
|
|
|
void RendererPSP::RenderTriangles( DaedalusVtx * p_vertices, u32 num_vertices, bool disable_zbuffer )
|
|
{
|
|
if( mTnL.Flags.Texture )
|
|
{
|
|
UpdateTileSnapshots( mTextureTile );
|
|
|
|
const std::shared_ptr<CNativeTexture> texture = mBoundTexture[0];
|
|
|
|
if( texture && (mTnL.Flags._u32 & (TNL_LIGHT|TNL_TEXGEN)) != (TNL_LIGHT|TNL_TEXGEN) )
|
|
{
|
|
float scale_x = texture->GetScaleX();
|
|
float scale_y = texture->GetScaleY();
|
|
|
|
// Hack to fix the sun in Zelda OOT/MM
|
|
if( g_ROM.ZELDA_HACK && (gRDPOtherMode.L == 0x0c184241) ) //&& ti.GetFormat() == G_IM_FMT_I && (ti.GetWidth() == 64)
|
|
{
|
|
scale_x *= 0.5f;
|
|
scale_y *= 0.5f;
|
|
}
|
|
sceGuTexOffset( -mTileTopLeft[ 0 ].s * scale_x / 4.f,
|
|
-mTileTopLeft[ 0 ].t * scale_y / 4.f );
|
|
sceGuTexScale( scale_x, scale_y );
|
|
}
|
|
else
|
|
{
|
|
sceGuTexOffset( 0.0f, 0.0f );
|
|
sceGuTexScale( 1.0f, 1.0f );
|
|
}
|
|
}
|
|
|
|
RenderUsingCurrentBlendMode( p_vertices, num_vertices, DRAW_MODE, GU_TRANSFORM_3D, disable_zbuffer );
|
|
}
|
|
|
|
inline void RendererPSP::RenderFog( DaedalusVtx * p_vertices, u32 num_vertices, u32 triangle_mode, u32 render_flags )
|
|
{
|
|
//This will render a second pass on triangles that are fog enabled to blend in the fog color as a function of depth(alpha) //Corn
|
|
//
|
|
//if( gRDPOtherMode.c1_m1a==3 || gRDPOtherMode.c1_m2a==3 || gRDPOtherMode.c2_m1a==3 || gRDPOtherMode.c2_m2a==3 )
|
|
{
|
|
//sceGuShadeModel(GU_SMOOTH);
|
|
sceGuDepthFunc(GU_EQUAL); //Make sure to only blend on pixels that has been rendered on first pass //Corn
|
|
sceGuDepthMask(GL_TRUE); //GL_TRUE to disable z-writes, no need to write to zbuffer for second pass //Corn
|
|
sceGuEnable(GU_BLEND);
|
|
sceGuDisable(GU_TEXTURE_2D); //Blend triangle without a texture
|
|
sceGuDisable(GU_ALPHA_TEST);
|
|
sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);
|
|
// Enable PSP hardware fog
|
|
sceGuEnable(GU_FOG);
|
|
|
|
// Configure fog range and color
|
|
u32 fogColor = (mFogColour.GetColour()); // Fog color
|
|
sceGuFog(mfog_near, mfog_far, fogColor);
|
|
|
|
sceGuDrawArray( triangle_mode, render_flags, num_vertices, nullptr, p_vertices );
|
|
|
|
sceGuDisable(GU_FOG);
|
|
|
|
sceGuDepthFunc(GU_GEQUAL); //Restore default depth function
|
|
}
|
|
}
|
|
|
|
void RendererPSP::RenderUsingCurrentBlendMode( DaedalusVtx * p_vertices, u32 num_vertices, u32 triangle_mode, u32 render_mode, bool disable_zbuffer )
|
|
{
|
|
static bool ZFightingEnabled = false;
|
|
|
|
DAEDALUS_PROFILE( "RendererPSP::RenderUsingCurrentBlendMode" );
|
|
|
|
if ( disable_zbuffer )
|
|
{
|
|
sceGuDisable(GU_DEPTH_TEST);
|
|
sceGuDepthMask( GL_TRUE ); // GL_TRUE to disable z-writes
|
|
}
|
|
else
|
|
{
|
|
// Fixes Zfighting issues we have on the PSP.
|
|
if( gRDPOtherMode.zmode == 3 )
|
|
{
|
|
if( !ZFightingEnabled )
|
|
{
|
|
ZFightingEnabled = true;
|
|
sceGuDepthRange(65535,80);
|
|
}
|
|
}
|
|
else if( ZFightingEnabled )
|
|
{
|
|
ZFightingEnabled = false;
|
|
sceGuDepthRange(65535,0);
|
|
}
|
|
|
|
// Enable or Disable ZBuffer test
|
|
if ( (mTnL.Flags.Zbuffer & gRDPOtherMode.z_cmp) | gRDPOtherMode.z_upd )
|
|
{
|
|
sceGuEnable(GU_DEPTH_TEST);
|
|
}
|
|
else
|
|
{
|
|
sceGuDisable(GU_DEPTH_TEST);
|
|
}
|
|
|
|
// GL_TRUE to disable z-writes
|
|
sceGuDepthMask( gRDPOtherMode.z_upd ? GL_FALSE : GL_TRUE );
|
|
}
|
|
|
|
// Initiate Texture Filter
|
|
//
|
|
// G_TF_AVERAGE : 1, G_TF_BILERP : 2 (linear)
|
|
// G_TF_POINT : 0 (nearest)
|
|
//
|
|
if( (gRDPOtherMode.text_filt != G_TF_POINT) | (gGlobalPreferences.ForceLinearFilter) )
|
|
{
|
|
sceGuTexFilter(GU_LINEAR,GU_LINEAR);
|
|
}
|
|
else
|
|
{
|
|
sceGuTexFilter(GU_NEAREST,GU_NEAREST);
|
|
}
|
|
|
|
u32 cycle_mode = gRDPOtherMode.cycle_type;
|
|
|
|
// Initiate Blender
|
|
//
|
|
if(cycle_mode < CYCLE_COPY && gRDPOtherMode.force_bl)
|
|
{
|
|
InitBlenderMode(gRDPOtherMode.blender);
|
|
}
|
|
else
|
|
{
|
|
sceGuDisable( GU_BLEND );
|
|
}
|
|
|
|
// Initiate Alpha test
|
|
//
|
|
if( (gRDPOtherMode.alpha_compare == G_AC_THRESHOLD) && !gRDPOtherMode.alpha_cvg_sel )
|
|
{
|
|
u8 alpha_threshold = mBlendColour.GetA();
|
|
sceGuAlphaFunc( (alpha_threshold | g_ROM.ALPHA_HACK) ? GU_GEQUAL : GU_GREATER, alpha_threshold, 0xff);
|
|
sceGuEnable(GU_ALPHA_TEST);
|
|
}
|
|
else if (gRDPOtherMode.cvg_x_alpha)
|
|
{
|
|
// Going over 0x70 breaks OOT, but going lesser than that makes lines on games visible...ex: Paper Mario.
|
|
// Also going over 0x30 breaks the birds in Tarzan :(. Need to find a better way to leverage this.
|
|
sceGuAlphaFunc(GU_GREATER, 0x70, 0xff);
|
|
sceGuEnable(GU_ALPHA_TEST);
|
|
}
|
|
else
|
|
{
|
|
sceGuDisable(GU_ALPHA_TEST);
|
|
}
|
|
|
|
SBlendStateEntry blend_entry;
|
|
|
|
switch ( cycle_mode )
|
|
{
|
|
case CYCLE_COPY: blend_entry.States = mCopyBlendStates; break;
|
|
case CYCLE_FILL: blend_entry.States = mFillBlendStates; break;
|
|
case CYCLE_1CYCLE: blend_entry = LookupBlendState( mMux, false ); break;
|
|
case CYCLE_2CYCLE: blend_entry = LookupBlendState( mMux, true ); break;
|
|
}
|
|
|
|
u32 render_flags = GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_VERTEX_32BITF | render_mode;
|
|
|
|
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
|
|
// Used for Blend Explorer, or Nasty texture
|
|
//
|
|
if( DebugBlendmode( p_vertices, num_vertices, triangle_mode, render_flags, mMux ) )
|
|
return;
|
|
#endif
|
|
|
|
// This check is for inexact blends which were handled either by a custom blendmode or auto blendmode thing
|
|
//
|
|
if( blend_entry.OverrideFunction != nullptr )
|
|
{
|
|
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
|
|
// Used for dumping mux and highlight inexact blend
|
|
//
|
|
DebugMux( blend_entry.States, p_vertices, num_vertices, triangle_mode, render_flags, mMux );
|
|
#endif
|
|
|
|
// Local vars for now
|
|
SBlendModeDetails details;
|
|
|
|
details.EnvColour = mEnvColour;
|
|
details.PrimColour = mPrimitiveColour;
|
|
details.InstallTexture = true;
|
|
details.ColourAdjuster.Reset();
|
|
|
|
blend_entry.OverrideFunction( details );
|
|
|
|
bool installed_texture = false;
|
|
|
|
if( details.InstallTexture )
|
|
{
|
|
u32 texture_idx = g_ROM.T1_HACK ? 1 : 0;
|
|
|
|
if( mBoundTexture[ texture_idx ] )
|
|
{
|
|
mBoundTexture[ texture_idx ]->InstallTexture();
|
|
|
|
sceGuTexWrap( mTexWrap[ texture_idx ].u, mTexWrap[ texture_idx ].v );
|
|
|
|
installed_texture = true;
|
|
}
|
|
}
|
|
|
|
// If no texture was specified, or if we couldn't load it, clear it out
|
|
if( !installed_texture )
|
|
{
|
|
sceGuDisable( GU_TEXTURE_2D );
|
|
}
|
|
|
|
if ( mTnL.Flags.Fog )
|
|
{
|
|
DaedalusVtx * p_FogVtx = static_cast<DaedalusVtx *>(sceGuGetMemory(num_vertices * sizeof(DaedalusVtx)));
|
|
memcpy( p_FogVtx, p_vertices, num_vertices * sizeof( DaedalusVtx ) );
|
|
details.ColourAdjuster.Process( p_vertices, num_vertices );
|
|
sceGuDrawArray( triangle_mode, render_flags, num_vertices, nullptr, p_vertices );
|
|
RenderFog( p_FogVtx, num_vertices, triangle_mode, render_flags );
|
|
}
|
|
else
|
|
{
|
|
details.ColourAdjuster.Process( p_vertices, num_vertices );
|
|
sceGuDrawArray( triangle_mode, render_flags, num_vertices, nullptr, p_vertices );
|
|
}
|
|
}
|
|
else if( blend_entry.States != nullptr )
|
|
{
|
|
RenderUsingRenderSettings( blend_entry.States, p_vertices, num_vertices, triangle_mode, render_flags );
|
|
}
|
|
else
|
|
{
|
|
#ifdef DAEDALUS_DEBUG_CONSOLE
|
|
// Set default states
|
|
DAEDALUS_ERROR( "Unhandled blend mode" );
|
|
#endif
|
|
sceGuDisable( GU_TEXTURE_2D );
|
|
sceGuDrawArray( triangle_mode, render_flags, num_vertices, nullptr, p_vertices );
|
|
}
|
|
}
|
|
|
|
void RendererPSP::RenderUsingRenderSettings( const CBlendStates * states, DaedalusVtx * p_vertices, u32 num_vertices, u32 triangle_mode, u32 render_flags)
|
|
{
|
|
DAEDALUS_PROFILE( "RendererPSP::RenderUsingRenderSettings" );
|
|
|
|
const CAlphaRenderSettings * alpha_settings( states->GetAlphaSettings() );
|
|
|
|
SRenderState state;
|
|
|
|
state.Vertices = p_vertices;
|
|
state.NumVertices = num_vertices;
|
|
state.PrimitiveColour = mPrimitiveColour;
|
|
state.EnvironmentColour = mEnvColour;
|
|
|
|
//Avoid copying vertices twice if we already save a copy to render fog //Corn
|
|
DaedalusVtx * p_FogVtx( mVtx_Save );
|
|
if( mTnL.Flags.Fog )
|
|
{
|
|
p_FogVtx = static_cast<DaedalusVtx *>(sceGuGetMemory(num_vertices * sizeof(DaedalusVtx)));
|
|
memcpy( p_FogVtx, p_vertices, num_vertices * sizeof( DaedalusVtx ) );
|
|
}
|
|
else if( states->GetNumStates() > 1 )
|
|
{
|
|
memcpy( mVtx_Save, p_vertices, num_vertices * sizeof( DaedalusVtx ) );
|
|
}
|
|
|
|
for( u32 i = 0; i < states->GetNumStates(); ++i )
|
|
{
|
|
const CRenderSettings * settings( states->GetColourSettings( i ) );
|
|
|
|
bool install_texture0( settings->UsesTexture0() || alpha_settings->UsesTexture0() );
|
|
bool install_texture1( settings->UsesTexture1() || alpha_settings->UsesTexture1() );
|
|
|
|
SRenderStateOut out;
|
|
|
|
memset( &out, 0, sizeof( out ) );
|
|
|
|
settings->Apply( install_texture0 || install_texture1, state, out );
|
|
alpha_settings->Apply( install_texture0 || install_texture1, state, out );
|
|
|
|
// TODO: this nobbles the existing diffuse colour on each pass. Need to use a second buffer...
|
|
if( i > 0 )
|
|
{
|
|
memcpy( p_vertices, p_FogVtx, num_vertices * sizeof( DaedalusVtx ) );
|
|
}
|
|
|
|
if(out.VertexExpressionRGB != nullptr)
|
|
{
|
|
out.VertexExpressionRGB->ApplyExpressionRGB( state );
|
|
}
|
|
if(out.VertexExpressionA != nullptr)
|
|
{
|
|
out.VertexExpressionA->ApplyExpressionAlpha( state );
|
|
}
|
|
|
|
bool installed_texture = false;
|
|
|
|
u32 texture_idx = 0;
|
|
|
|
if(install_texture0 || install_texture1)
|
|
{
|
|
u32 tfx = GU_TFX_MODULATE;
|
|
switch( out.BlendMode )
|
|
{
|
|
case PBM_MODULATE: tfx = GU_TFX_MODULATE; break;
|
|
case PBM_REPLACE: tfx = GU_TFX_REPLACE; break;
|
|
case PBM_BLEND: tfx = GU_TFX_BLEND; sceGuTexEnvColor( out.TextureFactor.GetColour() ); break;
|
|
}
|
|
|
|
sceGuTexFunc( tfx, out.BlendAlphaMode == PBAM_RGB ? GU_TCC_RGB : GU_TCC_RGBA );
|
|
|
|
if( g_ROM.T1_HACK )
|
|
{
|
|
// NB if install_texture0 and install_texture1 are both set, 1 wins out
|
|
texture_idx = install_texture1;
|
|
|
|
const std::shared_ptr<CNativeTexture> texture1 = mBoundTexture[ 1 ];
|
|
|
|
if( install_texture1 && texture1 && mTnL.Flags.Texture && (mTnL.Flags._u32 & (TNL_LIGHT|TNL_TEXGEN)) != (TNL_LIGHT|TNL_TEXGEN) )
|
|
{
|
|
float scale_x = texture1->GetScaleX();
|
|
float scale_y = texture1->GetScaleY();
|
|
|
|
sceGuTexOffset( -mTileTopLeft[ 1 ].s * scale_x / 4.f,
|
|
-mTileTopLeft[ 1 ].t * scale_y / 4.f );
|
|
sceGuTexScale( scale_x, scale_y );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// NB if install_texture0 and install_texture1 are both set, 0 wins out
|
|
texture_idx = install_texture0 ? 0 : 1;
|
|
}
|
|
|
|
std::shared_ptr<CNativeTexture> texture;
|
|
|
|
if(out.MakeTextureWhite)
|
|
{
|
|
TextureInfo white_ti = mBoundTextureInfo[ texture_idx ];
|
|
white_ti.SetWhite(true);
|
|
texture = CTextureCache::Get()->GetOrCreateTexture( white_ti );
|
|
}
|
|
else
|
|
{
|
|
texture = mBoundTexture[ texture_idx ];
|
|
}
|
|
|
|
if(texture != nullptr)
|
|
{
|
|
texture->InstallTexture();
|
|
installed_texture = true;
|
|
}
|
|
}
|
|
|
|
// If no texture was specified, or if we couldn't load it, clear it out
|
|
if( !installed_texture ) sceGuDisable(GU_TEXTURE_2D);
|
|
|
|
sceGuTexWrap( mTexWrap[texture_idx].u, mTexWrap[texture_idx].v );
|
|
|
|
sceGuDrawArray( triangle_mode, render_flags, num_vertices, nullptr, p_vertices );
|
|
|
|
if ( mTnL.Flags.Fog )
|
|
{
|
|
RenderFog( p_FogVtx, num_vertices, triangle_mode, render_flags );
|
|
}
|
|
}
|
|
}
|
|
|
|
void RendererPSP::TexRect( u32 tile_idx, const v2 & xy0, const v2 & xy1, TexCoord st0, TexCoord st1 )
|
|
{
|
|
mTnL.Flags.Fog = 0; //For now we force fog off for textrect, normally it should be fogged when depth_source is set //Corn
|
|
|
|
UpdateTileSnapshots( tile_idx );
|
|
|
|
// NB: we have to do this after UpdateTileSnapshot, as it set up mTileTopLeft etc.
|
|
PrepareTexRectUVs(&st0, &st1);
|
|
|
|
// Convert fixed point uvs back to floating point format.
|
|
// NB: would be nice to pass these as s16 ints, and use GU_TEXTURE_16BIT
|
|
v2 uv0( (float)st0.s / 32.f, (float)st0.t / 32.f );
|
|
v2 uv1( (float)st1.s / 32.f, (float)st1.t / 32.f );
|
|
|
|
v2 screen0;
|
|
v2 screen1;
|
|
if( gGlobalPreferences.ViewportType == VT_FULLSCREEN_HD )
|
|
{
|
|
screen0.x = roundf( roundf( HD_SCALE * xy0.x ) * mN64ToScreenScale.x + 59 ); //59 in translate is an ugly hack that only work on 480x272 display//Corn
|
|
screen0.y = roundf( roundf( xy0.y ) * mN64ToScreenScale.y + mN64ToScreenTranslate.y );
|
|
|
|
screen1.x = roundf( roundf( HD_SCALE * xy1.x ) * mN64ToScreenScale.x + 59 ); //59 in translate is an ugly hack that only work on 480x272 display//Corn
|
|
screen1.y = roundf( roundf( xy1.y ) * mN64ToScreenScale.y + mN64ToScreenTranslate.y );
|
|
}
|
|
else
|
|
{
|
|
ConvertN64ToScreen( xy0, screen0 );
|
|
ConvertN64ToScreen( xy1, screen1 );
|
|
}
|
|
|
|
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
|
|
DL_PF( " Screen: %.1f,%.1f -> %.1f,%.1f", screen0.x, screen0.y, screen1.x, screen1.y );
|
|
DL_PF( " Texture: %.1f,%.1f -> %.1f,%.1f", uv0.x, uv0.y, uv1.x, uv1.y );
|
|
#endif
|
|
const f32 depth = gRDPOtherMode.depth_source ? mPrimDepth : 0.0f;
|
|
|
|
#if 1 //1->SPRITE, 0->STRIP
|
|
DaedalusVtx * p_vertices = static_cast<DaedalusVtx *>(sceGuGetMemory(2 * sizeof(DaedalusVtx)));
|
|
|
|
p_vertices[0].Position.x = screen0.x;
|
|
p_vertices[0].Position.y = screen0.y;
|
|
p_vertices[0].Position.z = depth;
|
|
p_vertices[0].Colour = c32(0xffffffff);
|
|
p_vertices[0].Texture.x = uv0.x;
|
|
p_vertices[0].Texture.y = uv0.y;
|
|
|
|
p_vertices[1].Position.x = screen1.x;
|
|
p_vertices[1].Position.y = screen1.y;
|
|
p_vertices[1].Position.z = depth;
|
|
p_vertices[1].Colour = c32(0xffffffff);
|
|
p_vertices[1].Texture.x = uv1.x;
|
|
p_vertices[1].Texture.y = uv1.y;
|
|
|
|
RenderUsingCurrentBlendMode( p_vertices, 2, GU_SPRITES, GU_TRANSFORM_2D, gRDPOtherMode.depth_source ? false : true );
|
|
#else
|
|
// To be used with TRIANGLE_STRIP, which requires 40% less verts than TRIANGLE
|
|
// For reference for future ports and if SPRITES( which uses %60 less verts than TRIANGLE) causes issues
|
|
DaedalusVtx * p_vertices = static_cast<DaedalusVtx *>(sceGuGetMemory(4 * sizeof(DaedalusVtx)));
|
|
|
|
p_vertices[0].Position.x = screen0.x;
|
|
p_vertices[0].Position.y = screen0.y;
|
|
p_vertices[0].Position.z = depth;
|
|
p_vertices[0].Colour = c32(0xffffffff);
|
|
p_vertices[0].Texture.x = uv0.x;
|
|
p_vertices[0].Texture.y = uv0.y;
|
|
|
|
p_vertices[1].Position.x = screen1.x;
|
|
p_vertices[1].Position.y = screen0.y;
|
|
p_vertices[1].Position.z = depth;
|
|
p_vertices[1].Colour = c32(0xffffffff);
|
|
p_vertices[1].Texture.x = uv1.x;
|
|
p_vertices[1].Texture.y = uv0.y;
|
|
|
|
p_vertices[2].Position.x = screen0.x;
|
|
p_vertices[2].Position.y = screen1.y;
|
|
p_vertices[2].Position.z = depth;
|
|
p_vertices[2].Colour = c32(0xffffffff);
|
|
p_vertices[2].Texture.x = uv0.x;
|
|
p_vertices[2].Texture.y = uv1.y;
|
|
|
|
p_vertices[3].Position.x = screen1.x;
|
|
p_vertices[3].Position.y = screen1.y;
|
|
p_vertices[3].Position.z = depth;
|
|
p_vertices[3].Colour = c32(0xffffffff);
|
|
p_vertices[3].Texture.x = uv1.x;
|
|
p_vertices[3].Texture.y = uv1.y;
|
|
|
|
RenderUsingCurrentBlendMode( p_vertices, 4, GU_TRIANGLE_STRIP, GU_TRANSFORM_2D, gRDPOtherMode.depth_source ? false : true );
|
|
#endif
|
|
|
|
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
|
|
++mNumRect;
|
|
#endif
|
|
}
|
|
|
|
void RendererPSP::TexRectFlip( u32 tile_idx, const v2 & xy0, const v2 & xy1, TexCoord st0, TexCoord st1 )
|
|
{
|
|
mTnL.Flags.Fog = 0; //For now we force fog off for textrect, normally it should be fogged when depth_source is set //Corn
|
|
|
|
UpdateTileSnapshots( tile_idx );
|
|
|
|
// NB: we have to do this after UpdateTileSnapshot, as it set up mTileTopLeft etc.
|
|
PrepareTexRectUVs(&st0, &st1);
|
|
|
|
// Convert fixed point uvs back to floating point format.
|
|
// NB: would be nice to pass these as s16 ints, and use GU_TEXTURE_16BIT
|
|
v2 uv0( (float)st0.s / 32.f, (float)st0.t / 32.f );
|
|
v2 uv1( (float)st1.s / 32.f, (float)st1.t / 32.f );
|
|
|
|
v2 screen0;
|
|
v2 screen1;
|
|
// FIXME(strmnnrmn): why is VT_FULLSCREEN_HD code in TexRect() not also done here?
|
|
ConvertN64ToScreen( xy0, screen0 );
|
|
ConvertN64ToScreen( xy1, screen1 );
|
|
|
|
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
|
|
DL_PF( " Screen: %.1f,%.1f -> %.1f,%.1f", screen0.x, screen0.y, screen1.x, screen1.y );
|
|
DL_PF( " Texture: %.1f,%.1f -> %.1f,%.1f", uv0.x, uv0.y, uv1.x, uv1.y );
|
|
#endif
|
|
DaedalusVtx * p_vertices = static_cast<DaedalusVtx *>(sceGuGetMemory(4 * sizeof(DaedalusVtx)));
|
|
|
|
p_vertices[0].Position.x = screen0.x;
|
|
p_vertices[0].Position.y = screen0.y;
|
|
p_vertices[0].Position.z = 0.0f;
|
|
p_vertices[0].Colour = c32(0xffffffff);
|
|
p_vertices[0].Texture.x = uv0.x;
|
|
p_vertices[0].Texture.y = uv0.y;
|
|
|
|
p_vertices[1].Position.x = screen1.x;
|
|
p_vertices[1].Position.y = screen0.y;
|
|
p_vertices[1].Position.z = 0.0f;
|
|
p_vertices[1].Colour = c32(0xffffffff);
|
|
p_vertices[1].Texture.x = uv0.x;
|
|
p_vertices[1].Texture.y = uv1.y;
|
|
|
|
p_vertices[2].Position.x = screen0.x;
|
|
p_vertices[2].Position.y = screen1.y;
|
|
p_vertices[2].Position.z = 0.0f;
|
|
p_vertices[2].Colour = c32(0xffffffff);
|
|
p_vertices[2].Texture.x = uv1.x;
|
|
p_vertices[2].Texture.y = uv0.y;
|
|
|
|
p_vertices[3].Position.x = screen1.x;
|
|
p_vertices[3].Position.y = screen1.y;
|
|
p_vertices[3].Position.z = 0.0f;
|
|
p_vertices[3].Colour = c32(0xffffffff);
|
|
p_vertices[3].Texture.x = uv1.x;
|
|
p_vertices[3].Texture.y = uv1.y;
|
|
|
|
// FIXME(strmnnrmn): shouldn't this pass gRDPOtherMode.depth_source ? false : true for the disable_zbuffer arg, as TextRect()?
|
|
RenderUsingCurrentBlendMode( p_vertices, 4, GU_TRIANGLE_STRIP, GU_TRANSFORM_2D, true );
|
|
|
|
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
|
|
++mNumRect;
|
|
#endif
|
|
}
|
|
|
|
void RendererPSP::FillRect( const v2 & xy0, const v2 & xy1, u32 color )
|
|
{
|
|
/*
|
|
if ( (gRDPOtherMode._u64 & 0xffff0000) == 0x5f500000 ) //Used by Wave Racer
|
|
{
|
|
// this blend mode is mem*0 + mem*1, so we don't need to render it... Very odd!
|
|
DAEDALUS_ERROR(" mem*0 + mem*1 - skipped");
|
|
return;
|
|
}
|
|
*/
|
|
// This if for C&C - It might break other stuff (I'm not sure if we should allow alpha or not..)
|
|
//color |= 0xff000000;
|
|
|
|
v2 screen0;
|
|
v2 screen1;
|
|
ConvertN64ToScreen( xy0, screen0 );
|
|
ConvertN64ToScreen( xy1, screen1 );
|
|
|
|
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
|
|
DL_PF( " Screen: %.1f,%.1f -> %.1f,%.1f", screen0.x, screen0.y, screen1.x, screen1.y );
|
|
#endif
|
|
DaedalusVtx * p_vertices = static_cast<DaedalusVtx *>(sceGuGetMemory(2 * sizeof(DaedalusVtx)));
|
|
|
|
// No need for Texture.x/y as we don't do any texturing for fillrect
|
|
p_vertices[0].Position.x = screen0.x;
|
|
p_vertices[0].Position.y = screen0.y;
|
|
p_vertices[0].Position.z = 0.0f;
|
|
p_vertices[0].Colour = c32(color);
|
|
//p_vertices[0].Texture.x = 0.0f;
|
|
//p_vertices[0].Texture.y = 0.0f;
|
|
|
|
p_vertices[1].Position.x = screen1.x;
|
|
p_vertices[1].Position.y = screen1.y;
|
|
p_vertices[1].Position.z = 0.0f;
|
|
p_vertices[1].Colour = c32(color);
|
|
//p_vertices[1].Texture.x = 1.0f;
|
|
//p_vertices[1].Texture.y = 0.0f;
|
|
|
|
// FIXME(strmnnrmn): shouldn't this pass gRDPOtherMode.depth_source ? false : true for the disable_zbuffer arg, as TexRect()?
|
|
RenderUsingCurrentBlendMode( p_vertices, 2, GU_SPRITES, GU_TRANSFORM_2D, true );
|
|
|
|
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
|
|
++mNumRect;
|
|
#endif
|
|
}
|
|
|
|
void RendererPSP::Draw2DTexture(f32 x0, f32 y0, f32 x1, f32 y1,
|
|
f32 u0, f32 v0, f32 u1, f32 v1, std::shared_ptr<CNativeTexture> texture)
|
|
{
|
|
texture->InstallTexture();
|
|
DAEDALUS_PROFILE( "RendererPSP::Draw2DTexture" );
|
|
TextureVtx *p_verts = (TextureVtx*)sceGuGetMemory(4*sizeof(TextureVtx));
|
|
|
|
// Enable or Disable ZBuffer test
|
|
if ( (mTnL.Flags.Zbuffer & gRDPOtherMode.z_cmp) | gRDPOtherMode.z_upd )
|
|
{
|
|
sceGuEnable(GU_DEPTH_TEST);
|
|
}
|
|
else
|
|
{
|
|
sceGuDisable(GU_DEPTH_TEST);
|
|
}
|
|
|
|
// GL_TRUE to disable z-writes
|
|
sceGuDepthMask( gRDPOtherMode.z_upd ? GL_FALSE : GL_TRUE );
|
|
//sceGuShadeModel(GU_FLAT);
|
|
|
|
//ToDO: Set alpha/blend states according RenderUsingCurrentBlendMode?
|
|
sceGuTexFilter(GU_LINEAR, GU_LINEAR);
|
|
sceGuDisable(GU_ALPHA_TEST);
|
|
sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA);
|
|
|
|
sceGuEnable(GU_BLEND);
|
|
sceGuTexWrap(GU_CLAMP, GU_CLAMP);
|
|
|
|
// Handle large images (width > 512) with blitting, since the PSP HW can't handle
|
|
// Handling height > 512 doesn't work well? Ignore for now
|
|
if( u1 >= 512.f )
|
|
{
|
|
const std::shared_ptr<CNativeTexture> texture = mBoundTexture[0];
|
|
Draw2DTextureBlit( x0, y0, x1, y1, u0, v0, u1, v1, texture );
|
|
return;
|
|
}
|
|
|
|
//TODO: Investigate why handeling (height > 512) doesn't work?
|
|
DAEDALUS_ASSERT(v1 < 512.f, "Large textures with with height %d not supported",v1);
|
|
|
|
p_verts[0].pos.x = N64ToScreenX(x0);
|
|
p_verts[0].pos.y = N64ToScreenY(y0);
|
|
p_verts[0].pos.z = 0.0f;
|
|
p_verts[0].t0.x = u0;
|
|
p_verts[0].t0.y = v0;
|
|
|
|
p_verts[1].pos.x = N64ToScreenX(x1);
|
|
p_verts[1].pos.y = N64ToScreenY(y0);
|
|
p_verts[1].pos.z = 0.0f;
|
|
p_verts[1].t0.x = u1;
|
|
p_verts[1].t0.y = v0;
|
|
|
|
p_verts[2].pos.x = N64ToScreenX(x0);
|
|
p_verts[2].pos.y = N64ToScreenY(y1);
|
|
p_verts[2].pos.z = 0.0f;
|
|
p_verts[2].t0.x = u0;
|
|
p_verts[2].t0.y = v1;
|
|
|
|
p_verts[3].pos.x = N64ToScreenX(x1);
|
|
p_verts[3].pos.y = N64ToScreenY(y1);
|
|
p_verts[3].pos.z = 0.0f;
|
|
p_verts[3].t0.x = u1;
|
|
p_verts[3].t0.y = v1;
|
|
|
|
sceGuDrawArray( GU_TRIANGLE_STRIP, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_TRANSFORM_2D, 4, 0, p_verts );
|
|
}
|
|
|
|
void RendererPSP::Draw2DTextureR(f32 x0, f32 y0, f32 x1, f32 y1,
|
|
f32 x2, f32 y2, f32 x3, f32 y3,
|
|
f32 s, f32 t, std::shared_ptr<CNativeTexture> texture) // With Rotation
|
|
{
|
|
texture->InstallTexture();
|
|
DAEDALUS_PROFILE( "RendererPSP::Draw2DTextureR" );
|
|
TextureVtx *p_verts = (TextureVtx*)sceGuGetMemory(4*sizeof(TextureVtx));
|
|
|
|
// Enable or Disable ZBuffer test
|
|
if ( (mTnL.Flags.Zbuffer & gRDPOtherMode.z_cmp) | gRDPOtherMode.z_upd )
|
|
{
|
|
sceGuEnable(GU_DEPTH_TEST);
|
|
}
|
|
else
|
|
{
|
|
sceGuDisable(GU_DEPTH_TEST);
|
|
}
|
|
|
|
// GL_TRUE to disable z-writes
|
|
sceGuDepthMask( gRDPOtherMode.z_upd ? GL_FALSE : GL_TRUE );
|
|
//sceGuShadeModel(GU_FLAT);
|
|
|
|
//ToDO: Set alpha/blend states according RenderUsingCurrentBlendMode?
|
|
sceGuTexFilter(GU_LINEAR, GU_LINEAR);
|
|
sceGuDisable(GU_ALPHA_TEST);
|
|
sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA);
|
|
|
|
sceGuEnable(GU_BLEND);
|
|
sceGuTexWrap(GU_CLAMP, GU_CLAMP);
|
|
|
|
// Why are we not blitting large textures here?
|
|
#ifdef DAEDALUS_ENABLE_ASSERTS
|
|
if (s > 512.f || t > 512.f)
|
|
{
|
|
DAEDALUS_ERROR("Large texture isn't handled correctly %d x %d",s,t);
|
|
}
|
|
#endif
|
|
p_verts[0].pos.x = N64ToScreenX(x0);
|
|
p_verts[0].pos.y = N64ToScreenY(y0);
|
|
p_verts[0].pos.z = 0.0f;
|
|
p_verts[0].t0.x = 0.0f;
|
|
p_verts[0].t0.y = 0.0f;
|
|
|
|
p_verts[1].pos.x = N64ToScreenX(x1);
|
|
p_verts[1].pos.y = N64ToScreenY(y1);
|
|
p_verts[1].pos.z = 0.0f;
|
|
p_verts[1].t0.x = s;
|
|
p_verts[1].t0.y = 0.0f;
|
|
|
|
p_verts[2].pos.x = N64ToScreenX(x2);
|
|
p_verts[2].pos.y = N64ToScreenY(y2);
|
|
p_verts[2].pos.z = 0.0f;
|
|
p_verts[2].t0.x = s;
|
|
p_verts[2].t0.y = t;
|
|
|
|
p_verts[3].pos.x = N64ToScreenX(x3);
|
|
p_verts[3].pos.y = N64ToScreenY(y3);
|
|
p_verts[3].pos.z = 0.0f;
|
|
p_verts[3].t0.x = 0.0f;
|
|
p_verts[3].t0.y = t;
|
|
|
|
sceGuDrawArray( GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_TRANSFORM_2D, 4, 0, p_verts );
|
|
}
|
|
|
|
// The following blitting code was taken from The TriEngine.
|
|
// See http://www.assembla.com/code/openTRI for more information.
|
|
void RendererPSP::Draw2DTextureBlit(f32 x, f32 y, f32 width, f32 height,
|
|
f32 u0, f32 v0, f32 u1, f32 v1,
|
|
const std::shared_ptr<CNativeTexture> texture)
|
|
{
|
|
if (texture == nullptr)
|
|
{
|
|
DAEDALUS_ERROR("No texture in Draw2DTextureBlit");
|
|
return;
|
|
}
|
|
|
|
f32 cur_v = v0;
|
|
f32 cur_y = y;
|
|
f32 v_end = v1;
|
|
f32 y_end = height;
|
|
f32 vslice = 512.f;
|
|
f32 ystep = (height/(v1-v0) * vslice);
|
|
f32 vstep = (v1-v0) > 0 ? vslice : -vslice;
|
|
|
|
f32 x_end = width;
|
|
f32 uslice = 64.f;
|
|
//f32 ustep = (u1-u0)/width * xslice;
|
|
f32 xstep = (width/(u1-u0) * uslice);
|
|
f32 ustep = (u1-u0) > 0 ? uslice : -uslice;
|
|
|
|
const u8* data = static_cast<const u8*>(texture->GetData());
|
|
|
|
for ( ; cur_y < y_end; cur_y+=ystep, cur_v+=vstep )
|
|
{
|
|
f32 cur_u = u0;
|
|
f32 cur_x = x;
|
|
f32 u_end = u1;
|
|
|
|
f32 poly_height = ((cur_y+ystep) > y_end) ? (y_end-cur_y) : ystep;
|
|
f32 source_height = vstep;
|
|
|
|
// support negative vsteps
|
|
if ((vstep > 0) && (cur_v+vstep > v_end))
|
|
{
|
|
source_height = (v_end-cur_v);
|
|
}
|
|
else if ((vstep < 0) && (cur_v+vstep < v_end))
|
|
{
|
|
source_height = (cur_v-v_end);
|
|
}
|
|
|
|
const u8* udata = data;
|
|
// blit maximizing the use of the texture-cache
|
|
for( ; cur_x < x_end; cur_x+=xstep, cur_u+=ustep )
|
|
{
|
|
// support large images (width > 512)
|
|
if (cur_u>512.f || cur_u+ustep>512.f)
|
|
{
|
|
s32 off = (ustep>0) ? ((int)cur_u & ~31) : ((int)(cur_u+ustep) & ~31);
|
|
|
|
udata += off * GetBitsPerPixel( texture->GetFormat() );
|
|
cur_u -= off;
|
|
u_end -= off;
|
|
|
|
sceGuTexImage(0, std::min<u32>(512,texture->GetCorrectedWidth()), std::min<u32>(512,texture->GetCorrectedHeight()), texture->GetBlockWidth(), udata);
|
|
}
|
|
TextureVtx *p_verts = (TextureVtx*)sceGuGetMemory(2*sizeof(TextureVtx));
|
|
|
|
//f32 poly_width = ((cur_x+xstep) > x_end) ? (x_end-cur_x) : xstep;
|
|
f32 poly_width = xstep;
|
|
f32 source_width = ustep;
|
|
|
|
// support negative usteps
|
|
if ((ustep > 0) && (cur_u+ustep > u_end))
|
|
{
|
|
source_width = (u_end-cur_u);
|
|
}
|
|
else if ((ustep < 0) && (cur_u+ustep < u_end))
|
|
{
|
|
source_width = (cur_u-u_end);
|
|
}
|
|
|
|
p_verts[0].t0.x = cur_u;
|
|
p_verts[0].t0.y = cur_v;
|
|
p_verts[0].pos.x = N64ToScreenX(cur_x);
|
|
p_verts[0].pos.y = N64ToScreenY(cur_y);
|
|
p_verts[0].pos.z = 0;
|
|
|
|
p_verts[1].t0.x = cur_u + source_width;
|
|
p_verts[1].t0.y = cur_v + source_height;
|
|
p_verts[1].pos.x = N64ToScreenX(cur_x + poly_width);
|
|
p_verts[1].pos.y = N64ToScreenY(cur_y + poly_height);
|
|
p_verts[1].pos.z = 0;
|
|
|
|
sceGuDrawArray( GU_SPRITES, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_TRANSFORM_2D, 2, 0, p_verts );
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
|
|
void RendererPSP::SelectPlaceholderTexture( EPlaceholderTextureType type )
|
|
{
|
|
switch( type )
|
|
{
|
|
case PTT_WHITE: sceGuTexImage(0, kPlaceholderTextureWidth, kPlaceholderTextureHeight, kPlaceholderTextureWidth, gWhiteTexture); break;
|
|
case PTT_SELECTED: sceGuTexImage(0, kPlaceholderTextureWidth, kPlaceholderTextureHeight, kPlaceholderTextureWidth, gSelectedTexture); break;
|
|
case PTT_MISSING: sceGuTexImage(0, kPlaceholderTextureWidth, kPlaceholderTextureHeight, kPlaceholderTextureWidth, gPlaceholderTexture); break;
|
|
default:
|
|
DAEDALUS_ERROR( "Unhandled type" );
|
|
break;
|
|
}
|
|
}
|
|
#endif // DAEDALUS_DEBUG_DISPLAYLIST
|
|
|
|
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
|
|
// Used for Blend Explorer, or Nasty texture
|
|
bool RendererPSP::DebugBlendmode( DaedalusVtx * p_vertices, u32 num_vertices, u32 triangle_mode, u32 render_flags, u64 mux )
|
|
{
|
|
if( IsCombinerStateDisabled( mux ) )
|
|
{
|
|
if( mNastyTexture )
|
|
{
|
|
// Use the nasty placeholder texture
|
|
//
|
|
sceGuEnable(GU_TEXTURE_2D);
|
|
SelectPlaceholderTexture( PTT_SELECTED );
|
|
sceGuTexFunc(GU_TFX_REPLACE,GU_TCC_RGBA);
|
|
sceGuTexMode(GU_PSM_8888,0,0,GL_TRUE); // maxmips/a2/swizzle = 0
|
|
sceGuDrawArray( triangle_mode, render_flags, num_vertices, nullptr, p_vertices );
|
|
}
|
|
else
|
|
{
|
|
//Allow Blend Explorer
|
|
//
|
|
SBlendModeDetails details;
|
|
|
|
details.InstallTexture = true;
|
|
details.EnvColour = mEnvColour;
|
|
details.PrimColour = mPrimitiveColour;
|
|
details.ColourAdjuster.Reset();
|
|
|
|
//Insert the Blend Explorer
|
|
BLEND_MODE_MAKER
|
|
|
|
bool installed_texture = false;
|
|
|
|
if( details.InstallTexture )
|
|
{
|
|
if( mBoundTexture[0] != nullptr )
|
|
{
|
|
mBoundTexture[0]->InstallTexture();
|
|
installed_texture = true;
|
|
}
|
|
}
|
|
|
|
// If no texture was specified, or if we couldn't load it, clear it out
|
|
if( !installed_texture ) sceGuDisable( GU_TEXTURE_2D );
|
|
|
|
details.ColourAdjuster.Process( p_vertices, num_vertices );
|
|
sceGuDrawArray( triangle_mode, render_flags, num_vertices, nullptr, p_vertices );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif // DAEDALUS_DEBUG_DISPLAYLIST
|
|
|
|
|
|
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
|
|
void RendererPSP::DebugMux( const CBlendStates * states, DaedalusVtx * p_vertices, u32 num_vertices, u32 triangle_mode, u32 render_flags, u64 mux)
|
|
{
|
|
// Only dump missing_mux when we awant to search for inexact blends aka HighlightInexactBlendModes is enabled.
|
|
// Otherwise will dump lotsa of missing_mux even though is not needed since was handled correctly by auto blendmode thing - Salvy
|
|
//
|
|
if (gGlobalPreferences.HighlightInexactBlendModes && states->IsInexact())
|
|
{
|
|
if (mUnhandledCombinerStates.find( mux ) == mUnhandledCombinerStates.end())
|
|
{
|
|
std::filesystem::path filepath = setBasePath("Mux");
|
|
// filepath /= g_ROM.settings.GameName.c_str();
|
|
|
|
// Dump_GetDumpDirectory(filepath.c_str(), g_ROM.settings.GameName.c_str());
|
|
filepath /= "missing.mux";
|
|
|
|
FILE * fh = fopen(filepath.c_str(), mUnhandledCombinerStates.empty() ? "w" : "a");
|
|
if (fh != nullptr)
|
|
{
|
|
DLDebug_PrintMux( fh, mux );
|
|
fclose(fh);
|
|
}
|
|
|
|
mUnhandledCombinerStates.insert( mux );
|
|
}
|
|
|
|
sceGuEnable( GU_TEXTURE_2D );
|
|
sceGuTexMode( GU_PSM_8888, 0, 0, GL_TRUE ); // maxmips/a2/swizzle = 0
|
|
|
|
// Use the nasty placeholder texture
|
|
SelectPlaceholderTexture( PTT_MISSING );
|
|
sceGuTexFunc( GU_TFX_REPLACE, GU_TCC_RGBA );
|
|
sceGuDrawArray( triangle_mode, render_flags, num_vertices, nullptr, p_vertices );
|
|
}
|
|
}
|
|
|
|
#endif // DAEDALUS_DEBUG_DISPLAYLIST
|
|
|
|
|
|
|
|
|
|
|
|
bool CreateRenderer()
|
|
{
|
|
#ifdef DAEDALUS_ENABLE_ASSERTS
|
|
DAEDALUS_ASSERT_Q(gRenderer == nullptr);
|
|
#endif
|
|
gRendererPSP = new RendererPSP();
|
|
gRenderer = gRendererPSP;
|
|
return true;
|
|
}
|
|
void DestroyRenderer()
|
|
{
|
|
delete gRendererPSP;
|
|
gRendererPSP = nullptr;
|
|
gRenderer = nullptr;
|
|
}
|