diff --git a/trinity/CMakeLists.txt b/trinity/CMakeLists.txt index dc0e9c6e0..f290f0468 100644 --- a/trinity/CMakeLists.txt +++ b/trinity/CMakeLists.txt @@ -1411,6 +1411,7 @@ set(_SOURCES autoversion.h ContinueOnMainThread.cpp ContinueOnMainThread.h + EnumFilter.h EveSprite2dBracket.cpp EveSprite2dBracket.h EveSprite2dBracket_Blue.cpp diff --git a/trinity/EnumFilter.h b/trinity/EnumFilter.h new file mode 100644 index 000000000..d2f4f2dc6 --- /dev/null +++ b/trinity/EnumFilter.h @@ -0,0 +1,59 @@ +// Copyright © 2026 CCP ehf. + +#pragma once + +#include + + +/** + * @brief A filter for enum values stored as bits. + * @tparam Enum The enum type to filter. + * @tparam StorageType The underlying storage type for the filter bits. Needs to be large enough to hold all enum values. Defaults to uint8_t (up to 8 enum values). + */ +template +class EnumFilter +{ +public: + EnumFilter() = default; + EnumFilter( const EnumFilter& other ) = default; + EnumFilter( Enum value ) : m_filter( StorageType( StorageType( 1 ) << StorageType( value ) ) ) + { + CCP_ASSERT_M( uint64_t( value ) < 8 * sizeof( StorageType ), "Enum value out of range for the chosen storage type" ); + } + + EnumFilter& operator|=( Enum value ) + { + CCP_ASSERT_M( uint64_t( value ) < 8 * sizeof( StorageType ), "Enum value out of range for the chosen storage type" ); + m_filter |= StorageType( StorageType( 1 ) << StorageType( value ) ); + return *this; + } + + EnumFilter operator|( const EnumFilter& other ) const + { + return EnumFilter( m_filter | other.m_filter ); + } + + EnumFilter operator|( Enum value ) const + { + EnumFilter result( *this ); + result |= value; + return result; + } + + bool HasBit( Enum value ) const + { + CCP_ASSERT_M( uint64_t( value ) < 8 * sizeof( StorageType ), "Enum value out of range for the chosen storage type" ); + return ( m_filter & StorageType( StorageType( 1 ) << StorageType( value ) ) ) != 0; + } + + /// @brief Creates an EnumFilter with all bits set. + static EnumFilter AllBits() + { + EnumFilter filter; + filter.m_filter = ~StorageType( 0 ); + return filter; + } + + // This variable should be private, but we need to access it for MAP_ATTRIBUTE macros + StorageType m_filter = 0; +}; diff --git a/trinity/Eve/EveSpaceScene.cpp b/trinity/Eve/EveSpaceScene.cpp index 8e109eb1a..5020f6c26 100644 --- a/trinity/Eve/EveSpaceScene.cpp +++ b/trinity/Eve/EveSpaceScene.cpp @@ -1372,18 +1372,6 @@ void EveSpaceScene::BeginRender( bool enableDistortion, Tr2RenderContext& render renderContext.m_esm.BeginManagedRendering(); renderContext.m_esm.ApplyStandardStates( Tr2EffectStateManager::RM_OPAQUE ); - if( g_eveSpaceSceneDynamicLighting ) - { - if( auto lightManager = Tr2LightManager::GetOrCreateInstance( "res:/graphics/effect/managed/space/system/computelightlists.fx" ) ) - { - lightManager->SetVariableStore(); - } - } - else - { - Tr2LightManager::DeleteInstance(); - } - GatherBatches( enableDistortion, renderContext ); if( m_shadowQuality == ShadowQuality::SHADOW_RAYTRACED && m_enableShadows ) diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.cpp b/trinity/Eve/EveSpaceSceneRenderDriver.cpp index c72520ae5..7e894377b 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver.cpp @@ -17,6 +17,8 @@ CCP_STATS_DECLARE( updateDynamicLightLists, "Trinity/EveSpaceScene/updateDynamicLights", true, CST_TIME, "Time took to gather dynamic lights for EveSpaceScene" ); +extern bool g_eveSpaceSceneDynamicLighting; + namespace { @@ -486,6 +488,20 @@ void EveSpaceSceneRenderDriver::Execute( const Span& destina { TimeSection beginRenderSection( m_timers.beginRender, "BeginRender", rootTimer, renderContext ); + + if( g_eveSpaceSceneDynamicLighting ) + { + if( auto lightManager = Tr2LightManager::GetOrCreateInstance( "res:/graphics/effect/managed/space/system/computelightlists.fx" ) ) + { + lightManager->SetVariableStore(); + lightManager->SetLightingQuality( m_settings.lightingQuality ); + } + } + else + { + Tr2LightManager::DeleteInstance(); + } + m_scene->BeginRender( m_settings.enableDistortion, renderContext ); } { diff --git a/trinity/Eve/EveSpaceSceneRenderDriver.h b/trinity/Eve/EveSpaceSceneRenderDriver.h index 78ad4ef0c..722c242f0 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver.h +++ b/trinity/Eve/EveSpaceSceneRenderDriver.h @@ -58,6 +58,7 @@ BLUE_CLASS( EveSpaceSceneRenderDriver ) : AmbientOcclusionQuality aoQuality = AmbientOcclusionQuality::High; PostProcess::Quality postProcessingQuality = PostProcess::Quality::HIGH; Tr2VolumerticQuality volumetricQuality = Tr2VolumerticQuality::High; + LightingQuality lightingQuality = LightingQuality::HIGH; Color clearColor = Color( 0.0f, 0.0f, 0.0f, 1.0f ); diff --git a/trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp b/trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp index 312dc625f..802e44197 100644 --- a/trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp +++ b/trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp @@ -29,6 +29,12 @@ Be::VarChooser ShadowQualityChooser[] = { { "Raytraced", BeCast( ShadowQuality::SHADOW_RAYTRACED ), "" }, { 0 } }; +const Be::VarChooser LightingQualityChooser[] = { + { "Low", BeCast( LightingQuality::LOW ), "" }, + { "Medium", BeCast( LightingQuality::MEDIUM ), "" }, + { "High", BeCast( LightingQuality::HIGH ), "" }, + { 0 } +}; const Be::VarChooser TriRMChooser[] = { // Name Value Docstring @@ -52,6 +58,7 @@ extern Be::VarChooser PostProcessQualityChooser[]; BLUE_REGISTER_ENUM_EX( "EveSpaceSceneRenderDriverAntiAliasingQuality", EveSpaceSceneRenderDriver::AntiAliasingQuality, AntiAliasingQualityChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); BLUE_REGISTER_ENUM_EX( "EveSpaceSceneRenderDriverAmbientOcclusionQuality", EveSpaceSceneRenderDriver::AmbientOcclusionQuality, AmbientOcclusionQualityChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); BLUE_REGISTER_ENUM_EX( "ShadowQuality", ShadowQuality, ShadowQualityChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); +BLUE_REGISTER_ENUM_EX( "LightingQuality", LightingQuality, LightingQualityChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); @@ -152,6 +159,13 @@ const Be::ClassInfo* EveSpaceSceneRenderDriver::ExposeToBlue() Be::READWRITE | Be::ENUM, ShadowQualityChooser ) + MAP_ATTRIBUTE_WITH_CHOOSER( + "lightingQuality", + m_settings.lightingQuality, + "Lighting quality setting. One of trinity.LightingQuality enum", + Be::READWRITE | Be::ENUM, + LightingQualityChooser ) + MAP_ATTRIBUTE_WITH_CHOOSER( "antiAliasingQuality", m_settings.antiAliasingQuality, diff --git a/trinity/Eve/SpaceObject/Children/SmartLightSets/EveSmartLightPointLight.cpp b/trinity/Eve/SpaceObject/Children/SmartLightSets/EveSmartLightPointLight.cpp index 32b85c910..18e0d983c 100644 --- a/trinity/Eve/SpaceObject/Children/SmartLightSets/EveSmartLightPointLight.cpp +++ b/trinity/Eve/SpaceObject/Children/SmartLightSets/EveSmartLightPointLight.cpp @@ -5,11 +5,14 @@ #include "Resources/Tr2LightProfileRes.h" #include "TriMath.h" +extern bool g_scaleLightBrightnessByRadiusDefault; + EveSmartLightPointLight::EveSmartLightPointLight( IRoot* lockobj ) : m_staticOffsetTranslation( 0.f, 0.f, 0.f ), m_staticOffsetRotation( 0.f, 0.f, 0.f, 1.f ), m_activationStrength( 1.f ), - m_display( true ) + m_display( true ), + m_scaleBrightness( g_scaleLightBrightnessByRadiusDefault ) { m_lightGroupData = LightData(); m_lightType = Tr2Light::POINT_LIGHT; @@ -92,7 +95,7 @@ void EveSmartLightPointLight::GetLights( Tr2LightManager& lightManager ) const pointLightData.radius = m_lightGroupData.radius * scaling * perLightScaling; pointLightData.innerRadius = Float_16( m_lightGroupData.innerRadius * scaling * perLightScaling ); - pointLightData.flags = m_lightGroupData.flags | ( profileIndex << 4 ); + pointLightData.flags = Tr2LightManager::PackFlags( m_lightGroupData.flags, profileIndex ); Quaternion rotation = placements[index].initialRotation * placements[index].additionalRotation; if( m_staticOffsetTranslation != Vector3( 0.f, 0.f, 0.f ) ) @@ -126,7 +129,7 @@ void EveSmartLightPointLight::GetLights( Tr2LightManager& lightManager ) const pointLightData.innerAngle = Float_16( cos( TRI_2PI * m_lightGroupData.innerAngle / 360.0f ) ); } - lightManager.AddLight( pointLightData ); + lightManager.AddLight( pointLightData, m_scaleBrightness ); } } diff --git a/trinity/Eve/SpaceObject/Children/SmartLightSets/EveSmartLightPointLight.h b/trinity/Eve/SpaceObject/Children/SmartLightSets/EveSmartLightPointLight.h index 0d85bb2f3..0451b1cc4 100644 --- a/trinity/Eve/SpaceObject/Children/SmartLightSets/EveSmartLightPointLight.h +++ b/trinity/Eve/SpaceObject/Children/SmartLightSets/EveSmartLightPointLight.h @@ -38,6 +38,7 @@ BLUE_CLASS( EveSmartLightPointLight ) : protected: bool m_display; + bool m_scaleBrightness; LightData m_lightGroupData; Tr2Light::LIGHT_TYPE m_lightType; Vector3 m_staticOffsetTranslation; diff --git a/trinity/Eve/SpaceObject/Children/SmartLightSets/EveSmartLightPointLight_Blue.cpp b/trinity/Eve/SpaceObject/Children/SmartLightSets/EveSmartLightPointLight_Blue.cpp index aa0eac4ba..637ad772b 100644 --- a/trinity/Eve/SpaceObject/Children/SmartLightSets/EveSmartLightPointLight_Blue.cpp +++ b/trinity/Eve/SpaceObject/Children/SmartLightSets/EveSmartLightPointLight_Blue.cpp @@ -34,5 +34,7 @@ const Be::ClassInfo* EveSmartLightPointLight::ExposeToBlue() MAP_ATTRIBUTE( "staticOffsetTranslation", m_staticOffsetTranslation, "static per instance offset in local space before placement \n:jessica-group: StaticOffsetFromDistribution", Be::READWRITE | Be::PERSIST ) MAP_ATTRIBUTE( "staticOffsetRotation", m_staticOffsetRotation, "static per instance rotation in local space before placement \n:jessica-group: StaticOffsetFromDistribution", Be::READWRITE | Be::PERSIST ) + MAP_ATTRIBUTE( "scaleBrightness", m_scaleBrightness, "Scale light brightness by its radius", Be::READWRITE | Be::PERSIST ) + EXPOSURE_CHAINTO( EveSmartLightBaseGroup ) } diff --git a/trinity/Lights/Tr2FactionLight.cpp b/trinity/Lights/Tr2FactionLight.cpp index 0386e7d5e..1dd66999f 100644 --- a/trinity/Lights/Tr2FactionLight.cpp +++ b/trinity/Lights/Tr2FactionLight.cpp @@ -80,8 +80,10 @@ void Tr2FactionLight::RenderDebugInfo( ITr2DebugRenderer2& renderer, const Matri float outerAngle = TRI_2PI * m_lightData.outerAngle / 360.f; float innerAngle = TRI_2PI * m_lightData.innerAngle / 360.f; - renderer.DrawCone( this, lightMatrix, m_lightData.radius, outerAngle, 15, 15, Tr2DebugRenderer::Solid, Tr2DebugColor( baseColor + colorMod * 2.0f, baseColor ) ); - renderer.DrawCone( this, lightMatrix, m_lightData.innerRadius, innerAngle, 15, 15, Tr2DebugRenderer::Solid, Tr2DebugColor( baseColor + colorMod * 3.0f, baseColor + colorMod ) ); + renderer.DrawCone( this, lightMatrix, m_lightData.radius, outerAngle, 15, 15, Tr2DebugRenderer::Wireframe, Tr2DebugColor( baseColor + colorMod * 2.0f, baseColor ) ); + renderer.DrawCone( this, lightMatrix, m_lightData.radius, outerAngle, 15, 15, Tr2DebugRenderer::Solid, Color( 0, 0, 0, 0 ) ); + renderer.DrawCone( this, lightMatrix, m_lightData.innerRadius, innerAngle, 15, 15, Tr2DebugRenderer::Wireframe, Tr2DebugColor( baseColor + colorMod * 3.0f, baseColor + colorMod ) ); + renderer.DrawText( TRI_DBG_FONT_SMALL, lightMatrix.GetTranslation(), baseColor, "%s", m_name.empty() ? "Tr2FactionLight" : m_name.c_str() ); } else { @@ -96,7 +98,9 @@ void Tr2FactionLight::RenderDebugInfo( ITr2DebugRenderer2& renderer, const Matri } lightMatrix *= worldMatrix; - renderer.DrawSphere( this, lightMatrix, m_lightData.position, m_lightData.radius, 10, Tr2DebugRenderer::Solid, Tr2DebugColor( selectedColor, baseColor ) ); - renderer.DrawSphere( this, lightMatrix, m_lightData.position, m_lightData.innerRadius, 10, Tr2DebugRenderer::Solid, Tr2DebugColor( selectedColor, baseColor ) ); + renderer.DrawSphere( this, lightMatrix, m_lightData.position, m_lightData.radius, 10, Tr2DebugRenderer::Wireframe, Tr2DebugColor( selectedColor, baseColor ) ); + renderer.DrawSphere( this, lightMatrix, m_lightData.position, m_lightData.radius, 10, Tr2DebugRenderer::Solid, Color( 0, 0, 0, 0 ) ); + renderer.DrawSphere( this, lightMatrix, m_lightData.position, m_lightData.innerRadius, 10, Tr2DebugRenderer::Wireframe, Tr2DebugColor( selectedColor, baseColor ) ); + renderer.DrawText( TRI_DBG_FONT_SMALL, TransformCoord( m_lightData.position, lightMatrix ), baseColor, "%s", m_name.empty() ? "Tr2FactionLight" : m_name.c_str() ); } } diff --git a/trinity/Lights/Tr2FactionLight_Blue.cpp b/trinity/Lights/Tr2FactionLight_Blue.cpp index fe3dc9c56..c666e10de 100644 --- a/trinity/Lights/Tr2FactionLight_Blue.cpp +++ b/trinity/Lights/Tr2FactionLight_Blue.cpp @@ -37,9 +37,12 @@ const Be::ClassInfo* Tr2FactionLight::ExposeToBlue() MAP_ATTRIBUTE( "noiseFrequency", m_lightData.noiseFrequency, "Brightness noise frequency\n:jessica-group: Noise", Be::READWRITE | Be::PERSIST ) MAP_ATTRIBUTE( "noiseOctaves", m_lightData.noiseOctaves, "Brightness turbulence octaves\n:jessica-group: Noise", Be::READWRITE | Be::PERSIST ) - MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY | Be::ENUM, PerLightShadowSettingChooser ); + MAP_ATTRIBUTE_WITH_CHOOSER( "lightingQuality", m_lightData.lightingQuality.m_filter, "Filter for lighting quality settings. Controls whether light is active with a certain lighting quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, LightingQualityFilterChooser ); + MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); MAP_ATTRIBUTE( "isVolumetric", m_lightData.isVolumetric, "Volumetric lights affect participating media such as fog.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) + MAP_ATTRIBUTE( "scaleBrightness", m_scaleBrightness, "Scale light brightness by its radius", Be::READWRITE | Be::PERSIST ) + MAP_ATTRIBUTE( "lightProfilePath", m_lightProfilePath, diff --git a/trinity/Lights/Tr2Light.cpp b/trinity/Lights/Tr2Light.cpp index 20214c932..1c2efed54 100644 --- a/trinity/Lights/Tr2Light.cpp +++ b/trinity/Lights/Tr2Light.cpp @@ -4,6 +4,10 @@ #include "Tr2Light.h" #include "Include/TriMath.h" #include "Resources/Tr2LightProfileRes.h" +#include "../TriSettingsRegistrar.h" + +bool g_scaleLightBrightnessByRadiusDefault = true; +TRI_REGISTER_SETTING( "scaleLightBrightnessByRadiusDefault", g_scaleLightBrightnessByRadiusDefault ); LightData::LightData() : @@ -18,11 +22,12 @@ LightData::LightData() : rotation( 0.0f, 0.0f, 0.0f, 1.0f ), innerAngle( 0.0f ), outerAngle( 0.0f ), + falloff( uint8_t( LightFalloffType::INVERSE ) ), + lightingQuality( EnumFilter::AllBits() ), texturePath( L"" ), boneIndex( -1 ), flags( Tr2LightManager::FLAG_DEFAULT ), startTime( BeOS->GetCurrentFrameTime() ), - castsShadows( PerLightShadowSetting::DISABLED ), isVolumetric( false ) { } @@ -48,8 +53,7 @@ Tr2LightManager::PerLightData LightData::AsPerPointLightData( CXMMATRIX transfor data.color = ( Vector4( color ) * composedBrightness ).GetXYZ(); data.radius = radius * features.parentScale; data.innerRadius = Float_16( innerRadius * features.parentScale ); - int16_t profile = features.profileIndex; - data.flags = flags | ( profile << 4 ); + data.flags = Tr2LightManager::PackFlags( flags, features.profileIndex ); data.position = Vector3( XMVector3TransformCoord( position, transform ) ); Matrix lightRotation = RotationMatrix( rotation ) * transform; @@ -59,11 +63,19 @@ Tr2LightManager::PerLightData LightData::AsPerPointLightData( CXMMATRIX transfor data.innerAngle = Float_16( 0.0f ); data.projectionPlaneDistance = Float_16( 1.f / tan( TRI_2PI * 45.f / 360.0f ) ); - if( castsShadows == PerLightShadowSetting::ALWAYS_ENABLED || ( castsShadows == PerLightShadowSetting::ENABLED_ONLY_ON_HIGH_QUALITY && shadowQuality == ShadowQuality::SHADOW_HIGH ) || ( castsShadows == PerLightShadowSetting::ENABLED_ONLY_ON_HIGH_QUALITY && shadowQuality == ShadowQuality::SHADOW_RAYTRACED ) ) + if( castsShadows.HasBit( shadowQuality ) ) { data.flags |= Tr2LightManager::FLAG_CASTS_SHADOWS; } data.flags |= isVolumetric ? Tr2LightManager::FLAG_IS_VOLUMETRIC : 0; + if( falloff == uint8_t( LightFalloffType::INVERSE_SQUARE ) ) + { + data.flags |= Tr2LightManager::FLAG_FALLOFF_INV_SQUARE; + } + else + { + data.flags &= ~Tr2LightManager::FLAG_FALLOFF_INV_SQUARE; + } return data; } @@ -80,9 +92,9 @@ Tr2LightManager::PerLightData LightData::AsPerSpotLightData( CXMMATRIX transform } Tr2Light::Tr2Light( IRoot* lockobj ) : - m_isDynamic( false ), m_type( UNDEFINED_LIGHT ), - m_name( "" ), + m_isDynamic( false ), + m_scaleBrightness( g_scaleLightBrightnessByRadiusDefault ), m_brightnessMultiplier( 1.f ), m_boneTransform( IdentityMatrix() ) { @@ -118,6 +130,11 @@ void Tr2Light::ChangeLightColor( Color c ) void Tr2Light::AddLight( Tr2LightManager& lightManager, CXMMATRIX transform, float scale, const Float4x3* bones, size_t boneCount ) { + if( !m_lightData.lightingQuality.HasBit( lightManager.GetLightingQuality() ) ) + { + return; + } + if( m_isDynamic ) { this->Update(); @@ -139,12 +156,12 @@ void Tr2Light::AddLight( Tr2LightManager& lightManager, CXMMATRIX transform, flo if( m_type == Tr2Light::POINT_LIGHT ) { auto data = m_lightData.AsPerPointLightData( lightTransform, features, lightManager.GetCurrentSpaceSceneShadowQuality() ); - lightManager.AddLight( data ); + lightManager.AddLight( data, m_scaleBrightness ); } else if( m_type == Tr2Light::SPOT_LIGHT ) { auto data = m_lightData.AsPerSpotLightData( lightTransform, features, lightManager.GetCurrentSpaceSceneShadowQuality() ); - lightManager.AddLight( data ); + lightManager.AddLight( data, m_scaleBrightness ); } } diff --git a/trinity/Lights/Tr2Light.h b/trinity/Lights/Tr2Light.h index 1a1a605f1..9ca651b14 100644 --- a/trinity/Lights/Tr2Light.h +++ b/trinity/Lights/Tr2Light.h @@ -5,6 +5,7 @@ #include "Tr2LightManager.h" #include "Tr2DebugRenderer.h" #include "Utilities/MatrixUtils.h" +#include "../EnumFilter.h" BLUE_DECLARE( Tr2LightProfileRes ); @@ -17,11 +18,10 @@ struct LightFeatures float parentBrightness; }; -enum class PerLightShadowSetting +enum class LightFalloffType : uint8_t { - DISABLED, - ENABLED_ONLY_ON_HIGH_QUALITY, - ALWAYS_ENABLED + INVERSE, + INVERSE_SQUARE }; struct LightData @@ -45,16 +45,20 @@ struct LightData float outerAngle; float innerAngle; + // This should be LightFalloffType, but it can't be used because of a bug in MAP_ATTRIBUTE + uint8_t falloff; + EnumFilter lightingQuality; + // Textured light specifics std::wstring texturePath; int32_t boneIndex; uint16_t flags; - Be::Time startTime; - - PerLightShadowSetting castsShadows; + EnumFilter castsShadows; bool isVolumetric; + + Be::Time startTime; }; @@ -105,6 +109,7 @@ BLUE_CLASS( Tr2Light ) : std::string m_name; Be::Time m_startTime; bool m_isDynamic; + bool m_scaleBrightness; float m_brightnessMultiplier; Matrix m_boneTransform; // used for lights that have boneIndices @@ -116,3 +121,5 @@ TYPEDEF_BLUECLASS( Tr2Light ); extern const Be::VarChooser PerLightShadowSettingChooser[]; extern const Be::VarChooser Tr2LightFlagChooser[]; +extern const Be::VarChooser LightFalloffTypeChooser[]; +extern const Be::VarChooser LightingQualityFilterChooser[]; \ No newline at end of file diff --git a/trinity/Lights/Tr2Light_Blue.cpp b/trinity/Lights/Tr2Light_Blue.cpp index 3f1c0ef28..1efdaac3d 100644 --- a/trinity/Lights/Tr2Light_Blue.cpp +++ b/trinity/Lights/Tr2Light_Blue.cpp @@ -6,12 +6,21 @@ BLUE_DEFINE_ABSTRACT( Tr2Light ); const Be::VarChooser PerLightShadowSettingChooser[] = { - { "Disabled", BeCast( PerLightShadowSetting::DISABLED ), "Light does not cast shadow." }, - { "Enabled Only On High/Raytraced Shadow Quality Settings", BeCast( PerLightShadowSetting::ENABLED_ONLY_ON_HIGH_QUALITY ), "Light only casts shadow when Shadow Quality is set to High or Raytraced." }, - { "Enabled", BeCast( PerLightShadowSetting::ALWAYS_ENABLED ), "Light casts shadow regardless of Shadow Quality Setting" }, + { "Low", BeCast( 1u << static_cast( ShadowQuality::SHADOW_LOW ) ), "Light casts shadow when shadow quality is Low." }, + { "High", BeCast( 1u << static_cast( ShadowQuality::SHADOW_HIGH ) ), "Light casts shadow when shadow quality is High." }, + { "Raytraced", BeCast( 1u << static_cast( ShadowQuality::SHADOW_RAYTRACED ) ), "Light casts shadow when shadow quality is Raytraced." }, { 0 } }; -BLUE_REGISTER_ENUM_EX( "PerLightShadowSetting", PerLightShadowSetting, PerLightShadowSettingChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); +BLUE_REGISTER_ENUM_EX( "ShadowQualityFilter", uint8_t, PerLightShadowSettingChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); + +const Be::VarChooser LightingQualityFilterChooser[] = { + { "Low", BeCast( 1u << static_cast( LightingQuality::LOW ) ), "Active when lighting quality is Low." }, + { "Medium", BeCast( 1u << static_cast( LightingQuality::MEDIUM ) ), "Active when lighting quality is Medium." }, + { "High", BeCast( 1u << static_cast( LightingQuality::HIGH ) ), "Active when lighting quality is High." }, + { 0 } +}; +BLUE_REGISTER_ENUM_EX( "LightingQualityFilter", uint8_t, LightingQualityFilterChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); + const Be::VarChooser Tr2LightFlagChooser[] = { { "AFFECTS_SURFACES", BeCast( Tr2LightManager::FLAG_AFFECTS_SURFACES ), "Affects surfaces" }, @@ -20,6 +29,13 @@ const Be::VarChooser Tr2LightFlagChooser[] = { }; BLUE_REGISTER_ENUM_EX( "Tr2LightFlags", uint16_t, Tr2LightFlagChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); +const Be::VarChooser LightFalloffTypeChooser[] = { + { "INVERSE", BeCast( LightFalloffType::INVERSE ), "Inverse distance falloff" }, + { "INVERSE_SQUARE", BeCast( LightFalloffType::INVERSE_SQUARE ), "Inverse square distance falloff" }, + { 0 } +}; +BLUE_REGISTER_ENUM_EX( "LightFalloffType", LightFalloffType, LightFalloffTypeChooser, ENUM_REG_ENUM_OBJECT_ON_MODULE ); + const Be::ClassInfo* Tr2Light::ExposeToBlue() { EXPOSURE_BEGIN( Tr2Light, "" ) diff --git a/trinity/Lights/Tr2PointLight.cpp b/trinity/Lights/Tr2PointLight.cpp index bc0ccb794..523a48c92 100644 --- a/trinity/Lights/Tr2PointLight.cpp +++ b/trinity/Lights/Tr2PointLight.cpp @@ -24,6 +24,8 @@ void Tr2PointLight::RenderDebugInfo( ITr2DebugRenderer2& renderer, const Matrix& } lightMatrix *= worldMatrix; - renderer.DrawSphere( this, lightMatrix, m_lightData.position, m_lightData.radius, 10, Tr2DebugRenderer::Solid, Tr2DebugColor( selectedColor, baseColor ) ); - renderer.DrawSphere( this, lightMatrix, m_lightData.position, m_lightData.innerRadius, 10, Tr2DebugRenderer::Solid, Tr2DebugColor( selectedColor, baseColor ) ); + renderer.DrawSphere( this, lightMatrix, m_lightData.position, m_lightData.radius, 10, Tr2DebugRenderer::Wireframe, Tr2DebugColor( selectedColor, baseColor ) ); + renderer.DrawSphere( this, lightMatrix, m_lightData.position, m_lightData.radius, 10, Tr2DebugRenderer::Solid, Color( 0, 0, 0, 0 ) ); + renderer.DrawSphere( this, lightMatrix, m_lightData.position, m_lightData.innerRadius, 10, Tr2DebugRenderer::Wireframe, Tr2DebugColor( selectedColor, baseColor ) ); + renderer.DrawText( TRI_DBG_FONT_SMALL, TransformCoord( m_lightData.position, lightMatrix ), baseColor, "%s", m_name.empty() ? "Tr2PointLight" : m_name.c_str() ); } diff --git a/trinity/Lights/Tr2PointLight_Blue.cpp b/trinity/Lights/Tr2PointLight_Blue.cpp index f341951e2..01f065e85 100644 --- a/trinity/Lights/Tr2PointLight_Blue.cpp +++ b/trinity/Lights/Tr2PointLight_Blue.cpp @@ -26,13 +26,17 @@ const Be::ClassInfo* Tr2PointLight::ExposeToBlue() MAP_ATTRIBUTE( "color", m_lightData.color, "Light color (in linear space)\n:jessica-tuple-type: linearcolor", Be::READWRITE | Be::PERSIST | Be::NOTIFY ) MAP_ATTRIBUTE( "brightness", m_lightData.brightness, "Light brightness (modulates color) for easier animation", Be::READWRITE | Be::PERSIST | Be::NOTIFY ) + MAP_ATTRIBUTE_WITH_CHOOSER( "falloff", m_lightData.falloff, "Light falloff type", Be::READWRITE | Be::PERSIST | Be::NOTIFY | Be::ENUM, LightFalloffTypeChooser ) + MAP_ATTRIBUTE( "noiseAmplitude", m_lightData.noiseAmplitude, "Brightness noise amplitude\n:jessica-group: Noise", Be::READWRITE | Be::PERSIST ) MAP_ATTRIBUTE( "noiseFrequency", m_lightData.noiseFrequency, "Brightness noise frequency\n:jessica-group: Noise", Be::READWRITE | Be::PERSIST ) MAP_ATTRIBUTE( "noiseOctaves", m_lightData.noiseOctaves, "Brightness turbulence octaves\n:jessica-group: Noise", Be::READWRITE | Be::PERSIST ) - MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY | Be::ENUM, PerLightShadowSettingChooser ); + MAP_ATTRIBUTE_WITH_CHOOSER( "lightingQuality", m_lightData.lightingQuality.m_filter, "Filter for lighting quality settings. Controls whether light is active with a certain lighting quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, LightingQualityFilterChooser ); + MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); MAP_ATTRIBUTE( "isVolumetric", m_lightData.isVolumetric, "Volumetric lights affect participating media such as fog.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) + MAP_ATTRIBUTE( "scaleBrightness", m_scaleBrightness, "Scale light brightness by its radius", Be::READWRITE | Be::PERSIST ) MAP_ATTRIBUTE( "lightProfilePath", diff --git a/trinity/Lights/Tr2SpotLight.cpp b/trinity/Lights/Tr2SpotLight.cpp index f976c1ba3..874103a21 100644 --- a/trinity/Lights/Tr2SpotLight.cpp +++ b/trinity/Lights/Tr2SpotLight.cpp @@ -30,6 +30,8 @@ void Tr2SpotLight::RenderDebugInfo( ITr2DebugRenderer2& renderer, const Matrix& float outerAngle = TRI_2PI * m_lightData.outerAngle / 360.f; float innerAngle = TRI_2PI * m_lightData.innerAngle / 360.f; - renderer.DrawCone( this, lightMatrix, m_lightData.radius, outerAngle, 15, 15, Tr2DebugRenderer::Solid, Tr2DebugColor( baseColor + colorMod * 2.0f, baseColor ) ); - renderer.DrawCone( this, lightMatrix, m_lightData.innerRadius, innerAngle, 15, 15, Tr2DebugRenderer::Solid, Tr2DebugColor( baseColor + colorMod * 3.0f, baseColor + colorMod ) ); + renderer.DrawCone( this, lightMatrix, m_lightData.radius, outerAngle, 15, 15, Tr2DebugRenderer::Wireframe, Tr2DebugColor( baseColor + colorMod * 2.0f, baseColor ) ); + renderer.DrawCone( this, lightMatrix, m_lightData.radius, outerAngle, 15, 15, Tr2DebugRenderer::Solid, Color( 0, 0, 0, 0 ) ); + renderer.DrawCone( this, lightMatrix, m_lightData.innerRadius, innerAngle, 15, 15, Tr2DebugRenderer::Wireframe, Tr2DebugColor( baseColor + colorMod * 3.0f, baseColor + colorMod ) ); + renderer.DrawText( TRI_DBG_FONT_SMALL, lightMatrix.GetTranslation(), baseColor, "%s", m_name.empty() ? "Tr2SpotLight" : m_name.c_str() ); } \ No newline at end of file diff --git a/trinity/Lights/Tr2SpotLight_Blue.cpp b/trinity/Lights/Tr2SpotLight_Blue.cpp index 01535c91b..0cb2eae34 100644 --- a/trinity/Lights/Tr2SpotLight_Blue.cpp +++ b/trinity/Lights/Tr2SpotLight_Blue.cpp @@ -29,13 +29,18 @@ const Be::ClassInfo* Tr2SpotLight::ExposeToBlue() MAP_ATTRIBUTE( "color", m_lightData.color, "Light color (in linear space)\n:jessica-tuple-type: linearcolor", Be::READWRITE | Be::PERSIST | Be::NOTIFY ) MAP_ATTRIBUTE( "brightness", m_lightData.brightness, "Light brightness (modulates color) for easier animation", Be::READWRITE | Be::PERSIST | Be::NOTIFY ) + MAP_ATTRIBUTE_WITH_CHOOSER( "falloff", m_lightData.falloff, "Light falloff type", Be::READWRITE | Be::PERSIST | Be::NOTIFY | Be::ENUM, LightFalloffTypeChooser ) + MAP_ATTRIBUTE( "noiseAmplitude", m_lightData.noiseAmplitude, "Brightness noise amplitude\n:jessica-group: Noise", Be::READWRITE | Be::PERSIST ) MAP_ATTRIBUTE( "noiseFrequency", m_lightData.noiseFrequency, "Brightness noise frequency\n:jessica-group: Noise", Be::READWRITE | Be::PERSIST ) MAP_ATTRIBUTE( "noiseOctaves", m_lightData.noiseOctaves, "Brightness turbulence octaves\n:jessica-group: Noise", Be::READWRITE | Be::PERSIST ) - MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY | Be::ENUM, PerLightShadowSettingChooser ); + MAP_ATTRIBUTE_WITH_CHOOSER( "lightingQuality", m_lightData.lightingQuality.m_filter, "Filter for lighting quality settings. Controls whether light is active with a certain lighting quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, LightingQualityFilterChooser ); + MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); MAP_ATTRIBUTE( "isVolumetric", m_lightData.isVolumetric, "Volumetric lights affect participating media such as fog.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) + MAP_ATTRIBUTE( "scaleBrightness", m_scaleBrightness, "Scale light brightness by its radius", Be::READWRITE | Be::PERSIST ) + MAP_ATTRIBUTE( "lightProfilePath", m_lightProfilePath, diff --git a/trinity/Lights/Tr2TexturedPointLight_Blue.cpp b/trinity/Lights/Tr2TexturedPointLight_Blue.cpp index 029a2ed4e..92778ff02 100644 --- a/trinity/Lights/Tr2TexturedPointLight_Blue.cpp +++ b/trinity/Lights/Tr2TexturedPointLight_Blue.cpp @@ -26,6 +26,8 @@ const Be::ClassInfo* Tr2TexturedPointLight::ExposeToBlue() MAP_ATTRIBUTE( "color", m_lightData.color, "Light color (in linear space)\n:jessica-tuple-type: linearcolor", Be::READWRITE | Be::PERSIST | Be::NOTIFY ) MAP_ATTRIBUTE( "brightness", m_lightData.brightness, "Light brightness (modulates color) for easier animation", Be::READWRITE | Be::PERSIST | Be::NOTIFY ) + MAP_ATTRIBUTE_WITH_CHOOSER( "falloff", m_lightData.falloff, "Light falloff type", Be::READWRITE | Be::PERSIST | Be::NOTIFY | Be::ENUM, LightFalloffTypeChooser ) + MAP_ATTRIBUTE( "noiseAmplitude", m_lightData.noiseAmplitude, "Brightness noise amplitude\n:jessica-group: Noise", Be::READWRITE | Be::PERSIST ) MAP_ATTRIBUTE( "noiseFrequency", m_lightData.noiseFrequency, "Brightness noise frequency\n:jessica-group: Noise", Be::READWRITE | Be::PERSIST ) MAP_ATTRIBUTE( "noiseOctaves", m_lightData.noiseOctaves, "Brightness turbulence octaves\n:jessica-group: Noise", Be::READWRITE | Be::PERSIST ) @@ -33,9 +35,12 @@ const Be::ClassInfo* Tr2TexturedPointLight::ExposeToBlue() MAP_ATTRIBUTE( "texturePath", m_lightData.texturePath, ":jessica-group: Texture", Be::READWRITE | Be::PERSIST | Be::NOTIFY ) MAP_ATTRIBUTE( "texture", m_texture, ":jessica-group: Texture", Be::READ ) - MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY | Be::ENUM, PerLightShadowSettingChooser ); + MAP_ATTRIBUTE_WITH_CHOOSER( "lightingQuality", m_lightData.lightingQuality.m_filter, "Filter for lighting quality settings. Controls whether light is active with a certain lighting quality setting.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, LightingQualityFilterChooser ); + MAP_ATTRIBUTE_WITH_CHOOSER( "castsShadows", m_lightData.castsShadows.m_filter, "Casts shadows. If the light is also volumetric, it will create godrays around the affected objects.", Be::READWRITE | Be::PERSIST | Be::NOTIFY, PerLightShadowSettingChooser ); MAP_ATTRIBUTE( "isVolumetric", m_lightData.isVolumetric, "Volumetric lights affect participating media such as fog.", Be::READWRITE | Be::NOTIFY | Be::PERSIST ) + MAP_ATTRIBUTE( "scaleBrightness", m_scaleBrightness, "Scale light brightness by its radius", Be::READWRITE | Be::PERSIST ) + MAP_ATTRIBUTE( "lightProfilePath", m_lightProfilePath, diff --git a/trinity/Tr2LightManager.cpp b/trinity/Tr2LightManager.cpp index ae20b7c3f..a256ca628 100644 --- a/trinity/Tr2LightManager.cpp +++ b/trinity/Tr2LightManager.cpp @@ -217,6 +217,11 @@ void Tr2LightManager::SetVariableStore() m_indexBufferVariable = m_indexBuffer; } +uint16_t Tr2LightManager::PackFlags( uint16_t flags, uint16_t profileIndex ) +{ + return ( flags & ( ( 1 << FLAG_BITS ) - 1 ) ) | ( profileIndex << FLAG_BITS ); +} + void Tr2LightManager::Clear( Tr2RenderContext& renderContext ) { m_lightBufferVariable = m_lightBuffer; @@ -294,7 +299,17 @@ void Tr2LightManager::SetShadowQuality( ShadowQuality shadowQuality, uint64_t fr m_ShadowMap.m_atlasSettings.actualTextureSize = CalculateShadowMapAtlasSettings( m_ShadowMap.m_qualityUsedByAtlas ).size; } -void Tr2LightManager::AddPointLight( const Vector3& position, float radius, const Color& color, Float_16 innerRadius, uint16_t flags ) +void Tr2LightManager::SetLightingQuality( LightingQuality lightingQuality ) +{ + m_lightingQuality = lightingQuality; +} + +LightingQuality Tr2LightManager::GetLightingQuality() const +{ + return m_lightingQuality; +} + +void Tr2LightManager::AddPointLight( const Vector3& position, float radius, const Color& color, Float_16 innerRadius, uint16_t flags, bool scaleBrightness ) { if( !AreLightFlagsValid( flags ) ) { @@ -319,9 +334,20 @@ void Tr2LightManager::AddPointLight( const Vector3& position, float radius, cons { float dimming = std::min( ( size - m_adjustedCutoff ) / FADE_SIZE, 1.f ); data.color = reinterpret_cast( color ); - data.color.x *= radius * dimming; - data.color.y *= radius * dimming; - data.color.z *= radius * dimming; + if( scaleBrightness ) + { + if( flags & FLAG_FALLOFF_INV_SQUARE ) + { + dimming *= radius * radius; + } + else + { + dimming *= radius; + } + } + data.color.x *= dimming; + data.color.y *= dimming; + data.color.z *= dimming; data.innerRadius = innerRadius; data.flags = flags; data.direction = Vector3_16( Vector3( 1.f, 0.f, 0.f ) ); @@ -331,7 +357,7 @@ void Tr2LightManager::AddPointLight( const Vector3& position, float radius, cons } } -void Tr2LightManager::AddLight( PerLightData& data ) +void Tr2LightManager::AddLight( PerLightData& data, bool scaleBrightness ) { if( !AreLightFlagsValid( data.flags ) ) { @@ -352,10 +378,20 @@ void Tr2LightManager::AddLight( PerLightData& data ) if( size > m_adjustedCutoff ) { float dimming = std::min( ( size - m_adjustedCutoff ) / FADE_SIZE, 1.f ); - data.color.x *= data.radius * dimming; - data.color.y *= data.radius * dimming; - data.color.z *= data.radius * dimming; - + if( scaleBrightness ) + { + if( data.flags & FLAG_FALLOFF_INV_SQUARE ) + { + dimming *= data.radius * data.radius; + } + else + { + dimming *= data.radius; + } + } + data.color.x *= dimming; + data.color.y *= dimming; + data.color.z *= dimming; bool usingShadowMap = m_currentSpaceSceneShadowQuality == ShadowQuality::SHADOW_LOW || m_currentSpaceSceneShadowQuality == ShadowQuality::SHADOW_HIGH; if( m_currentSpaceSceneShadowQuality == ShadowQuality::SHADOW_DISABLED || ( usingShadowMap && m_ShadowMap.m_qualityUsedByAtlas == ShadowQuality::SHADOW_DISABLED ) || diff --git a/trinity/Tr2LightManager.h b/trinity/Tr2LightManager.h index d89b55156..4db9d9b4d 100644 --- a/trinity/Tr2LightManager.h +++ b/trinity/Tr2LightManager.h @@ -30,6 +30,14 @@ enum class ShadowQuality SHADOW_RAYTRACED }; +// The base is deliberately set to 32bit because of a bug in MAP_ATTRIBUTE +enum class LightingQuality : int32_t +{ + LOW, + MEDIUM, + HIGH, +}; + // -------------------------------------------------------------------------------------- // Description: // Manages light sources for tiled/clustered lighting. An object of this class does not @@ -101,19 +109,28 @@ class Tr2LightManager : public Tr2DeviceResource static const uint16_t FLAG_AFFECTS_PARTICLES = 1 << 1; static const uint16_t FLAG_CASTS_SHADOWS = 1 << 2; static const uint16_t FLAG_IS_VOLUMETRIC = 1 << 3; + static const uint16_t FLAG_FALLOFF_INV_SQUARE = 1 << 4; + + static const uint16_t FLAG_BITS = 5; static const uint16_t FLAG_DEFAULT = FLAG_AFFECTS_SURFACES; + static uint16_t PackFlags( uint16_t flags, uint16_t profileIndex ); + void Clear( Tr2RenderContext& renderContext ); void SetFrustum( const TriFrustum& frustum ); - void AddPointLight( const Vector3& position, float radius, const Color& color, Float_16 innerRadius = Float_16( 0.f ), uint16_t flags = FLAG_DEFAULT ); - void AddLight( PerLightData& data ); + void AddPointLight( const Vector3& position, float radius, const Color& color, Float_16 innerRadius = Float_16( 0.f ), uint16_t flags = FLAG_DEFAULT, bool scaleBrightness = true ); + void AddLight( PerLightData& data, bool scaleBrightness = true ); void ResolveLightData(); ALResult UpdateLists( const Tr2TextureAL& depthMap, Tr2RenderContext& renderContext ); void SetVariableStore(); void AdjustLightCutoff( float lodFactor ); void SetShadowQuality( ShadowQuality shadowQuality, uint64_t frameCounter ); + void SetLightingQuality( LightingQuality lightingQuality ); + LightingQuality GetLightingQuality() const; + + static const uint8_t ANY_LIGHT_QUALITY = 0xff; virtual void ReleaseResources( TriStorage s ); @@ -195,6 +212,7 @@ class Tr2LightManager : public Tr2DeviceResource uint32_t nextFrameShadowQuality; // bitmask, collecting ShadowQualities during the current frame ShadowQuality m_currentSpaceSceneShadowQuality; + LightingQuality m_lightingQuality = LightingQuality::HIGH; uint64_t m_currentFrameCounter; struct