Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
0025a82
Add an option to disable light brightness scale based on its radius
filipppavlov Jun 26, 2026
6b23c8c
Change light debug rendering: draw wireframe instead of solid and ren…
filipppavlov Jul 1, 2026
b01c8f4
Change "castShadows" member for Tr2Light descendants to a bitfield so…
filipppavlov Jul 1, 2026
28d4be5
Add an option to switch light falloff curve between inverse and inver…
filipppavlov Jul 2, 2026
6f97dd8
Add lighting quality setting; fix auto brightness scaling for lights …
filipppavlov Jul 6, 2026
69adfac
Add an option to disable light brightness scale based on its radius
filipppavlov Jun 26, 2026
1632345
Change light debug rendering: draw wireframe instead of solid and ren…
filipppavlov Jul 1, 2026
5602459
Change "castShadows" member for Tr2Light descendants to a bitfield so…
filipppavlov Jul 1, 2026
d0b49ac
Add an option to switch light falloff curve between inverse and inver…
filipppavlov Jul 2, 2026
a8ce975
Add lighting quality setting; fix auto brightness scaling for lights …
filipppavlov Jul 6, 2026
94023fa
Merge branch 'light-improvements' of https://github.com/carbonengine/…
filipppavlov Jul 24, 2026
6201c8f
Address PR comments
filipppavlov Jul 24, 2026
322b5ba
Try to fix cpp-format action failing to add comments to PRs
filipppavlov Jul 24, 2026
114901a
Undo cpp-format change: it did not make a difference
filipppavlov Jul 24, 2026
18b5814
Formatting
filipppavlov Aug 25, 2026
d5a5858
Merge branch 'main' into light-improvements
filipppavlov Aug 26, 2026
30ad453
Expose lighting quality filter in EveSmartLightPointLight
filipppavlov Aug 31, 2026
db76223
Merge branch 'main' into light-improvements
filipppavlov Sep 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions trinity/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1439,6 +1439,7 @@ set(_SOURCES
autoversion.h
ContinueOnMainThread.cpp
ContinueOnMainThread.h
EnumFilter.h
EveSprite2dBracket.cpp
EveSprite2dBracket.h
EveSprite2dBracket_Blue.cpp
Expand Down
60 changes: 60 additions & 0 deletions trinity/EnumFilter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright © 2026 CCP ehf.

#pragma once

#include <cstdint>


/**
* @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 <typename Enum, typename StorageType = uint8_t>
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;
};
12 changes: 0 additions & 12 deletions trinity/Eve/EveSpaceScene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1375,18 +1375,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 )
Expand Down
16 changes: 16 additions & 0 deletions trinity/Eve/EveSpaceSceneRenderDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{

Expand Down Expand Up @@ -486,6 +488,20 @@ void EveSpaceSceneRenderDriver::Execute( const Span<const Tr2TextureAL>& 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 );
}
{
Expand Down
1 change: 1 addition & 0 deletions trinity/Eve/EveSpaceSceneRenderDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 );

Expand Down
14 changes: 14 additions & 0 deletions trinity/Eve/EveSpaceSceneRenderDriver_Blue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 );



Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -68,6 +71,10 @@ void EveSmartLightPointLight::GetLights( Tr2LightManager& lightManager ) const
{
return;
}
if( !m_lightGroupData.lightingQuality.HasBit( lightManager.GetLightingQuality() ) )
{
return;
}

const PlacementDataWithIdentifierStructureList& placements = *m_distribution->GetPlacementData();
size_t size = m_distribution->GetNumberOfPlacements();
Expand All @@ -92,7 +99,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 ) )
Expand Down Expand Up @@ -126,7 +133,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 );
Comment thread
filipppavlov marked this conversation as resolved.
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const Be::ClassInfo* EveSmartLightPointLight::ExposeToBlue()
MAP_ATTRIBUTE( "display", m_display, "", Be::READWRITE | Be::PERSIST )

MAP_ATTRIBUTE_WITH_CHOOSER( "flags", m_lightGroupData.flags, "Various light options", Be::READWRITE | Be::PERSIST, Tr2LightFlagChooser )
MAP_ATTRIBUTE_WITH_CHOOSER( "lightingQuality", m_lightGroupData.lightingQuality.m_filter, "Filter for lighting quality settings. Controls whether light is active with a certain lighting quality setting.", Be::READWRITE | Be::PERSIST, LightingQualityFilterChooser );

MAP_ATTRIBUTE( "radius", m_lightGroupData.radius, "Light radius", Be::READWRITE | Be::PERSIST )
MAP_ATTRIBUTE( "innerRadius", m_lightGroupData.innerRadius, "Inner light radius (to mimick a glowing sphere)", Be::READWRITE | Be::PERSIST )
Expand All @@ -34,5 +35,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 )
}
12 changes: 8 additions & 4 deletions trinity/Lights/Tr2FactionLight.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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() );
}
}
5 changes: 4 additions & 1 deletion trinity/Lights/Tr2FactionLight_Blue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
33 changes: 25 additions & 8 deletions trinity/Lights/Tr2Light.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() :
Expand All @@ -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<LightingQuality>::AllBits() ),
Comment thread
filipppavlov marked this conversation as resolved.
texturePath( L"" ),
boneIndex( -1 ),
flags( Tr2LightManager::FLAG_DEFAULT ),
startTime( BeOS->GetCurrentFrameTime() ),
castsShadows( PerLightShadowSetting::DISABLED ),
isVolumetric( false )
{
}
Expand All @@ -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;
Expand All @@ -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;
}
Expand All @@ -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() )
{
Expand Down Expand Up @@ -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();
Expand All @@ -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 );
}
}

Expand Down
Loading