diff --git a/trinity/CMakeLists.txt b/trinity/CMakeLists.txt index cbf221aef..1c92eedee 100644 --- a/trinity/CMakeLists.txt +++ b/trinity/CMakeLists.txt @@ -1346,6 +1346,9 @@ set(_SOURCES Sprite2d/Tr2Sprite2dContainer_Blue.cpp Sprite2d/Tr2Sprite2dDisplayList.cpp Sprite2d/Tr2Sprite2dDisplayList.h + Sprite2d/Tr2Sprite2dEffect.cpp + Sprite2d/Tr2Sprite2dEffect.h + Sprite2d/Tr2Sprite2dEffect_Blue.cpp Sprite2d/Tr2Sprite2dFrame.cpp Sprite2d/Tr2Sprite2dFrame.h Sprite2d/Tr2Sprite2dFrame_Blue.cpp diff --git a/trinity/Sprite2d/Tr2Sprite2dDisplayList.cpp b/trinity/Sprite2d/Tr2Sprite2dDisplayList.cpp index 013971fd6..5cb5d1346 100644 --- a/trinity/Sprite2d/Tr2Sprite2dDisplayList.cpp +++ b/trinity/Sprite2d/Tr2Sprite2dDisplayList.cpp @@ -66,8 +66,11 @@ void Tr2Sprite2dDisplayList::Entry::SubmitGeometry( Tr2RenderContext& renderCont transposedMatrixes[i] = Transpose( transformArray[i] ); } - renderContext.SetSrv( PIXEL_SHADER, textureRegisters[0], ( texture0 && texture0->GetTexture() ) ? *texture0->GetTexture() : Tr2TextureAL(), colorSpace ); - renderContext.SetSrv( PIXEL_SHADER, textureRegisters[1], ( texture1 && texture1->GetTexture() ) ? *texture1->GetTexture() : Tr2TextureAL(), colorSpace ); + if( bindTextures ) + { + renderContext.SetSrv( PIXEL_SHADER, textureRegisters[0], ( texture0 && texture0->GetTexture() ) ? *texture0->GetTexture() : Tr2TextureAL(), colorSpace ); + renderContext.SetSrv( PIXEL_SHADER, textureRegisters[1], ( texture1 && texture1->GetTexture() ) ? *texture1->GetTexture() : Tr2TextureAL(), colorSpace ); + } FillAndSetConstants( *m_uiTransformsCb, diff --git a/trinity/Sprite2d/Tr2Sprite2dDisplayList.h b/trinity/Sprite2d/Tr2Sprite2dDisplayList.h index 6e08e587b..37cbe00cf 100644 --- a/trinity/Sprite2d/Tr2Sprite2dDisplayList.h +++ b/trinity/Sprite2d/Tr2Sprite2dDisplayList.h @@ -33,6 +33,8 @@ struct Tr2Sprite2dDisplayList : public Tr2DeviceResource Tr2AtlasTexturePtr texture0; Tr2AtlasTexturePtr texture1; uint32_t textureRegisters[2]; + // Whether to bind texture0 and texture1. False only for an override effect. + bool bindTextures; Tr2RenderContextEnum::ColorSpace colorSpace; Tr2EffectPtr effect; //hardcoded due to need for CB slot//Tr2EffectConstant transformsHandle; diff --git a/trinity/Sprite2d/Tr2Sprite2dEffect.cpp b/trinity/Sprite2d/Tr2Sprite2dEffect.cpp new file mode 100644 index 000000000..37015b372 --- /dev/null +++ b/trinity/Sprite2d/Tr2Sprite2dEffect.cpp @@ -0,0 +1,83 @@ +// Copyright © 2026 CCP ehf. + +#include "StdAfx.h" +#include "Tr2Sprite2dEffect.h" +#include "Tr2Sprite2dScene.h" +#include "Tr2Sprite2dPickingMask.h" +#include "Shader/Tr2Effect.h" + +static unsigned short s_quadIndices[6] = { 0, 1, 3, 3, 1, 2 }; + +Tr2Sprite2dEffect::Tr2Sprite2dEffect( IRoot* lockobj /*= NULL */ ) : + m_color( 1.0f, 1.0f, 1.0f, 1.0f ), + m_glowBrightness( 1.0f ), + m_target( Tr2SpriteTarget::COLOR ) +{ +} + +void Tr2Sprite2dEffect::GatherSprites( Tr2Sprite2dScene* renderer ) +{ + TRINITY_STATS_ZONE( __FUNCTION__ ); + + if( !m_display || !m_effect ) + { + return; + } + + static const Vector2 s_corners[4] = { + Vector2( 0.0f, 0.0f ), + Vector2( 1.0f, 0.0f ), + Vector2( 1.0f, 1.0f ), + Vector2( 0.0f, 1.0f ) + }; + + // Every field is set here, from the sprite or as a default Tr2SpriteObject + // would set it, so the effect gets the same vertices whatever the sprites + // before it left + renderer->SetDepth( 0.0f ); + + Tr2Sprite2dD3DVertex vertices[4]; + for( int i = 0; i < 4; ++i ) + { + Tr2Sprite2dD3DVertex& vertex = vertices[i]; + vertex.position = Vector3( m_translation.x + s_corners[i].x * m_displayWidth, m_translation.y + s_corners[i].y * m_displayHeight, 0.0f ); + vertex.color = m_color; + vertex.texCoord[0] = s_corners[i]; + vertex.texCoord[1] = Vector2( m_displayWidth, m_displayHeight ); + vertex.glowBrightness = m_glowBrightness; + vertex.transformIndex = 0; + vertex.blendMode = PackBlendMode( TR2_SBM_NONE, m_target ); + vertex.spriteEffect = TR2_SFX_FILL; + vertex.tileMode = S2D_TS_NONE; + vertex.outlineColor = Color( 0.0f, 0.0f, 0.0f, 1.0f ); + vertex.outlineThreshold = 0.0f; + } + + renderer->RenderTriangleVerts( *m_effect, vertices, 4, s_quadIndices, 6 ); +} + +ITr2SpriteObject* Tr2Sprite2dEffect::PickPoint( float x, float y, Tr2Sprite2dScene* renderer ) +{ + if( !m_display ) + { + return NULL; + } + + if( m_pickState == TR2_SPS_ON ) + { + if( renderer->IsInside( Vector2( x, y ), m_translation, m_displayWidth, m_displayHeight, 0.0f ) ) + { + if( !m_pickingMask || m_pickingMask->SampleMask( renderer->InverseTransformPoint( Vector2( x, y ) ), m_translation, m_displayWidth, m_displayHeight ) ) + { + return this; + } + } + } + + return NULL; +} + +unsigned int Tr2Sprite2dEffect::GetVertexCount() +{ + return ( m_display && m_effect ) ? 4 : 0; +} diff --git a/trinity/Sprite2d/Tr2Sprite2dEffect.h b/trinity/Sprite2d/Tr2Sprite2dEffect.h new file mode 100644 index 000000000..85608fdd2 --- /dev/null +++ b/trinity/Sprite2d/Tr2Sprite2dEffect.h @@ -0,0 +1,46 @@ +// Copyright © 2026 CCP ehf. + +#pragma once +#ifndef Tr2Sprite2dEffect_h +#define Tr2Sprite2dEffect_h + +#include "ITr2SpriteObject.h" +#include "Tr2SpriteObject.h" + +BLUE_DECLARE( Tr2Effect ); +BLUE_DECLARE( Tr2Sprite2dEffect ); + +// A leaf sprite that draws one effect over its rect, in order with the other +// sprites, as a draw call of its own. The effect draws the sprite's quad in +// place of the scene's own shaders, so it gets the same vertices and +// constants (UIVertexInput, g_uiTransforms and UIViewportSize in +// Carbon/UI/UI.fxh), clips and transforms them as they do, and should write +// glow to SV_Target1 like them. Texcoord 0 runs 0 to 1 over the quad, texcoord +// 1 is the quad's width and height, and the vertex color is the sprite's color +// with its ancestors' opacity multiplied into alpha. The glow brightness and +// sprite target are the sprite's own, and the other vertex fields hold a +// default sprite's values, whatever was drawn before. The effect gets none of +// the scene's textures. +class Tr2Sprite2dEffect : public Tr2SpriteObjectBase +{ +public: + EXPOSE_TO_BLUE(); + + Tr2Sprite2dEffect( IRoot* lockobj = NULL ); + + ////////////////////////////////////////////////////////////////////////// + // ITr2SpriteObject + unsigned int GetVertexCount(); + void GatherSprites( Tr2Sprite2dScene* renderer ) override; + ITr2SpriteObject* PickPoint( float x, float y, Tr2Sprite2dScene* renderer ) override; + +private: + Tr2EffectPtr m_effect; + Color m_color; + float m_glowBrightness; + Tr2SpriteTarget m_target; +}; + +TYPEDEF_BLUECLASS( Tr2Sprite2dEffect ); + +#endif diff --git a/trinity/Sprite2d/Tr2Sprite2dEffect_Blue.cpp b/trinity/Sprite2d/Tr2Sprite2dEffect_Blue.cpp new file mode 100644 index 000000000..01bf5b1b2 --- /dev/null +++ b/trinity/Sprite2d/Tr2Sprite2dEffect_Blue.cpp @@ -0,0 +1,41 @@ +// Copyright © 2026 CCP ehf. + +#include "StdAfx.h" +#include "Tr2Sprite2dEffect.h" + +BLUE_DEFINE( Tr2Sprite2dEffect ); + +const Be::ClassInfo* Tr2Sprite2dEffect::ExposeToBlue() +{ + EXPOSURE_BEGIN( Tr2Sprite2dEffect, "" ) + MAP_INTERFACE( ITr2SpriteObject ) + MAP_INTERFACE( Tr2Sprite2dEffect ) + + MAP_ATTRIBUTE( + "effect", + m_effect, + "The effect drawn over the sprite's quad in place of the scene's shaders, or None. It gets the\n" + "scene's vertices and constants (see Carbon/UI/UI.fxh), with texcoord 0 running 0 to 1 over the quad,\n" + "texcoord 1 holding the quad's width and height, and the sprite's color in the vertex color.", + Be::READWRITE | Be::NOTIFY ) + + MAP_ATTRIBUTE( + "color", + m_color, + "Color of the sprite, passed to the effect as the vertex color. Its alpha is multiplied with its ancestors' opacity.", + Be::READWRITE | Be::NOTIFY ) + + MAP_ATTRIBUTE( + "glowBrightness", + m_glowBrightness, + "Glow brightness passed to the effect in the vertices. See spriteTarget.", + Be::READWRITE | Be::NOTIFY ) + + MAP_ATTRIBUTE_WITH_CHOOSER( + "spriteTarget", + m_target, + "Render target passed to the effect in the vertices: either color or glow target", + Be::READWRITE | Be::ENUM | Be::NOTIFY, + Tr2SpriteTargetChooser ) + EXPOSURE_CHAINTO( Tr2SpriteObjectBase ) +} diff --git a/trinity/Sprite2d/Tr2Sprite2dScene.cpp b/trinity/Sprite2d/Tr2Sprite2dScene.cpp index f914ab0a6..7ba4b4dd8 100644 --- a/trinity/Sprite2d/Tr2Sprite2dScene.cpp +++ b/trinity/Sprite2d/Tr2Sprite2dScene.cpp @@ -117,6 +117,7 @@ Tr2Sprite2dScene::Tr2Sprite2dScene( IRoot* lockobj ) : m_maxSpriteCount( 1024 ), m_tileMode( S2D_TS_NONE ), m_drawCallStartIndex( 0 ), + m_effectOverride( nullptr ), m_transformsHandle(), m_useLinearColorSpace( false ), m_isGammaCorrectingText( true ), @@ -1041,6 +1042,17 @@ void Tr2Sprite2dScene::RenderTriangleVerts( Tr2Sprite2dD3DVertex* verticesSrc, u CopyIndicesWithOffset( indices, indexCount, vertexOffset ); } +void Tr2Sprite2dScene::RenderTriangleVerts( Tr2Effect& effect, Tr2Sprite2dD3DVertex* verticesSrc, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount ) +{ + // Draw what's already batched with the scene's shader, then batch the + // vertices on their own and draw them with the effect + IssueDrawCall(); + m_effectOverride = &effect; + RenderTriangleVerts( verticesSrc, vertexCount, indices, indexCount ); + IssueDrawCall(); + m_effectOverride = nullptr; +} + void Tr2Sprite2dScene::RenderTriangleVerts( Tr2BufferAL& verticesSrc, unsigned int vertexCount, Tr2BufferAL& indices, unsigned int indexCount ) { CCP_ASSERT( !m_captureDisplayList ); @@ -1103,6 +1115,7 @@ void Tr2Sprite2dScene::IssueDrawCall() entry.texture0 = m_texture[0]; entry.texture1 = m_texture[1]; entry.effect = m_effect; + entry.bindTextures = !m_effectOverride; m_texelSizeVar[0].GetValue( entry.texelSize0 ); m_texelSizeVar[1].GetValue( entry.texelSize1 ); @@ -1177,22 +1190,26 @@ void Tr2Sprite2dScene::SubmitGeometry( Tr2RenderContext& renderContext ) transposedMatrixes[i] = Transpose( m_transformArray[i] ); } - for( uint32_t i = 0; i < 2; ++i ) + // An override effect gets none of the scene's textures + if( !m_effectOverride ) { - Tr2TextureAL* texAL = nullptr; - if( m_texture[i] ) + for( uint32_t i = 0; i < 2; ++i ) { - texAL = m_texture[i]->GetTexture(); - if( !texAL ) + Tr2TextureAL* texAL = nullptr; + if( m_texture[i] ) { - if( m_texture[i]->GetRenderTarget() ) + texAL = m_texture[i]->GetTexture(); + if( !texAL ) { - texAL = m_texture[i]->GetRenderTarget(); + if( m_texture[i]->GetRenderTarget() ) + { + texAL = m_texture[i]->GetRenderTarget(); + } } } + auto colorSpace = m_useLinearColorSpace ? Tr2RenderContextEnum::COLOR_SPACE_SRGB : Tr2RenderContextEnum::COLOR_SPACE_LINEAR; + renderContext.SetSrv( PIXEL_SHADER, m_textureRegisters[i], texAL ? *texAL : Tr2TextureAL(), colorSpace ); } - auto colorSpace = m_useLinearColorSpace ? Tr2RenderContextEnum::COLOR_SPACE_SRGB : Tr2RenderContextEnum::COLOR_SPACE_LINEAR; - renderContext.SetSrv( PIXEL_SHADER, m_textureRegisters[i], texAL ? *texAL : Tr2TextureAL(), colorSpace ); } bool result = FillAndSetConstants( @@ -1718,8 +1735,17 @@ void Tr2Sprite2dScene::SetOutlineThreshold( float outlineThreshold ) m_outlineThreshold = outlineThreshold; } +// Selects the effect for the batched vertices and returns whether it can draw +// them. An override effect always can, even before its shader loads, so that +// a display list captured meanwhile still records its draw call. bool Tr2Sprite2dScene::SelectEffect() { + if( m_effectOverride ) + { + m_effect = m_effectOverride; + return true; + } + Tr2Effect* newEffect; newEffect = m_is2dRender ? m_uberShader2d : m_uberShader3d; @@ -1791,6 +1817,7 @@ void Tr2Sprite2dScene::RunJob( TriRenderJob* job ) entry.primitiveCount = 0; entry.texture0 = nullptr; entry.texture1 = nullptr; + entry.bindTextures = true; entry.texelSize0 = Vector4( 0, 0, 0, 0 ); entry.texelSize1 = Vector4( 0, 0, 0, 0 ); diff --git a/trinity/Sprite2d/Tr2Sprite2dScene.h b/trinity/Sprite2d/Tr2Sprite2dScene.h index d68e0ac98..0f8f08f07 100644 --- a/trinity/Sprite2d/Tr2Sprite2dScene.h +++ b/trinity/Sprite2d/Tr2Sprite2dScene.h @@ -117,6 +117,15 @@ class Tr2Sprite2dScene : public ITr2Scene, unsigned short* indices, unsigned int indexCount ); + // Draws the vertices as a draw call of their own, with the given effect in + // place of the scene's shader and none of the scene's textures. + void RenderTriangleVerts( + Tr2Effect& effect, + Tr2Sprite2dD3DVertex* verticesSrc, + unsigned int vertexCount, + unsigned short* indices, + unsigned int indexCount ); + void RenderTriangleVerts( Tr2BufferAL& verticesSrc, unsigned int vertexCount, @@ -202,6 +211,10 @@ class Tr2Sprite2dScene : public ITr2Scene, Tr2EffectPtr m_effect; uint32_t m_textureRegisters[2]; + // Effect to draw with instead of the scene's shader. Only set inside + // RenderTriangleVerts with an effect. + Tr2Effect* m_effectOverride; + Tr2EffectConstant m_transformsHandle; // The desired visual effect