diff --git a/CHANGELOG.md b/CHANGELOG.md
index 89614c17d..2cea2b24c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,15 +6,18 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
## Version 1.6 - xxxx-xx-xx
### Bug fixes
-* Fixed engine performance around bridges.
-* Fixed engine performance if weather or bubble effects are active.
+* Significantly improved renderer performance.
+* Improved engine performance around bridges.
+* Improved engine performance if weather or bubble effects are active.
* Fixed silent crashes if loaded level is corrupted or in incorrect format.
* Fixed occasional crashes if there are static meshes placed within room border walls.
* Fixed incorrect clipping of scaled off-centered static meshes.
* Fixed incorrect collision detection for off-centered moveables.
+* Fixed incorrect slide directions for sub-click geometry.
* Fixed stutter during jumps between cameras in a flyby sequence.
* Fixed uzi targeting issues after using flycheat.
* Fixed snow particles not always melting on the ground.
+* Fixed enemies not damaging Lara if she is staying on the sector where enemies were triggered.
* Fixed enemy pickups dropping on death sectors.
* Fixed Sarcophagus and Search Object pickup triggers.
* Fixed vehicle transfer not happening for levels which were not previously visited.
@@ -30,6 +33,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
* Fixed original issue with deactivation of Dart Emitter.
* Fixed Lens Flare object not functioning properly.
* Fixed lens flares not being occluded by static meshes and moveables.
+* Fixed spotlight shadows.
* Fixed Skeleton and Mummy not reacting to shotgun hits.
### New Features
@@ -43,14 +47,20 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
* Added Moveable:GetCollidable() and Moveable:SetCollidable() functions.
* Added Flow.GetFreezeMode() and Flow.SetFreezeMode() functions.
* Added Flow.GetNextLevel() function to get script entry for incoming level, if it's about to start.
+* Added Effects.EmitSpotLight() function for directional spotlights.
+* Added optional cast shadow and name parameters for Effects.EmitLight() function.
* Added Effects.GetWind() function to get current wind speed vector.
+* Added Rotation:Direction() method to get directional vector.
* Added support for transparency value in DisplayString class.
* Added extra argument for SetAmbientTrack() function to specify if new ambient track should play from the beginning.
* Use load camera instead of load screen by playing fixed camera from OnEnd() event and removing loadScreenFile field from level's gameflow entry.
* Fixed DisplayString class not supporting some Unicode characters and empty lines in multiline strings.
+* Fixed DisplayString not being deallocated after showing.
+* Fixed incorrect behaviour of Moveable:GetJointRotation() function.
* Fixed incorrect behaviour of Logic.EnableEvent() and Logic.DisableEvent() functions.
* Fixed Util.HasLineOfSight() not taking static meshes into consideration.
* Fixed collision callbacks not properly clearing after leveljump.
+* Fixed SetIntroImagePath() not using the correct path
## [Version 1.5](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.2) - 2024-11-03
diff --git a/Documentation/compile.bat b/Documentation/compile.bat
index d6815322e..4ebed825b 100644
--- a/Documentation/compile.bat
+++ b/Documentation/compile.bat
@@ -1,8 +1,11 @@
@echo off
setlocal
+set DOC_DIR=.\doc
set LDOC_DIR=.\compiler\ldoc
set LUA_PATH=.\compiler\?.lua
set LUA_CPATH=.\compiler\?.dll
+rmdir /s /q %DOC_DIR%
+mkdir %DOC_DIR%
.\compiler\lua.exe %LDOC_DIR%\\ldoc.lua %*
del output.xml
exit /b %ERRORLEVEL%
diff --git a/Documentation/doc/1 modules/Effects.html b/Documentation/doc/1 modules/Effects.html
index 0e51a9a27..e1c3b28bb 100644
--- a/Documentation/doc/1 modules/Effects.html
+++ b/Documentation/doc/1 modules/Effects.html
@@ -125,10 +125,14 @@
Emit a shockwave, similar to that seen when a harpy projectile hits something.
- EmitLight(pos, color, radius)
+ EmitLight(pos[, color][, radius][, shadows][, name])
Emit dynamic light that lasts for a single frame.
+ EmitSpotLight(pos, dir[, color][, radius][, falloff][, distance][, shadows][, name])
+ Emit dynamic directional spotlight that lasts for a single frame.
+
+
EmitBlood(pos, count)
Emit blood.
@@ -363,7 +367,7 @@
- EmitLight(pos, color, radius)
+ EmitLight(pos[, color][, radius][, shadows][, name])
Emit dynamic light that lasts for a single frame.
@@ -375,17 +379,84 @@
pos
Vec3
+ position of the light
+
+ color
+ Color
+ light color (default Color(255, 255, 255))
+ (optional )
+
+ radius
+ int
+ measured in "clicks" or 256 world units (default 20)
+ (optional )
+
+ shadows
+ bool
+ determines whether light should generate dynamic shadows for applicable moveables (default is false)
+ (optional )
+
+ name
+ string
+ if provided, engine will interpolate this light for high framerate mode (be careful not to use same name for different lights)
+ (optional )
+
+
+
+
+
+
+
+ EmitSpotLight(pos, dir[, color][, radius][, falloff][, distance][, shadows][, name])
+
+
+ Emit dynamic directional spotlight that lasts for a single frame.
+ If you want a light that sticks around, you must call this each frame.
+
+
+
+ Parameters:
+
+ pos
+ Vec3
+ position of the light
+
+ dir
+ Vec3
+ direction, or a point to which spotlight should be directed to
color
Color
(default Color(255, 255, 255))
+ (optional )
radius
int
- (default 20) corresponds loosely to both intensity and range
+ overall radius at the endpoint of a light cone, measured in "clicks" or 256 world units (default 10)
+ (optional )
+
+ falloff
+ int
+ radius, at which light starts to fade out, measured in "clicks" (default 5)
+ (optional )
+
+ distance
+ int
+ distance, at which light cone fades out, measured in "clicks" (default 20)
+ (optional )
+
+ shadows
+ bool
+ determines whether light should generate dynamic shadows for applicable moveables (default is false)
+ (optional )
+
+ name
+ string
+ if provided, engine will interpolate this light for high framerate mode (be careful not to use same name for different lights)
+ (optional )
diff --git a/Documentation/doc/1 modules/Misc.html b/Documentation/doc/1 modules/Misc.html
deleted file mode 100644
index 4338d7f99..000000000
--- a/Documentation/doc/1 modules/Misc.html
+++ /dev/null
@@ -1,1009 +0,0 @@
-
-
-
-
- TombEngine 1.1.0 Lua API
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
TombEngine
-
-
-
-
1 Modules
-
-
2 Classes
-
-
3 Primitive Classes
-
-
4 Enums
-
-
5 Lua utility modules
-
-
-
-
-
-
-
Table Misc
-
Functions that don't fit in the other modules.
-
-
-
-
-
-
-
-
- HasLineOfSight(room1, pos1, pos2)
- Determine if there's a line of sight between two points.
-
-
- Vibrate(strength, time)
- Vibrate game controller, if function is available and setting is on.
-
-
- FadeOut(speed)
- Do a full-screen fade-to-black.
-
-
- FadeIn(speed)
- Do a full-screen fade-in from black.
-
-
- SetCineBars(height, speed)
- Move black cinematic bars in from the top and bottom of the game window.
-
-
- SetFOV(angle)
- Set field of view.
-
-
- GetCameraType()
- Shows the mode of the game camera.
-
-
- PlayAudioTrack(name, type)
- Play an audio track
-
-
- SetAmbientTrack(name)
- Set and play an ambient track
-
-
- StopAudioTracks()
- Stop any audio tracks currently playing
-
-
- StopAudioTrack(type)
- Stop audio track that is currently playing
-
-
- GetAudioTrackLoudness(type)
- Get current loudness level for specified track type
-
-
- GetCurrentSubtitle()
- Get current subtitle string for a voice track currently playing.
-
-
- PlaySound(sound[, position])
- Play sound effect
-
-
- IsSoundPlaying(Sound)
- Check if the sound effect is playing
-
-
- IsAudioTrackPlaying(Track)
- Check if the audio track is playing
-
-
- FlipMap(flipmap)
- Do FlipMap with specific ID
-
-
- PlayFlyBy(flyby)
- Enable FlyBy with specific ID
-
-
- CalculateDistance(posA, posB)
- Calculate the distance between two positions.
-
-
- CalculateHorizontalDistance(posA, posB)
- Calculate the horizontal distance between two positions.
-
-
- PercentToScreen(x, y)
- Translate a pair of percentages to screen-space pixel coordinates.
-
-
- ScreenToPercent(x, y)
- Translate a pair of coordinates to percentages of window dimensions.
-
-
- ResetObjCamera()
- Reset object camera back to Lara and deactivate object camera.
-
-
- PrintLog(message, logLevel[, allowSpam])
- Write messages within the Log file
-
-
- Vibrate(strength, time)
- Vibrate gamepad, if possible.
-
-
- KeyIsHeld(action)
- Check if particular action key is held
-
-
- KeyIsHit(action)
- Check if particular action key was hit (once)
-
-
- KeyPush(action)
- Emulate pushing of a certain action key
-
-
- KeyClear(action)
- Clears particular input from action key
-
-
-
-
-
-
-
-
-
-
-
-
- HasLineOfSight(room1, pos1, pos2)
-
-
- Determine if there's a line of sight between two points.
-
-i.e. if we run a direct line from one position to another
-will any geometry get in the way?
-
-Note: if you use this with Moveable:GetPosition to test if (for example)
-two creatures can see one another, you might have to do some extra adjustments.
-
-This is because the "position" for most objects refers to its base, i.e., the floor.
-As a solution, you can increase the y-coordinate of this position to correspond to roughly where the
-eyes of the creatures would be.
-
-
-
-
Parameters:
-
- room1
- float
- ID of the room where the first position is
-
- pos1
- Vec3
- first position
-
- pos2
- Vec3
- second position
-
-
-
- Returns:
-
-
- bool
- is there a direct line of sight between the two positions?
-
-
-
-
- Usage:
- local flamePlinthPos = flamePlinth:GetPosition() + Vec3(0 , flamePlinthHeight, 0 );
-print (Misc.HasLineOfSight(enemyHead:GetRoomNumber(), enemyHead:GetPosition(), flamePlinthPos))
-
-
-
-
-
- Vibrate(strength, time)
-
-
- Vibrate game controller, if function is available and setting is on.
-
-
-
- Parameters:
-
- strength
- float
- Strength of the vibration
-
- time
- float
- (default 0.3) Time of the vibration, in seconds
-
-
-
-
-
-
-
-
-
-
- FadeOut(speed)
-
-
- Do a full-screen fade-to-black. The screen will remain black until a call to FadeIn.
-
-
-
- Parameters:
-
- speed
- float
- (default 1.0). Speed in "amount" per second. A value of 1 will make the fade take one second.
-
-
-
-
-
-
-
-
-
-
- FadeIn(speed)
-
-
- Do a full-screen fade-in from black.
-
-
-
- Parameters:
-
- speed
- float
- (default 1.0). Speed in "amount" per second. A value of 1 will make the fade take one second.
-
-
-
-
-
-
-
-
-
-
- SetCineBars(height, speed)
-
-
- Move black cinematic bars in from the top and bottom of the game window.
-
-
-
- Parameters:
-
- height
- float
- (default 30) Percentage of the screen to be covered
-
- speed
- float
- (default 30) Coverage percent per second
-
-
-
-
-
-
-
-
-
-
- SetFOV(angle)
-
-
- Set field of view.
-
-
-
- Parameters:
-
- angle
- float
- in degrees (clamped to [10, 170])
-
-
-
-
-
-
-
-
-
-
- GetCameraType()
-
-
- Shows the mode of the game camera.
-
-
-
-
- Returns:
-
-
- CameraType
- value used by the Main Camera.
-
-
-
-
- Usage:
- LevelFuncs.OnControlPhase = function ()
- if (Misc.GetCameraType() == CameraType.Combat) then
- end
-end
-
-
-
-
-
- PlayAudioTrack(name, type)
-
-
- Play an audio track
-
-
-
- Parameters:
-
- name
- string
- of track (without file extension) to play
-
- type
- SoundTrackType
- of the audio track to play
-
-
-
-
-
-
-
-
-
-
- SetAmbientTrack(name)
-
-
- Set and play an ambient track
-
-
-
- Parameters:
-
- name
- string
- of track (without file extension) to play
-
-
-
-
-
-
-
-
-
-
- StopAudioTracks()
-
-
- Stop any audio tracks currently playing
-
-
-
-
-
-
-
-
-
-
-
- StopAudioTrack(type)
-
-
- Stop audio track that is currently playing
-
-
-
- Parameters:
-
-
-
-
-
-
-
-
-
- GetAudioTrackLoudness(type)
-
-
- Get current loudness level for specified track type
-
-
-
- Parameters:
-
-
- Returns:
-
-
- float
- current loudness of a specified audio track
-
-
-
-
-
-
-
-
- GetCurrentSubtitle()
-
-
- Get current subtitle string for a voice track currently playing.
-Subtitle file must be in .srt format, have same filename as voice track, and be placed in same directory as voice track.
-Returns nil if no voice track is playing or no subtitle present.
-
-
-
-
- Returns:
-
-
- string
- current subtitle string
-
-
-
-
-
-
-
-
- PlaySound(sound[, position])
-
-
- Play sound effect
-
-
-
- Parameters:
-
- sound
- int
- ID to play. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window.
-
- position
- Vec3
- The 3D position of the sound, i.e. where the sound "comes from". If not given, the sound will not be positional.
- (optional )
-
-
-
-
-
-
-
-
-
-
- IsSoundPlaying(Sound)
-
-
- Check if the sound effect is playing
-
-
-
- Parameters:
-
- Sound
- int
- ID to check. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window.
-
-
-
-
-
-
-
-
-
-
- IsAudioTrackPlaying(Track)
-
-
- Check if the audio track is playing
-
-
-
- Parameters:
-
- Track
- string
- filename to check. Should be without extension and without full directory path.
-
-
-
-
-
-
-
-
-
-
- FlipMap(flipmap)
-
-
- Do FlipMap with specific ID
-
-
-
- Parameters:
-
- flipmap
- int
- (ID of flipmap)
-
-
-
-
-
-
-
-
-
-
- PlayFlyBy(flyby)
-
-
- Enable FlyBy with specific ID
-
-
-
- Parameters:
-
- flyby
- short
- (ID of flyby)
-
-
-
-
-
-
-
-
-
-
- CalculateDistance(posA, posB)
-
-
- Calculate the distance between two positions.
-
-
-
- Parameters:
-
- posA
- Vec3
- first position
-
- posB
- Vec3
- second position
-
-
-
- Returns:
-
-
- int
- the direct distance from one position to the other
-
-
-
-
-
-
-
-
- CalculateHorizontalDistance(posA, posB)
-
-
- Calculate the horizontal distance between two positions.
-
-
-
- Parameters:
-
- posA
- Vec3
- first position
-
- posB
- Vec3
- second position
-
-
-
- Returns:
-
-
- int
- the direct distance on the XZ plane from one position to the other
-
-
-
-
-
-
-
-
- PercentToScreen(x, y)
-
-
- Translate a pair of percentages to screen-space pixel coordinates.
-To be used with Strings.DisplayString:SetPosition and Strings.DisplayString .
-
-
-
- Parameters:
-
- x
- float
- percent value to translate to x-coordinate
-
- y
- float
- percent value to translate to y-coordinate
-
-
-
- Returns:
-
-
- int
- x coordinate in pixels
-
- int
- y coordinate in pixels
-
-
-
-
- Usage:
- local halfwayX, halfwayY = PercentToScreen(50 , 50 )
-local baddy
-local spawnLocationNullmesh = GetMoveableByName("position_behind_left_pillar" )
-local str1 = DisplayString("You spawned a baddy!" , halfwayX, halfwayY, Color(255 , 100 , 100 ), false , {DisplayStringOption.SHADOW, DisplayStringOption.CENTER})
-
-LevelFuncs.triggerOne = function (obj)
- ShowString(str1, 4 )
-end
-
-
-
-
-
- ScreenToPercent(x, y)
-
-
- Translate a pair of coordinates to percentages of window dimensions.
-To be used with Strings.DisplayString:GetPosition .
-
-
-
- Parameters:
-
- x
- int
- pixel value to translate to a percentage of the window width
-
- y
- int
- pixel value to translate to a percentage of the window height
-
-
-
- Returns:
-
-
- float
- x coordinate as percentage
-
- float
- y coordinate as percentage
-
-
-
-
-
-
-
-
- ResetObjCamera()
-
-
- Reset object camera back to Lara and deactivate object camera.
-
-
-
-
-
-
-
-
-
-
-
- PrintLog(message, logLevel[, allowSpam])
-
-
- Write messages within the Log file
-
- For native Lua handling of errors, see the official Lua website:
-
-Error management
-
-debug.traceback
-
-
- Parameters:
-
- message
- string
- to be displayed within the Log
-
- logLevel
- LogLevel
- log level to be displayed
-
- allowSpam
- bool
- true allows spamming of the message
- (optional )
-
-
-
-
-
-
- Usage:
- PrintLog('test info log' , LogLevel.INFO)
-PrintLog('test warning log' , LogLevel.WARNING)
-PrintLog('test error log' , LogLevel.ERROR)
-PrintLog('test spam log' , LogLevel.INFO, true )
-
-
-
-
-
- Vibrate(strength, time)
-
-
- Vibrate gamepad, if possible.
-
-
-
- Parameters:
-
- strength
- float
-
-
-
-
- time
- float
- (in seconds, default: 0.3)
-
-
-
-
-
-
-
-
-
-
- KeyIsHeld(action)
-
-
- Check if particular action key is held
-
-
-
- Parameters:
-
- action
- ActionID
- action mapping index to check
-
-
-
-
-
-
-
-
-
-
- KeyIsHit(action)
-
-
- Check if particular action key was hit (once)
-
-
-
- Parameters:
-
- action
- ActionID
- action mapping index to check
-
-
-
-
-
-
-
-
-
-
- KeyPush(action)
-
-
- Emulate pushing of a certain action key
-
-
-
- Parameters:
-
- action
- ActionID
- action mapping index to push
-
-
-
-
-
-
-
-
-
-
- KeyClear(action)
-
-
- Clears particular input from action key
-
-
-
- Parameters:
-
- action
- ActionID
- action mapping index to clear
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Documentation/doc/1 modules/Strings.html b/Documentation/doc/1 modules/Strings.html
index 597b921ff..dfb0f1204 100644
--- a/Documentation/doc/1 modules/Strings.html
+++ b/Documentation/doc/1 modules/Strings.html
@@ -160,7 +160,7 @@ Default: nil (i.e. infinite)
should be string automatically deleted after timeout is reached.
If not given, the string will remain allocated even after timeout is reached, and can be
shown again without re-initialization.
-Default: false
+Default: true
diff --git a/Documentation/doc/2 classes/DisplaySprite.html b/Documentation/doc/2 classes/DisplaySprite.html
deleted file mode 100644
index f40c12cab..000000000
--- a/Documentation/doc/2 classes/DisplaySprite.html
+++ /dev/null
@@ -1,529 +0,0 @@
-
-
-
-
- TombEngine 1.1.0 Lua API
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
TombEngine
-
-
-
-
1 Modules
-
-
2 Classes
-
-
3 Primitive Classes
-
-
4 Enums
-
-
5 Lua utility modules
-
-
-
-
-
-
-
Class DisplaySprite
-
Represents a screen-space display sprite.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- DisplaySprite(ID, int, pos, rot, scale[, color])
-
-
- Create a DisplaySprite object.
-
-
-
- Parameters:
-
- ID
- ObjID
- of the sprite sequence object.
-
- int
- int
- spriteID ID of the sprite in the sequence.
-
- pos
- Vec2
- Display position in percent.
-
- rot
- float
- Rotation in degrees.
-
- scale
- Vec2
- Horizontal and vertical scale in percent. Scaling is interpreted by the DisplaySpriteEnum.ScaleMode passed to the Draw() function call.
-
- color
- Color
- Color. Default: Color(255, 255, 255, 255)
- (optional )
-
-
-
- Returns:
-
-
- DisplaySprite
- A new DisplaySprite object.
-
-
-
-
-
-
-
-
- DisplaySprite:GetObjectID()
-
-
- Get the object ID of the sprite sequence object used by the display sprite. ()
-
-
-
-
- Returns:
-
-
- ObjID
- Sprite sequence object ID.
-
-
-
-
-
-
-
-
- DisplaySprite:GetSpriteID()
-
-
- Get the sprite ID in the sprite sequence object used by the display sprite. ()
-
-
-
-
- Returns:
-
-
- int
- Sprite ID in the sprite sequence object.
-
-
-
-
-
-
-
-
- DisplaySprite:GetPosition()
-
-
- Get the display position of the display sprite in percent. ()
-
-
-
-
- Returns:
-
-
- Vec2
- Display position in percent.
-
-
-
-
-
-
-
-
- DisplaySprite:GetRotation()
-
-
- Get the rotation of the display sprite in degrees. ()
-
-
-
-
- Returns:
-
-
- float
- Rotation in degrees.
-
-
-
-
-
-
-
-
- DisplaySprite:GetScale()
-
-
- Get the horizontal and vertical scale of the display sprite in percent. ()
-
-
-
-
- Returns:
-
-
- Vec2
- Horizontal and vertical scale in percent.
-
-
-
-
-
-
-
-
- DisplaySprite:GetColor()
-
-
- Get the color of the display sprite. ()
-
-
-
-
- Returns:
-
-
- Color
- Color.
-
-
-
-
-
-
-
-
- DisplaySprite:SetObjectID(New)
-
-
- Set the sprite sequence object ID used by the display sprite. (Objects.ObjID)
-
-
-
- Parameters:
-
- New
- ObjID
- sprite sequence object ID.
-
-
-
-
-
-
-
-
-
-
- DisplaySprite:SetSpriteID(New)
-
-
- Set the sprite ID in the sprite sequence object used by the display sprite. (int)
-
-
-
- Parameters:
-
- New
- int
- sprite ID in the sprite sequence object.
-
-
-
-
-
-
-
-
-
-
- DisplaySprite:SetPosition(New)
-
-
- Set the display position of the display sprite in percent. (Vec2)
-
-
-
- Parameters:
-
- New
- Vec2
- display position in percent.
-
-
-
-
-
-
-
-
-
-
- DisplaySprite:SetRotation(New)
-
-
- Set the rotation of the display sprite in degrees. (float)
-
-
-
- Parameters:
-
- New
- float
- rotation in degrees.
-
-
-
-
-
-
-
-
-
-
- DisplaySprite:SetScale(New)
-
-
- Set the horizontal and vertical scale of the display sprite in percent. (Vec2)
-
-
-
- Parameters:
-
- New
- float
- horizontal and vertical scale in percent.
-
-
-
-
-
-
-
-
-
-
- DisplaySprite:SetColor(New)
-
-
- Set the color of the display sprite. (Color)
-
-
-
- Parameters:
-
- New
- float
- color.
-
-
-
-
-
-
-
-
-
-
- DisplaySprite:Draw([priority][, alignMode][, scaleMode][, blendMode])
-
-
- Draw the display sprite in display space for the current frame.
-
-
-
- Parameters:
-
- priority
- ObjID
- Draw priority. Can be thought of as a layer, with higher values having precedence. Default: 0
- (optional )
-
- alignMode
- AlignMode
- Align mode interpreting an offset from the sprite's position. Default: DisplaySprite.AlignMode.CENTER
- (optional )
-
- scaleMode
- ScaleMode
- Scale mode interpreting the display sprite's horizontal and vertical scale. Default: DisplaySprite.ScaleMode.FIT
- (optional )
-
- blendMode
- BlendID
- Blend mode. Default: Effects.BlendID.ALPHABLEND
- (optional )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Documentation/doc/2 classes/Flow.Level.html b/Documentation/doc/2 classes/Flow.Level.html
index 7872a908d..6fa508fd0 100644
--- a/Documentation/doc/2 classes/Flow.Level.html
+++ b/Documentation/doc/2 classes/Flow.Level.html
@@ -110,6 +110,10 @@
+
+ nameKey
+ (string) string key for the level's (localised) name.
+
scriptFile
(string) Level-specific Lua script file.
@@ -206,6 +210,22 @@
+
+
+ nameKey
+
+
+ (string) string key for the level's (localised) name.
+ Corresponds to an entry in strings.lua.
+
+
+
+
+
+
+
+
+
scriptFile
diff --git a/Documentation/doc/3 primitive classes/Rotation.html b/Documentation/doc/3 primitive classes/Rotation.html
index 9fffd646f..fc7ffcb82 100644
--- a/Documentation/doc/3 primitive classes/Rotation.html
+++ b/Documentation/doc/3 primitive classes/Rotation.html
@@ -130,6 +130,10 @@
+
+
+ Direction()
+ Converts rotation to a direction normal.
__tostring(rotation)
@@ -232,6 +236,27 @@
+
+
+
+ Direction()
+
+
+ Converts rotation to a direction normal.
+
+
+
+
+ Returns:
+
+
+ Vec3
+ resulting normal calculated from this rotation.
+
+
+
+
+
diff --git a/Documentation/doc/4 enums/Flow.BreakMode.html b/Documentation/doc/4 enums/Flow.BreakMode.html
deleted file mode 100644
index db34cabb1..000000000
--- a/Documentation/doc/4 enums/Flow.BreakMode.html
+++ /dev/null
@@ -1,166 +0,0 @@
-
-
-
-
- TombEngine 1.5 Lua API
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
TombEngine
-
-
-
-
1 Modules
-
-
2 Classes
-
-
3 Primitive Classes
-
-
4 Enums
-
-
5 Lua utility modules
-
-
-
-
-
-
-
Enum Flow.BreakMode
-
Constants for break modes.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
The following constants are inside Flow.BreakMode.
-
-
NONE
-FULL
-SPECTATOR
-PLAYER
-
-
-
-
-
-
-
- CONSTANT_STRING_HERE
-
-
- Table of break modes.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Documentation/doc/4 enums/Flow.GameMode.html b/Documentation/doc/4 enums/Flow.GameMode.html
deleted file mode 100644
index d6b98b125..000000000
--- a/Documentation/doc/4 enums/Flow.GameMode.html
+++ /dev/null
@@ -1,165 +0,0 @@
-
-
-
-
- TombEngine 1.5 Lua API
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
TombEngine
-
-
-
-
1 Modules
-
-
2 Classes
-
-
3 Primitive Classes
-
-
4 Enums
-
-
5 Lua utility modules
-
-
-
-
-
-
-
Enum Flow.GameMode
-
Constants for game modes.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
The following constants are inside Flow.GameMode.
-
-
NORMAL
-FROZEN
-MENU
-
-
-
-
-
-
-
- CONSTANT_STRING_HERE
-
-
- Table of game modes.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Documentation/doc/4 enums/Misc.ActionID.html b/Documentation/doc/4 enums/Misc.ActionID.html
deleted file mode 100644
index 9aa746fde..000000000
--- a/Documentation/doc/4 enums/Misc.ActionID.html
+++ /dev/null
@@ -1,195 +0,0 @@
-
-
-
-
- TombEngine 1.1.0 Lua API
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
TombEngine
-
-
-
-
1 Modules
-
-
2 Classes
-
-
3 Primitive Classes
-
-
4 Enums
-
-
5 Lua utility modules
-
-
-
-
-
-
-
Enum Misc.ActionID
-
Constants for action key IDs.
-
-
-
-
-
-
-
-
- CONSTANT_STRING_HERE
- Table of action ID constants (for use with KeyIsHeld / KeyIsHit / etc commands).
-
-
-
-
-
-
-
-
-
-
-
-
-
-
The following constants are inside ActionID.
-
-
FORWARD
-BACK
-LEFT
-RIGHT
-STEP_LEFT
-STEP_RIGHT
-WALK
-SPRINT
-CROUCH
-JUMP
-ROLL
-ACTION
-DRAW
-LOOK
-
-ACCELERATE
-REVERSE
-SPEED
-SLOW
-BRAKE
-FIRE
-
-FLARE
-SMALL_MEDIPACK
-LARGE_MEDIPACK
-PREVIOUS_WEAPON
-NEXT_WEAPON
-WEAPON_1
-WEAPON_2
-WEAPON_3
-WEAPON_4
-WEAPON_5
-WEAPON_6
-WEAPON_7
-WEAPON_8
-WEAPON_9
-WEAPON_10
-
-SELECT
-DESELECT
-PAUSE
-INVENTORY
-SAVE
-LOAD
-
-
-
-
-
-
-
- CONSTANT_STRING_HERE
-
-
- Table of action ID constants (for use with KeyIsHeld / KeyIsHit / etc commands).
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Documentation/doc/4 enums/Misc.CameraType.html b/Documentation/doc/4 enums/Misc.CameraType.html
deleted file mode 100644
index 5c57ac2d6..000000000
--- a/Documentation/doc/4 enums/Misc.CameraType.html
+++ /dev/null
@@ -1,157 +0,0 @@
-
-
-
-
- TombEngine 1.1.0 Lua API
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
TombEngine
-
-
-
-
1 Modules
-
-
2 Classes
-
-
3 Primitive Classes
-
-
4 Enums
-
-
5 Lua utility modules
-
-
-
-
-
-
-
Enum Misc.CameraType
-
Constants for the type of the Camera.
-
-
-
-
-
-
-
-
- CONSTANT_STRING_HERE
- Table of camera type constants (for use with GetCameraType() function).
-
-
-
-
-
-
-
-
-
-
-
-
-
-
The following constants are inside CameraType.
-
-
CHASE
-FIXED
-LOOK
-COMBAT
-HEAVY
-OBJECT
-
-
-
-
-
-
-
- CONSTANT_STRING_HERE
-
-
- Table of camera type constants (for use with GetCameraType() function).
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Documentation/doc/4 enums/Misc.LogLevel.html b/Documentation/doc/4 enums/Misc.LogLevel.html
deleted file mode 100644
index 97a3f2081..000000000
--- a/Documentation/doc/4 enums/Misc.LogLevel.html
+++ /dev/null
@@ -1,153 +0,0 @@
-
-
-
-
- TombEngine 1.1.0 Lua API
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
TombEngine
-
-
-
-
1 Modules
-
-
2 Classes
-
-
3 Primitive Classes
-
-
4 Enums
-
-
5 Lua utility modules
-
-
-
-
-
-
-
Enum Misc.LogLevel
-
Constants for LogLevel IDs.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
The following constants are inside LogLevel.
-
-
INFO
-WARNING
-ERROR
-
-
-
-
-
-
-
- CONSTANT_STRING_HERE
-
-
- Table of LogLevel ID constants (for use with PrintLog() command).
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Documentation/doc/4 enums/Misc.SoundTrackType.html b/Documentation/doc/4 enums/Misc.SoundTrackType.html
deleted file mode 100644
index 31e0cd998..000000000
--- a/Documentation/doc/4 enums/Misc.SoundTrackType.html
+++ /dev/null
@@ -1,154 +0,0 @@
-
-
-
-
- TombEngine 1.1.0 Lua API
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
TombEngine
-
-
-
-
1 Modules
-
-
2 Classes
-
-
3 Primitive Classes
-
-
4 Enums
-
-
5 Lua utility modules
-
-
-
-
-
-
-
Enum Misc.SoundTrackType
-
Constants for the type of the audio tracks.
-
-
-
-
-
-
-
-
- CONSTANT_STRING_HERE
- Table of sound track type constants (for use with sound track functions).
-
-
-
-
-
-
-
-
-
-
-
-
-
-
The following constants are inside SoundTrackType.
-
-
ONESHOT
-LOOPED
-VOICE
-
-
-
-
-
-
-
- CONSTANT_STRING_HERE
-
-
- Table of sound track type constants (for use with sound track functions).
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/TombEngine/Game/Lara/lara_tests.cpp b/TombEngine/Game/Lara/lara_tests.cpp
index d2124d9ec..745d8d478 100644
--- a/TombEngine/Game/Lara/lara_tests.cpp
+++ b/TombEngine/Game/Lara/lara_tests.cpp
@@ -1778,7 +1778,7 @@ bool TestLaraPoleCollision(ItemInfo* item, CollisionInfo* coll, bool goingUp, fl
bool atLeastOnePoleCollided = false;
- auto collObjects = GetCollidedObjects(*item, true, false, BLOCK(1), ObjectCollectionMode::Items);
+ auto collObjects = GetCollidedObjects(*item, true, false, BLOCK(2), ObjectCollectionMode::Items);
if (!collObjects.IsEmpty())
{
auto laraBox = GameBoundingBox(item).ToBoundingOrientedBox(item->Pose);
diff --git a/TombEngine/Game/collision/collide_item.cpp b/TombEngine/Game/collision/collide_item.cpp
index 7970ec5dc..e9d74b743 100644
--- a/TombEngine/Game/collision/collide_item.cpp
+++ b/TombEngine/Game/collision/collide_item.cpp
@@ -118,7 +118,10 @@ CollidedObjectData GetCollidedObjects(ItemInfo& collidingItem, bool onlyVisible,
// Override extents if specified.
if (customRadius > 0.0f)
+ {
collidingAabb = BoundingBox(collidingItem.Pose.Position.ToVector3(), Vector3(customRadius));
+ convertedBounds.Extents = Vector3(customRadius);
+ }
// Run through neighboring rooms.
const auto& room = g_Level.Rooms[collidingItem.RoomNumber];
diff --git a/TombEngine/Game/collision/floordata.cpp b/TombEngine/Game/collision/floordata.cpp
index 30af7c2c8..29da6a4e2 100644
--- a/TombEngine/Game/collision/floordata.cpp
+++ b/TombEngine/Game/collision/floordata.cpp
@@ -381,8 +381,8 @@ namespace TEN::Collision::Floordata
// Calculate and return tilt.
auto sign = isFloor ? 1 : -1;
return Vector2i(
- round(scaledNormal.x * 4),
- round(scaledNormal.z * 4)) * sign;
+ (round(scaledNormal.x) * 4),
+ (round(scaledNormal.z) * 4)) * sign;
}
Vector2i GetSectorPoint(int x, int z)
diff --git a/TombEngine/Game/control/trigger.cpp b/TombEngine/Game/control/trigger.cpp
index 44e8c9119..ac47e9d19 100644
--- a/TombEngine/Game/control/trigger.cpp
+++ b/TombEngine/Game/control/trigger.cpp
@@ -370,7 +370,6 @@ void Trigger(short const value, short const flags)
if (item->Flags & IFLAG_KILLED)
return;
- item->TouchBits = NO_JOINT_BITS;
item->Flags |= TRIGGERED;
if (flags & ONESHOT)
@@ -408,6 +407,7 @@ void Trigger(short const value, short const flags)
}
item->Status = ITEM_ACTIVE;
+ item->TouchBits = NO_JOINT_BITS;
item->DisableInterpolation = true;
}
}
diff --git a/TombEngine/Game/effects/effects.cpp b/TombEngine/Game/effects/effects.cpp
index 9a47a93cb..80566e8da 100644
--- a/TombEngine/Game/effects/effects.cpp
+++ b/TombEngine/Game/effects/effects.cpp
@@ -1247,19 +1247,20 @@ void KillAllCurrentItems(short itemNumber)
// TODO: Reimplement this functionality.
}
-// TODO: Rename to SpawnDynamicLight().
-void TriggerDynamicLight(const Vector3& pos, const Color& color, float falloff)
+void TriggerDynamicPointLight(const Vector3& pos, const Color& color, float falloff, bool castShadows, int hash)
{
- g_Renderer.AddDynamicLight(
- pos.x, pos.y, pos.z,
- falloff * UCHAR_MAX,
- color.x * UCHAR_MAX, color.y * UCHAR_MAX, color.z * UCHAR_MAX);
+ g_Renderer.AddDynamicPointLight(pos, falloff , color, castShadows, hash);
+}
+
+void TriggerDynamicSpotLight(const Vector3& pos, const Vector3& dir, const Color& color, float radius, float falloff, float distance, bool castShadows, int hash)
+{
+ g_Renderer.AddDynamicSpotLight(pos, dir, radius, falloff, distance, color, castShadows, hash);
}
// Deprecated. Use above version instead.
void TriggerDynamicLight(int x, int y, int z, short falloff, byte r, byte g, byte b)
{
- g_Renderer.AddDynamicLight(x, y, z, falloff, r, g, b);
+ g_Renderer.AddDynamicPointLight(Vector3(x, y, z), (float)(falloff * UCHAR_MAX), Color(r / (float)UCHAR_MAX, g / (float)UCHAR_MAX, b / (float)UCHAR_MAX), false);
}
void SpawnPlayerWaterSurfaceEffects(const ItemInfo& item, int waterHeight, int waterDepth)
diff --git a/TombEngine/Game/effects/effects.h b/TombEngine/Game/effects/effects.h
index e980a8d22..3da3f6ab9 100644
--- a/TombEngine/Game/effects/effects.h
+++ b/TombEngine/Game/effects/effects.h
@@ -300,6 +300,8 @@ void ControlWaterfallMist(short itemNumber);
void TriggerWaterfallMist(const ItemInfo& item);
void KillAllCurrentItems(short itemNumber);
void TriggerDynamicLight(int x, int y, int z, short falloff, byte r, byte g, byte b);
+void TriggerDynamicPointLight(const Vector3& pos, const Color& color, float falloff, bool castShadows = false, int hash = 0);
+void TriggerDynamicSpotLight(const Vector3& pos, const Vector3& dir, const Color& color, float radius, float falloff, float distance, bool castShadows = false, int hash = 0);
void TriggerRocketFlame(int x, int y, int z, int xv, int yv, int zv, int itemNumber);
void TriggerRocketSmoke(int x, int y, int z);
void TriggerFlashSmoke(int x, int y, int z, short roomNumber);
@@ -313,5 +315,3 @@ void TriggerExplosionBubbles(int x, int y, int z, short roomNumber);
void Ricochet(Pose& pos);
void ProcessEffects(ItemInfo* item);
void UpdateWibble();
-
-void TriggerDynamicLight(const Vector3& pos, const Color& color, float falloff);
diff --git a/TombEngine/Game/effects/tomb4fx.cpp b/TombEngine/Game/effects/tomb4fx.cpp
index ef50c0418..46c46d2e4 100644
--- a/TombEngine/Game/effects/tomb4fx.cpp
+++ b/TombEngine/Game/effects/tomb4fx.cpp
@@ -1383,7 +1383,7 @@ void UpdateShockwaves()
{
auto lightColor = Color(shockwave.r / (float)UCHAR_MAX, shockwave.g / (float)UCHAR_MAX, shockwave.b / (float)UCHAR_MAX);
auto pos = Vector3(shockwave.x, shockwave.y, shockwave.z);
- TriggerDynamicLight(pos, lightColor, shockwave.life / (float)UCHAR_MAX);
+ TriggerDynamicPointLight(pos, lightColor, shockwave.life / 4.0f);
}
if (shockwave.style != (int)ShockwaveStyle::Knockback)
diff --git a/TombEngine/Objects/TR2/Entity/Bartoli.cpp b/TombEngine/Objects/TR2/Entity/Bartoli.cpp
index 1eb6b0287..3eedc9838 100644
--- a/TombEngine/Objects/TR2/Entity/Bartoli.cpp
+++ b/TombEngine/Objects/TR2/Entity/Bartoli.cpp
@@ -74,28 +74,26 @@ namespace TEN::Entities::Creatures::TR2
// Spawn light.
auto lightPos = item.Pose.Position.ToVector3() + Vector3(0.0f, -CLICK(1), 0.0f);
auto lightColor = Color(0.0f,Random::GenerateFloat(0.7f, 1.0f), Random::GenerateFloat(0.2f, 0.3f));
- float lightFalloff = Random::GenerateFloat(0.1f, 0.2f);
- float transformationLightFalloff = (0.5, 1.0);
- TriggerDynamicLight(lightPos, lightColor, lightFalloff);
+ TriggerDynamicPointLight(lightPos, lightColor, Random::GenerateFloat(BLOCK(6), BLOCK(12)));
// Handle transformation.
if (effectTimer == timeExplosion1)
{
- TriggerDynamicLight(lightPos, lightColor, transformationLightFalloff);
+ TriggerDynamicPointLight(lightPos, lightColor, Random::GenerateFloat(BLOCK(12), BLOCK(24)));
SpawnBartoliTransformEffect(item, ID_SPHERE_OF_DOOM);
SoundEffect(SFX_TR2_MARCO_BARTOLLI_TRANSFORM, &item.Pose);
}
if (effectTimer == timeExplosion2)
{
- TriggerDynamicLight(lightPos, lightColor, transformationLightFalloff);
+ TriggerDynamicPointLight(lightPos, lightColor, Random::GenerateFloat(BLOCK(12), BLOCK(24)));
SpawnBartoliTransformEffect(item, ID_SPHERE_OF_DOOM2);
SoundEffect(SFX_TR2_MARCO_BARTOLLI_TRANSFORM, &item.Pose);
}
if (effectTimer == timeExplosion3)
{
- TriggerDynamicLight(lightPos, lightColor, transformationLightFalloff);
+ TriggerDynamicPointLight(lightPos, lightColor, Random::GenerateFloat(BLOCK(12), BLOCK(24)));
SpawnBartoliTransformEffect(item, ID_SPHERE_OF_DOOM3);
SoundEffect(SFX_TR2_MARCO_BARTOLLI_TRANSFORM, &item.Pose);
item.Animation.FrameNumber = animationFrameEnd;
diff --git a/TombEngine/Objects/TR2/Entity/Dragon.cpp b/TombEngine/Objects/TR2/Entity/Dragon.cpp
index 90e90a51c..893b5ff2b 100644
--- a/TombEngine/Objects/TR2/Entity/Dragon.cpp
+++ b/TombEngine/Objects/TR2/Entity/Dragon.cpp
@@ -193,9 +193,9 @@ namespace TEN::Entities::Creatures::TR2
Random::GenerateFloat(0.8f, 0.9f),
Random::GenerateFloat(0.4f, 0.5f),
Random::GenerateFloat(0.2f, 0.3f));
- float falloff = Random::GenerateFloat(0.1f, 0.4f);
+ float falloff = Random::GenerateFloat(BLOCK(6), BLOCK(20));
- TriggerDynamicLight(pos, color, falloff);
+ TriggerDynamicPointLight(pos, color, falloff);
}
break;
@@ -205,9 +205,9 @@ namespace TEN::Entities::Creatures::TR2
Random::GenerateFloat(0.8f, 0.9f),
Random::GenerateFloat(0.2f, 0.3f),
Random::GenerateFloat(0.0f, 0.1f));
- float falloff = Random::GenerateFloat(0.1f, 0.2f);
+ float falloff = Random::GenerateFloat(BLOCK(6), BLOCK(12));
- TriggerDynamicLight(pos, color, falloff);
+ TriggerDynamicPointLight(pos, color, falloff);
}
break;
}
diff --git a/TombEngine/Objects/TR3/Entity/SealMutant.cpp b/TombEngine/Objects/TR3/Entity/SealMutant.cpp
index 0050fa143..aa297dbe2 100644
--- a/TombEngine/Objects/TR3/Entity/SealMutant.cpp
+++ b/TombEngine/Objects/TR3/Entity/SealMutant.cpp
@@ -178,8 +178,8 @@ namespace TEN::Entities::Creatures::TR3
{
auto pos = GetJointPosition(item, SealMutantGasBite.BoneID, Vector3(0.0f, -SEAL_MUTANT_FLAME_LIGHT_Y_OFFSET, 0.0f));
auto color = Color(Random::GenerateFloat(0.75f, 1.0f), Random::GenerateFloat(0.4f, 0.5f), Random::GenerateFloat(0.0f, 0.25f));
- float falloff = Random::GenerateFloat(0.03f, 0.04f);
- TriggerDynamicLight(pos.ToVector3(), color, falloff);
+ float falloff = Random::GenerateFloat(BLOCK(1.5f), BLOCK(2.5f));
+ TriggerDynamicPointLight(pos.ToVector3(), color, falloff);
}
}
else if (TestAnimFrameRange(item, 1, 124))
diff --git a/TombEngine/Objects/TR4/Entity/tr4_demigod.cpp b/TombEngine/Objects/TR4/Entity/tr4_demigod.cpp
index 4b4e1fe91..c7dbbca90 100644
--- a/TombEngine/Objects/TR4/Entity/tr4_demigod.cpp
+++ b/TombEngine/Objects/TR4/Entity/tr4_demigod.cpp
@@ -713,7 +713,7 @@ namespace TEN::Entities::TR4
TriggerShockwave((Pose*)&pos, 24, 88, 200, 128, 128, 128, 32, EulerAngles::Identity, 8, true, false, true, (int)ShockwaveStyle::Normal);
auto lightColor = Color(1.0f, 0.4f, 0.2f);
- TriggerDynamicLight(pos.ToVector3(), lightColor, 0.1f);
+ TriggerDynamicPointLight(pos.ToVector3(), lightColor, BLOCK(6));
Camera.bounce = -128;
diff --git a/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp b/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp
index 397276556..ef4c8e1d0 100644
--- a/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp
+++ b/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp
@@ -600,7 +600,7 @@ namespace TEN::Entities::Creatures::TR5
TriggerShockwave(&Pose(pos1), 16, 160, 64, 0, color / 2, color, 48, EulerAngles::Identity, 1, true, false, true, (int)ShockwaveStyle::Normal);
auto lightColor = Color(0.4f, 0.3f, 0.0f);
- TriggerDynamicLight(pos.ToVector3(), lightColor, 0.04f);
+ TriggerDynamicPointLight(pos.ToVector3(), lightColor, BLOCK(2.5f));
}
deltaFrame = item->Animation.FrameNumber - GetAnimData(item).frameBase;
@@ -629,7 +629,7 @@ namespace TEN::Entities::Creatures::TR5
if (item->ItemFlags[3])
{
auto lightColor = Color(0.0f, 0.4f, 1.0f);
- TriggerDynamicLight(pos.ToVector3(), lightColor, 0.06f);
+ TriggerDynamicPointLight(pos.ToVector3(), lightColor, BLOCK(4));
}
}
}
diff --git a/TombEngine/Objects/TR5/Trap/LaserBeam.cpp b/TombEngine/Objects/TR5/Trap/LaserBeam.cpp
index 83574e637..bf7252333 100644
--- a/TombEngine/Objects/TR5/Trap/LaserBeam.cpp
+++ b/TombEngine/Objects/TR5/Trap/LaserBeam.cpp
@@ -74,10 +74,10 @@ namespace TEN::Entities::Traps
static void SpawnLaserBeamLight(const Vector3& pos, int roomNumber, const Color& color, float intensity, float amplitudeMax)
{
- constexpr auto FALLOFF = 0.03f;
+ constexpr auto LASER_BEAM_FALLOFF = BLOCK(1.5f);
float intensityNorm = intensity - Random::GenerateFloat(0.0f, amplitudeMax);
- TriggerDynamicLight(pos, color * intensityNorm, FALLOFF);
+ TriggerDynamicPointLight(pos, color * intensityNorm, LASER_BEAM_FALLOFF);
}
void LaserBeamEffect::StoreInterpolationData()
diff --git a/TombEngine/Renderer/Renderer.cpp b/TombEngine/Renderer/Renderer.cpp
index bb54ef41e..f55770a2e 100644
--- a/TombEngine/Renderer/Renderer.cpp
+++ b/TombEngine/Renderer/Renderer.cpp
@@ -32,8 +32,6 @@ namespace TEN::Renderer
void Renderer::FreeRendererData()
{
- _shadowLight = nullptr;
-
_items.resize(0);
_effects.resize(0);
_moveableObjects.resize(0);
@@ -47,6 +45,12 @@ namespace TEN::Renderer
_animatedTextures.resize(0);
_animatedTextureSets.resize(0);
+ _shadowLight = nullptr;
+
+ _dynamicLightList = 0;
+ for (auto& dynamicLightList : _dynamicLights)
+ dynamicLightList.resize(0);
+
for (auto& mesh : _meshes)
delete mesh;
_meshes.resize(0);
@@ -195,12 +199,23 @@ namespace TEN::Renderer
}
_context->PSSetSamplers((UINT)registerType, 1, &samplerState);
- }
+ }
+
+ void Renderer::BindLight(RendererLight& light, ShaderLight* lights, int index)
+ {
+ memcpy(&lights[index], &light, sizeof(ShaderLight));
+
+ if (light.Hash == 0)
+ return;
+
+ lights[index].Position = Vector3::Lerp(light.PrevPosition, light.Position, GetInterpolationFactor());
+ lights[index].Direction = Vector3::Lerp(light.PrevDirection, light.Direction, GetInterpolationFactor());
+ }
void Renderer::BindRoomLights(std::vector& lights)
{
for (int i = 0; i < lights.size(); i++)
- memcpy(&_stRoom.RoomLights[i], lights[i], sizeof(ShaderLight));
+ BindLight(*lights[i], _stRoom.RoomLights, i);
_stRoom.NumRoomLights = (int)lights.size();
}
@@ -208,7 +223,7 @@ namespace TEN::Renderer
void Renderer::BindStaticLights(std::vector& lights)
{
for (int i = 0; i < lights.size(); i++)
- memcpy(&_stStatic.Lights[i], lights[i], sizeof(ShaderLight));
+ BindLight(*lights[i], _stStatic.Lights, i);
_stStatic.NumLights = (int)lights.size();
}
@@ -216,7 +231,7 @@ namespace TEN::Renderer
void Renderer::BindInstancedStaticLights(std::vector& lights, int instanceID)
{
for (int i = 0; i < lights.size(); i++)
- memcpy(&_stInstancedStaticMeshBuffer.StaticMeshes[instanceID].Lights[i], lights[i], sizeof(ShaderLight));
+ BindLight(*lights[i], _stInstancedStaticMeshBuffer.StaticMeshes[instanceID].Lights, i);
_stInstancedStaticMeshBuffer.StaticMeshes[instanceID].NumLights = (int)lights.size();
}
@@ -242,7 +257,7 @@ namespace TEN::Renderer
if (fadedCoeff == 0.0f)
continue;
- memcpy(&_stItem.Lights[numLights], lights[i], sizeof(ShaderLight));
+ BindLight(*lights[i], _stItem.Lights, numLights);
_stItem.Lights[numLights].Intensity *= fadedCoeff;
numLights++;
}
diff --git a/TombEngine/Renderer/Renderer.h b/TombEngine/Renderer/Renderer.h
index 02a5f9a2f..6e733a52f 100644
--- a/TombEngine/Renderer/Renderer.h
+++ b/TombEngine/Renderer/Renderer.h
@@ -245,7 +245,8 @@ namespace TEN::Renderer
// Lights
- std::vector _dynamicLights;
+ int _dynamicLightList = 0;
+ std::vector _dynamicLights[2];
RendererLight* _shadowLight;
// Lines
@@ -417,6 +418,7 @@ namespace TEN::Renderer
void ApplySMAA(RenderTarget2D* renderTarget, RenderView& view);
void ApplyFXAA(RenderTarget2D* renderTarget, RenderView& view);
void BindTexture(TextureRegister registerType, TextureBase* texture, SamplerStateRegister samplerType);
+ void BindLight(RendererLight& light, ShaderLight* lights, int index);
void BindRoomLights(std::vector& lights);
void BindStaticLights(std::vector& lights);
void BindInstancedStaticLights(std::vector& lights, int instanceID);
@@ -643,7 +645,9 @@ namespace TEN::Renderer
void AddString(const std::string& string, const Vector2& pos, const Color& color, float scale, int flags);
void AddDebugString(const std::string& string, const Vector2& pos, const Color& color, float scale, RendererDebugPage page = RendererDebugPage::None);
void FreeRendererData();
- void AddDynamicLight(int x, int y, int z, short falloff, byte r, byte g, byte b);
+ void AddDynamicPointLight(const Vector3& pos, float radius, const Color& color, bool castShadows, int hash = 0);
+ void AddDynamicSpotLight(const Vector3& pos, const Vector3& dir, float radius, float falloff, float distance, const Color& color, bool castShadows, int hash = 0);
+ void StoreInterpolatedDynamicLightData(RendererLight& light);
void RenderLoadingScreen(float percentage);
void RenderFreezeMode(float interpFactor, bool staticBackground);
void UpdateProgress(float value);
diff --git a/TombEngine/Renderer/RendererCompatibility.cpp b/TombEngine/Renderer/RendererCompatibility.cpp
index 5f5be02ef..83e4408b0 100644
--- a/TombEngine/Renderer/RendererCompatibility.cpp
+++ b/TombEngine/Renderer/RendererCompatibility.cpp
@@ -34,6 +34,10 @@ namespace TEN::Renderer
_meshes.clear();
+ _dynamicLightList = 0;
+ for (auto& dynamicLightList : _dynamicLights)
+ dynamicLightList.clear();
+
int allocatedItemSize = (int)g_Level.Items.size() + MAX_SPAWNED_ITEM_COUNT;
auto item = RendererItem();
diff --git a/TombEngine/Renderer/RendererDraw.cpp b/TombEngine/Renderer/RendererDraw.cpp
index 7f5ac704d..3cc21fec1 100644
--- a/TombEngine/Renderer/RendererDraw.cpp
+++ b/TombEngine/Renderer/RendererDraw.cpp
@@ -148,8 +148,10 @@ namespace TEN::Renderer
SetBlendMode(BlendMode::Opaque);
SetCullMode(CullMode::CounterClockwise);
- int steps = _shadowLight->Type == LightType::Point ? 6 : 1;
- for (int step = 0; step < steps; step++)
+ auto shadowLightPos = _shadowLight->Hash == 0 ? _shadowLight->Position :
+ Vector3::Lerp(_shadowLight->PrevPosition, _shadowLight->Position, GetInterpolationFactor());
+
+ for (int step = 0; step < 6; step++)
{
// Bind render target
_context->OMSetRenderTargets(1, _shadowMap.RenderTargetView[step].GetAddressOf(),
@@ -158,7 +160,7 @@ namespace TEN::Renderer
_context->RSSetViewports(1, &_shadowMapViewport);
ResetScissor();
- if (_shadowLight->Position == item->Position)
+ if (shadowLightPos == item->Position)
return;
UINT stride = sizeof(Vertex);
@@ -178,27 +180,11 @@ namespace TEN::Renderer
BindTexture(TextureRegister::NormalMap, &std::get<1>(_moveablesTextures[0]), SamplerStateRegister::AnisotropicClamp);
// Set camera matrices
- Matrix view;
- Matrix projection;
- if (_shadowLight->Type == LightType::Point)
- {
- view = Matrix::CreateLookAt(_shadowLight->Position, _shadowLight->Position +
- RenderTargetCube::forwardVectors[step] * BLOCK(10),
- RenderTargetCube::upVectors[step]);
+ Matrix view = Matrix::CreateLookAt(shadowLightPos, shadowLightPos +
+ RenderTargetCube::forwardVectors[step] * BLOCK(10),
+ RenderTargetCube::upVectors[step]);
- projection = Matrix::CreatePerspectiveFieldOfView(90.0f * PI / 180.0f, 1.0f, 16.0f, _shadowLight->Out);
-
- }
- else if (_shadowLight->Type == LightType::Spot)
- {
- view = Matrix::CreateLookAt(_shadowLight->Position,
- _shadowLight->Position + _shadowLight->Direction * BLOCK(10),
- Vector3(0.0f, -1.0f, 0.0f));
-
- // Vertex lighting fades out in 1024-steps. increase angle artificially for a bigger blend radius.
- float projectionAngle = _shadowLight->OutRange * 1.5f * (PI / 180.0f);
- projection = Matrix::CreatePerspectiveFieldOfView(projectionAngle, 1.0f, 16.0f, _shadowLight->Out);
- }
+ Matrix projection = Matrix::CreatePerspectiveFieldOfView(90.0f * PI / 180.0f, 1.0f, 16.0f, _shadowLight->Out);
CCameraMatrixBuffer shadowProjection;
shadowProjection.ViewProjection = view * projection;
@@ -1536,41 +1522,102 @@ namespace TEN::Renderer
AddDebugSphere(sphere.Center, sphere.Radius, color, page, isWireframe);
}
- void Renderer::AddDynamicLight(int x, int y, int z, short falloff, byte r, byte g, byte b)
+ void Renderer::AddDynamicSpotLight(const Vector3& pos, const Vector3& dir, float radius, float falloff, float distance, const Color& color, bool castShadows, int hash)
{
if (_isLocked || g_GameFlow->LastFreezeMode != FreezeMode::None)
return;
RendererLight dynamicLight = {};
- if (falloff >= 8)
- {
- dynamicLight.Color = Vector3(r / 255.0f, g / 255.0f, b / 255.0f) * 2.0f;
- }
- else
- {
- r = (r * falloff) >> 3;
- g = (g * falloff) >> 3;
- b = (b * falloff) >> 3;
+ dynamicLight.Color = Vector3(color.x, color.y, color.z) * 2.0f;
+ if (falloff < 8)
+ dynamicLight.Color *= (falloff / 8.0f);
- dynamicLight.Color = Vector3(r / 255.0f, g / 255.0f, b / 255.0f) * 2.0f;
- }
+ // Calculate outer cone angle (degrees) based on radius at the cone's end distance.
+ float outerConeAngle = atan(radius / distance) * (180.0f / PI);
+ float innerConeAngle = atan((radius - falloff) / distance) * (180.0f / PI);
+ float innerDistance = std::max(0.0f, distance - distance * (falloff / radius));
+
+ // Normalize direction for safety.
+ auto normalizedDirection = dir;
+ normalizedDirection.Normalize();
dynamicLight.RoomNumber = NO_VALUE;
dynamicLight.Intensity = 1.0f;
- dynamicLight.Position = Vector3(float(x), float(y), float(z));
- dynamicLight.Out = falloff * 256.0f;
- dynamicLight.Type = LightType::Point;
- dynamicLight.BoundingSphere = BoundingSphere(dynamicLight.Position, dynamicLight.Out);
+ dynamicLight.Position = pos;
+ dynamicLight.Direction = normalizedDirection;
+ dynamicLight.In = innerDistance;
+ dynamicLight.Out = distance;
+ dynamicLight.InRange = innerConeAngle > 0.0f ? innerConeAngle : 0.5f;
+ dynamicLight.OutRange = outerConeAngle;
+ dynamicLight.Type = LightType::Spot;
+ dynamicLight.CastShadows = castShadows;
+ dynamicLight.BoundingSphere = BoundingSphere(pos, distance);
dynamicLight.Luma = Luma(dynamicLight.Color);
+ dynamicLight.Hash = hash;
- _dynamicLights.push_back(dynamicLight);
+ StoreInterpolatedDynamicLightData(dynamicLight);
+ _dynamicLights[_dynamicLightList].push_back(dynamicLight);
+ }
+
+ void Renderer::AddDynamicPointLight(const Vector3& pos, float radius, const Color& color, bool castShadows, int hash)
+ {
+ if (_isLocked || g_GameFlow->LastFreezeMode != FreezeMode::None)
+ return;
+
+ RendererLight dynamicLight = {};
+
+ dynamicLight.Color = Vector3(color.x, color.y, color.z) * 2.0f;
+ if (radius < BLOCK(2))
+ dynamicLight.Color *= (radius / BLOCK(2));
+
+ dynamicLight.RoomNumber = NO_VALUE;
+ dynamicLight.Intensity = 1.0f;
+ dynamicLight.Position = pos;
+ dynamicLight.In = 1.0f;
+ dynamicLight.Out = radius;
+ dynamicLight.Type = LightType::Point;
+ dynamicLight.CastShadows = castShadows;
+ dynamicLight.BoundingSphere = BoundingSphere(pos, radius);
+ dynamicLight.Luma = Luma(dynamicLight.Color);
+ dynamicLight.Hash = hash;
+
+ StoreInterpolatedDynamicLightData(dynamicLight);
+ _dynamicLights[_dynamicLightList].push_back(dynamicLight);
+ }
+
+ void Renderer::StoreInterpolatedDynamicLightData(RendererLight& light)
+ {
+ // Hash is not provided, do not search for same light in old buffer.
+ if (light.Hash == 0)
+ return;
+
+ // Determine the previous buffer index.
+ const auto& previousList = _dynamicLights[1 - _dynamicLightList];
+
+ // Find a light in the previous buffer with the same Hash.
+ auto it = std::find_if(previousList.begin(), previousList.end(),
+ [&light](const auto& prevLight)
+ {
+ return prevLight.Hash == light.Hash;
+ });
+
+ if (it == previousList.end())
+ return;
+
+ // If a matching light is found, copy its data.
+ const auto& prevLight = *it;
+ light.PrevPosition = prevLight.Position;
+ light.PrevDirection = prevLight.Direction;
}
void Renderer::PrepareScene()
{
if (g_GameFlow->CurrentFreezeMode == FreezeMode::None)
- _dynamicLights.clear();
+ {
+ _dynamicLightList ^= 1;
+ _dynamicLights[_dynamicLightList].clear();
+ }
_lines2DToDraw.clear();
_lines3DToDraw.clear();
@@ -1854,7 +1901,7 @@ namespace TEN::Renderer
_context->ClearDepthStencilView(_renderTarget.DepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
// HUD must be drawn before post-processing to be antialiased.
- if (renderMode == SceneRenderMode::Full)
+ if (renderMode == SceneRenderMode::Full && g_GameFlow->LastGameStatus == GameStatus::Normal)
g_Hud.Draw(*LaraItem);
if (renderMode != SceneRenderMode::NoPostprocess)
@@ -1889,7 +1936,7 @@ namespace TEN::Renderer
DrawLines2D();
}
- if (renderMode == SceneRenderMode::Full)
+ if (renderMode == SceneRenderMode::Full && g_GameFlow->LastGameStatus == GameStatus::Normal)
{
// Draw display sprites sorted by priority.
CollectDisplaySprites(view);
@@ -2292,7 +2339,7 @@ namespace TEN::Renderer
for (int k = 0; k < moveableObj.ObjectMeshes.size(); k++)
{
- if (!(nativeItem->MeshBits & (1 << k)))
+ if (!nativeItem->MeshBits.Test(k))
continue;
DrawMoveableMesh(item, GetMesh(item->MeshIds[k]), room, k, view, rendererPass);
diff --git a/TombEngine/Renderer/RendererDrawMenu.cpp b/TombEngine/Renderer/RendererDrawMenu.cpp
index 6e8685634..10d407b43 100644
--- a/TombEngine/Renderer/RendererDrawMenu.cpp
+++ b/TombEngine/Renderer/RendererDrawMenu.cpp
@@ -885,7 +885,7 @@ namespace TEN::Renderer
void Renderer::RenderTitleImage()
{
Texture2D texture;
- SetTextureOrDefault(texture, TEN::Utils::ToWString(g_GameFlow->IntroImagePath.c_str()));
+ SetTextureOrDefault(texture, TEN::Utils::ToWString(g_GameFlow->GetGameDir() + g_GameFlow->IntroImagePath.c_str()));
if (!texture.Texture)
return;
diff --git a/TombEngine/Renderer/RendererFrame.cpp b/TombEngine/Renderer/RendererFrame.cpp
index d40516df1..696c8ed81 100644
--- a/TombEngine/Renderer/RendererFrame.cpp
+++ b/TombEngine/Renderer/RendererFrame.cpp
@@ -530,7 +530,7 @@ namespace TEN::Renderer
void Renderer::CollectLights(Vector3 position, float radius, int roomNumber, int prevRoomNumber, bool prioritizeShadowLight, bool useCachedRoomLights, std::vector* roomsLights, std::vector* outputLights)
{
- if (_rooms.size() < roomNumber)
+ if (_rooms.size() <= roomNumber)
return;
// Now collect lights from dynamic list and from rooms
@@ -540,164 +540,105 @@ namespace TEN::Renderer
auto& room = _rooms[roomNumber];
RendererLight* brightestLight = nullptr;
- float brightest = 0.0f;
+ float highestIntensity = 0.0f;
- // Dynamic lights have the priority
- for (auto& light : _dynamicLights)
+ auto calculateIntensity = [](float distSqr, const RendererLight& light, float radius) -> std::optional
{
- float distSqr =
- SQUARE(position.x - light.Position.x) +
- SQUARE(position.y - light.Position.y) +
- SQUARE(position.z - light.Position.z);
-
- // Collect only lights nearer than 20 sectors
- if (distSqr >= SQUARE(BLOCK(20)))
- continue;
-
- // Check the out radius
- if (distSqr > SQUARE(light.Out + radius))
- continue;
+ if (distSqr >= SQUARE(BLOCK(20)) || distSqr > SQUARE(light.Out + radius))
+ return std::nullopt; // Light is too far.
float distance = sqrt(distSqr);
float attenuation = 1.0f - distance / light.Out;
- float intensity = attenuation * light.Intensity * light.Luma;
+ return attenuation * light.Intensity * light.Luma;
+ };
- RendererLightNode node = { &light, intensity, distance, 1 };
- tempLights.push_back(node);
+ auto processLight = [&](RendererLight& light, float distSqr, int dynamicFlag)
+ {
+ float distance = sqrt(distSqr);
+ float intensity = calculateIntensity(distSqr, light, radius).value_or(0.0f);
+
+ if (intensity <= EPSILON)
+ return;
+
+ if ((light.Type == LightType::Point || light.Type == LightType::Spot) &&
+ light.CastShadows && prioritizeShadowLight && intensity >= highestIntensity)
+ {
+ highestIntensity = intensity;
+ brightestLight = &light;
+ }
+
+ tempLights.push_back({ &light, intensity, distance, dynamicFlag });
+ };
+
+ // Dynamic lights have the priority
+ for (auto& light : _dynamicLights[_dynamicLightList])
+ {
+ float distSqr = Vector3::DistanceSquared(position, light.Position);
+ processLight(light, distSqr, 1);
}
-
+
if (!useCachedRoomLights)
{
// Check current room and neighbor rooms.
for (int roomToCheck : room.Neighbors)
{
auto& currentRoom = _rooms[roomToCheck];
- int lightCount = (int)currentRoom.Lights.size();
-
- for (int j = 0; j < lightCount; j++)
+ for (auto& light : currentRoom.Lights)
{
- auto* light = ¤tRoom.Lights[j];
-
- float intensity = 0;
- float dist = 0;
-
// Check only lights different from sun.
- if (light->Type == LightType::Sun)
+ if (light.Type == LightType::Sun)
{
// Suns from non-adjacent rooms not added.
if (roomToCheck != roomNumber && (prevRoomNumber != roomToCheck || prevRoomNumber == NO_VALUE))
continue;
// Sun is added without distance checks.
- intensity = light->Intensity * Luma(light->Color);
+ float intensity = light.Intensity * Luma(light.Color);
+ RendererLightNode node = { &light, intensity, 0.0f, 0 };
+ tempLights.push_back(node);
+
+ if (roomsLights != nullptr)
+ roomsLights->push_back(node);
}
- else if (light->Type == LightType::Point || light->Type == LightType::Shadow)
+ else if (light.Type == LightType::Point ||
+ light.Type == LightType::Shadow ||
+ light.Type == LightType::Spot)
{
- float distSqr =
- SQUARE(position.x - light->Position.x) +
- SQUARE(position.y - light->Position.y) +
- SQUARE(position.z - light->Position.z);
-
- // Collect only lights nearer than 20 blocks.
- if (distSqr >= SQUARE(BLOCK(20)))
- continue;
-
- // Check out radius.
- if (distSqr > SQUARE(light->Out + radius))
- continue;
-
- dist = sqrt(distSqr);
- float attenuation = 1.0f - dist / light->Out;
- intensity = attenuation * light->Intensity * Luma(light->Color);
-
- // If collecting shadows, try collecting shadow-casting light.
- if (light->CastShadows && prioritizeShadowLight && light->Type == LightType::Point)
- {
- if (intensity >= brightest)
- {
- brightest = intensity;
- brightestLight = light;
- }
- }
- }
- else if (light->Type == LightType::Spot)
- {
- float distSqr =
- SQUARE(position.x - light->Position.x) +
- SQUARE(position.y - light->Position.y) +
- SQUARE(position.z - light->Position.z);
-
- // Collect only lights nearer than 20 blocks.
- if (distSqr >= SQUARE(BLOCK(20)))
- continue;
-
- // Check range.
- if (distSqr > SQUARE(light->Out + radius))
- continue;
-
- dist = sqrt(distSqr);
- float attenuation = 1.0f - dist / light->Out;
- intensity = attenuation * light->Intensity * light->Luma;
-
- // If shadow pointer provided, try collecting shadow-casting light.
- if (light->CastShadows && prioritizeShadowLight)
- {
- if (intensity >= brightest)
- {
- brightest = intensity;
- brightestLight = light;
- }
- }
+ float distSqr = Vector3::DistanceSquared(position, light.Position);
+ processLight(light, distSqr, 0);
}
else
{
// Invalid light type.
continue;
}
-
- RendererLightNode node = { light, intensity, dist, 0 };
-
- if (roomsLights != nullptr)
- roomsLights->push_back(node);
-
- tempLights.push_back(node);
}
}
}
else
{
- for (int i = 0; i < roomsLights->size(); i++)
- tempLights.push_back(roomsLights->at(i));
+ for (auto& node : *roomsLights)
+ tempLights.push_back(node);
}
// Sort lights.
if (tempLights.size() > MAX_LIGHTS_PER_ITEM)
{
- std::sort(
- tempLights.begin(),
- tempLights.end(),
- [](RendererLightNode a, RendererLightNode b)
- {
- if (a.Dynamic == b.Dynamic)
- {
- return (a.LocalIntensity > b.LocalIntensity);
- }
- else
- {
- return (a.Dynamic > b.Dynamic);
- }
- });
+ std::sort(tempLights.begin(), tempLights.end(), [](const RendererLightNode& a, const RendererLightNode& b)
+ {
+ return (a.Dynamic == b.Dynamic) ? (a.LocalIntensity > b.LocalIntensity) : (a.Dynamic > b.Dynamic);
+ });
}
// Put actual lights in provided vector.
outputLights->clear();
- // Add brightest ligh, if collecting shadow light is specified, even if it's far in range.
+ // Add brightest light, if collecting shadow light is specified, even if it's far in range.
if (prioritizeShadowLight && brightestLight)
outputLights->push_back(brightestLight);
// Add max 8 lights per item, including shadow light for player eventually.
- for (auto l : tempLights)
+ for (auto& l : tempLights)
{
if (prioritizeShadowLight && brightestLight == l.Light)
continue;
@@ -714,7 +655,7 @@ namespace TEN::Renderer
std::vector lightsToDraw;
CollectLights(Vector3(Camera.pos.x, Camera.pos.y, Camera.pos.z), CAMERA_LIGHT_COLLECTION_RADIUS, Camera.pos.RoomNumber, NO_VALUE, true, false, nullptr, &lightsToDraw);
- if (lightsToDraw.size() > 0 && lightsToDraw.front()->CastShadows)
+ if (!lightsToDraw.empty() && lightsToDraw.front()->CastShadows)
{
_shadowLight = lightsToDraw.front();
}
@@ -784,9 +725,9 @@ namespace TEN::Renderer
ROOM_INFO* r = &g_Level.Rooms[roomNumber];
// Collect dynamic lights for rooms
- for (int i = 0; i < _dynamicLights.size(); i++)
+ for (int i = 0; i < _dynamicLights[_dynamicLightList].size(); i++)
{
- RendererLight* light = &_dynamicLights[i];
+ RendererLight* light = &_dynamicLights[_dynamicLightList][i];
// If no radius, ignore
if (light->Out == 0.0f)
diff --git a/TombEngine/Renderer/RendererInit.cpp b/TombEngine/Renderer/RendererInit.cpp
index 44ab3ef55..08d9b11a9 100644
--- a/TombEngine/Renderer/RendererInit.cpp
+++ b/TombEngine/Renderer/RendererInit.cpp
@@ -126,11 +126,13 @@ namespace TEN::Renderer
_currentCausticsFrame = 0;
// Preallocate lists
- _dynamicLights = createVector(MAX_DYNAMIC_LIGHTS);
_lines2DToDraw = createVector(MAX_LINES_2D);
_lines3DToDraw = createVector(MAX_LINES_3D);
_triangles3DToDraw = createVector(MAX_TRIANGLES_3D);
+ for (auto& dynamicLightList : _dynamicLights)
+ dynamicLightList = createVector(MAX_DYNAMIC_LIGHTS);
+
for (auto& item : _items)
item.LightsToDraw = createVector(MAX_LIGHTS_PER_ITEM);
diff --git a/TombEngine/Renderer/Structures/RendererLight.h b/TombEngine/Renderer/Structures/RendererLight.h
index baf818784..e9ab8cf62 100644
--- a/TombEngine/Renderer/Structures/RendererLight.h
+++ b/TombEngine/Renderer/Structures/RendererLight.h
@@ -25,6 +25,11 @@ namespace TEN::Renderer::Structures
bool AffectNeighbourRooms;
bool CastShadows;
float Luma;
+
+ Vector3 PrevPosition;
+ Vector3 PrevDirection;
+
+ int Hash = 0;
};
struct RendererLightNode
diff --git a/TombEngine/Scripting/Internal/ReservedScriptNames.h b/TombEngine/Scripting/Internal/ReservedScriptNames.h
index dcf278189..eccac5e63 100644
--- a/TombEngine/Scripting/Internal/ReservedScriptNames.h
+++ b/TombEngine/Scripting/Internal/ReservedScriptNames.h
@@ -306,6 +306,7 @@ static constexpr char ScriptReserved_EmitParticle[] = "EmitParticle";
static constexpr char ScriptReserved_EmitLightningArc[] = "EmitLightningArc";
static constexpr char ScriptReserved_EmitShockwave[] = "EmitShockwave";
static constexpr char ScriptReserved_EmitLight[] = "EmitLight";
+static constexpr char ScriptReserved_EmitSpotLight[] = "EmitSpotLight";
static constexpr char ScriptReserved_EmitBlood[] = "EmitBlood";
static constexpr char ScriptReserved_EmitFire[] = "EmitFire";
static constexpr char ScriptReserved_MakeExplosion[] = "MakeExplosion";
@@ -421,3 +422,7 @@ constexpr char ScriptReserved_Vec3Length[] = "Length";
constexpr char ScriptReserved_Vec3Lerp[] = "Lerp";
constexpr char ScriptReserved_Vec3Normalize[] = "Normalize";
constexpr char ScriptReserved_Vec3Rotate[] = "Rotate";
+
+// Rotation
+
+constexpr char ScriptReserved_RotationDirection[] = "Direction";
diff --git a/TombEngine/Scripting/Internal/TEN/Effects/EffectsFunctions.cpp b/TombEngine/Scripting/Internal/TEN/Effects/EffectsFunctions.cpp
index e3404b00e..af8b20b74 100644
--- a/TombEngine/Scripting/Internal/TEN/Effects/EffectsFunctions.cpp
+++ b/TombEngine/Scripting/Internal/TEN/Effects/EffectsFunctions.cpp
@@ -23,6 +23,7 @@
#include "Scripting/Internal/TEN/Vec2/Vec2.h"
#include "Sound/sound.h"
#include "Specific/clock.h"
+#include "Specific/trutils.h"
/***
Functions to generate effects.
@@ -258,15 +259,38 @@ namespace TEN::Scripting::Effects
/***Emit dynamic light that lasts for a single frame.
* If you want a light that sticks around, you must call this each frame.
@function EmitLight
-@tparam Vec3 pos
-@tparam Color color (default Color(255, 255, 255))
-@tparam int radius (default 20) corresponds loosely to both intensity and range
+@tparam Vec3 pos position of the light
+@tparam[opt] Color color light color (default Color(255, 255, 255))
+@tparam[opt] int radius measured in "clicks" or 256 world units (default 20)
+@tparam[opt] bool shadows determines whether light should generate dynamic shadows for applicable moveables (default is false)
+@tparam[opt] string name if provided, engine will interpolate this light for high framerate mode (be careful not to use same name for different lights)
*/
- static void EmitLight(Vec3 pos, TypeOrNil col, TypeOrNil radius)
+ static void EmitLight(Vec3 pos, TypeOrNil col, TypeOrNil radius, TypeOrNil castShadows, TypeOrNil name)
{
auto color = USE_IF_HAVE(ScriptColor, col, ScriptColor(255, 255, 255));
- int rad = USE_IF_HAVE(int, radius, 20);
- TriggerDynamicLight(pos.x, pos.y, pos.z, rad, color.GetR(), color.GetG(), color.GetB());
+ int rad = (float)(USE_IF_HAVE(int, radius, 20) * BLOCK(0.25f));
+ TriggerDynamicPointLight(pos.ToVector3(), color, rad, USE_IF_HAVE(bool, castShadows, false), GetHash(USE_IF_HAVE(std::string, name, std::string())));
+ }
+
+/***Emit dynamic directional spotlight that lasts for a single frame.
+* If you want a light that sticks around, you must call this each frame.
+@function EmitSpotLight
+@tparam Vec3 pos position of the light
+@tparam Vec3 dir direction, or a point to which spotlight should be directed to
+@tparam[opt] Color color (default Color(255, 255, 255))
+@tparam[opt] int radius overall radius at the endpoint of a light cone, measured in "clicks" or 256 world units (default 10)
+@tparam[opt] int falloff radius, at which light starts to fade out, measured in "clicks" (default 5)
+@tparam[opt] int distance distance, at which light cone fades out, measured in "clicks" (default 20)
+@tparam[opt] bool shadows determines whether light should generate dynamic shadows for applicable moveables (default is false)
+@tparam[opt] string name if provided, engine will interpolate this light for high framerate mode (be careful not to use same name for different lights)
+*/
+ static void EmitSpotLight(Vec3 pos, Vec3 dir, TypeOrNil col, TypeOrNil radius, TypeOrNil falloff, TypeOrNil distance, TypeOrNil castShadows, TypeOrNil name)
+ {
+ auto color = USE_IF_HAVE(ScriptColor, col, ScriptColor(255, 255, 255));
+ int rad = (float)(USE_IF_HAVE(int, radius, 10) * BLOCK(0.25f));
+ int fallOff = (float)(USE_IF_HAVE(int, falloff, 5) * BLOCK(0.25f));
+ int dist = (float)(USE_IF_HAVE(int, distance, 20) * BLOCK(0.25f));
+ TriggerDynamicSpotLight(pos.ToVector3(), dir.ToVector3(), color, rad, fallOff, dist, USE_IF_HAVE(bool, castShadows, false), GetHash(USE_IF_HAVE(std::string, name, std::string())));
}
/***Emit blood.
@@ -328,6 +352,7 @@ namespace TEN::Scripting::Effects
tableEffects.set_function(ScriptReserved_EmitParticle, &EmitParticle);
tableEffects.set_function(ScriptReserved_EmitShockwave, &EmitShockwave);
tableEffects.set_function(ScriptReserved_EmitLight, &EmitLight);
+ tableEffects.set_function(ScriptReserved_EmitSpotLight, &EmitSpotLight);
tableEffects.set_function(ScriptReserved_EmitBlood, &EmitBlood);
tableEffects.set_function(ScriptReserved_MakeExplosion, &MakeExplosion);
tableEffects.set_function(ScriptReserved_EmitFire, &EmitFire);
diff --git a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp
index 7308d8cf2..a41dfdc69 100644
--- a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp
+++ b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp
@@ -637,7 +637,20 @@ Vec3 Moveable::GetJointPos(int jointIndex, sol::optional offset) const
Rotation Moveable::GetJointRot(int jointIndex) const
{
- return GetBoneOrientation(*m_item, jointIndex);
+ auto point1 = GetJointPosition(m_item, jointIndex);
+ auto point2 = GetJointPosition(m_item, jointIndex, Vector3::Forward * BLOCK(1));
+
+ auto normal = (point1 - point2).ToVector3();
+ normal.Normalize();
+
+ auto eulers = EulerAngles(normal);
+
+ return
+ {
+ TO_DEGREES(eulers.x),
+ TO_DEGREES(eulers.y),
+ TO_DEGREES(eulers.z)
+ };
}
// This does not guarantee that the returned value will be identical
diff --git a/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.cpp b/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.cpp
index 3a2cdf881..e12344984 100644
--- a/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.cpp
+++ b/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.cpp
@@ -18,6 +18,7 @@ void Rotation::Register(sol::table& parent)
ctors(),
sol::call_constructor, ctors(),
sol::meta_function::to_string, &Rotation::ToString,
+ ScriptReserved_RotationDirection, &Rotation::Direction,
/// (float) X angle component.
// @mem x
@@ -77,6 +78,24 @@ Rotation::operator Vector3() const
return Vector3(x, y, z);
};
+/// Converts rotation to a direction normal.
+// @treturn Vec3 resulting normal calculated from this rotation.
+// @function Direction
+Vec3 Rotation::Direction() const
+{
+ // Convert degrees to radians.
+ float xRad = x * RADIAN;
+ float yRad = y * RADIAN;
+
+ // Calculate the direction vector.
+ float dirX = sin(yRad) * cos(xRad);
+ float dirY = -sin(xRad);
+ float dirZ = cos(yRad) * cos(xRad);
+
+ // Scale by the given distance.
+ return Vec3(dirX, dirY, dirZ);
+}
+
/// @tparam Rotation rotation this Rotation.
// @treturn string A string showing the X, Y, and Z angle components of the Rotation.
// @function __tostring
diff --git a/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.h b/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.h
index 87919cc30..00ab7a96f 100644
--- a/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.h
+++ b/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.h
@@ -1,5 +1,6 @@
#pragma once
#include "Math/Objects/EulerAngles.h"
+#include "Scripting/Internal/TEN/Vec3/Vec3.h"
class EulerAngles;
class Pose;
@@ -27,6 +28,7 @@ public:
// Converters
std::string ToString() const;
EulerAngles ToEulerAngles() const;
+ Vec3 Direction() const;
// Operators
operator Vector3() const;
diff --git a/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp b/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp
index 8e87c312c..e2db6fd0a 100644
--- a/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp
+++ b/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp
@@ -30,7 +30,7 @@ Default: nil (i.e. infinite)
@tparam bool autoDelete should be string automatically deleted after timeout is reached.
If not given, the string will remain allocated even after timeout is reached, and can be
shown again without re-initialization.
-Default: false
+Default: true
*/
table.set_function(ScriptReserved_ShowString, &StringsHandler::ShowString, this);
@@ -93,7 +93,7 @@ void StringsHandler::ShowString(const DisplayString& str, sol::optional n
auto it = m_userDisplayStrings.find(str.GetID());
it->second._timeRemaining = numSeconds.value_or(0.0f);
it->second._isInfinite = !numSeconds.has_value();
- it->second._deleteWhenZero = autoDelete.value_or(false);
+ it->second._deleteWhenZero = autoDelete.value_or(true);
}
bool StringsHandler::IsStringDisplaying(const DisplayString& displayString)
@@ -110,7 +110,7 @@ void StringsHandler::ProcessDisplayStrings(float deltaTime)
{
auto& str = it->second;
bool endOfLife = 0.0f >= str._timeRemaining;
- if (str._deleteWhenZero && endOfLife)
+ if (!str._isInfinite && str._deleteWhenZero && endOfLife)
{
ScriptAssertF(!str._isInfinite, "The infinite string {} (key \"{}\") went out of scope without being hidden.", it->first, str._key);
it = m_userDisplayStrings.erase(it);
diff --git a/TombEngine/Shaders/Rooms.fx b/TombEngine/Shaders/Rooms.fx
index 286e78026..afb02de4c 100644
--- a/TombEngine/Shaders/Rooms.fx
+++ b/TombEngine/Shaders/Rooms.fx
@@ -135,7 +135,7 @@ PixelShaderOutput PS(PixelShaderInput input)
if (AmbientOcclusion == 1)
{
float2 samplePosition;
- samplePosition = input.PositionCopy.xy / input.PositionCopy.w; // perspective divide
+ samplePosition = input.PositionCopy.xy / input.PositionCopy.w; // Perspective divide
samplePosition = samplePosition * 0.5f + 0.5f; // transform to range 0.0 - 1.0
samplePosition.y = 1.0f - samplePosition.y;
occlusion = pow(SSAOTexture.Sample(SSAOSampler, samplePosition).x, AmbientOcclusionExponent);
@@ -143,41 +143,26 @@ PixelShaderOutput PS(PixelShaderInput input)
if (CastShadows)
{
- if (Light.Type == LT_POINT)
- {
- DoPointLightShadow(input.WorldPosition, lighting);
-
- }
- else if (Light.Type == LT_SPOT)
- {
- DoSpotLightShadow(input.WorldPosition, lighting);
- }
+ float isPointLight = step(0.5f, Light.Type == LT_POINT); // 1.0 if LT_POINT, 0.0 otherwise
+ float isSpotLight = step(0.5f, Light.Type == LT_SPOT); // 1.0 if LT_SPOT, 0.0 otherwise
+ float isOtherLight = 1.0 - (isPointLight + isSpotLight); // 1.0 if neither LT_POINT nor LT_SPOT
+
+ float3 pointLightShadow = DoPointLightShadow(input.WorldPosition, lighting);
+ float3 spotLightShadow = DoSpotLightShadow(input.WorldPosition, normal, lighting);
+
+ // Blend the shadows based on the light type
+ lighting = pointLightShadow * isPointLight + spotLightShadow * isSpotLight + lighting * isOtherLight;
}
- DoBlobShadows(input.WorldPosition, lighting);
+ lighting = DoBlobShadows(input.WorldPosition, lighting);
- if (doLights)
+ for (int i = 0; i < NumRoomLights; i++)
{
- for (int i = 0; i < NumRoomLights; i++)
- {
- float3 lightPos = RoomLights[i].Position.xyz;
- float3 color = RoomLights[i].Color.xyz;
- float radius = RoomLights[i].Out;
+ float isPointLightRoom = step(0.5f, RoomLights[i].Type == LT_POINT);
+ float isSpotLightRoom = step(0.5f, RoomLights[i].Type == LT_SPOT);
- float3 lightVec = (lightPos - input.WorldPosition);
- float distance = length(lightVec);
- if (distance > radius)
- continue;
-
- lightVec = normalize(lightVec);
- float d = saturate(dot(normal, lightVec ));
- if (d < 0)
- continue;
-
- float attenuation = pow(((radius - distance) / radius), 2);
-
- lighting += color * attenuation * d;
- }
+ lighting += DoPointLight(input.WorldPosition, normal, RoomLights[i]) * isPointLightRoom;
+ lighting += DoSpotLight(input.WorldPosition, normal, RoomLights[i]) * isSpotLightRoom;
}
if (Caustics)
diff --git a/TombEngine/Shaders/ShaderLight.hlsli b/TombEngine/Shaders/ShaderLight.hlsli
index 64c47f13e..4a837cad9 100644
--- a/TombEngine/Shaders/ShaderLight.hlsli
+++ b/TombEngine/Shaders/ShaderLight.hlsli
@@ -6,7 +6,7 @@
float3 DoSpecularPoint(float3 pos, float3 n, ShaderLight light, float strength)
{
- if ((strength <= 0.0))
+ if (strength <= 0.0)
return float3(0, 0, 0);
else
{
@@ -97,116 +97,57 @@ float3 DoSpecularSpot(float3 pos, float3 n, ShaderLight light, float strength)
}
}
-float3 DoPointLight(float3 pos, float3 n, ShaderLight light)
+float3 DoPointLight(float3 pos, float3 normal, ShaderLight light)
{
- float3 lightPos = light.Position.xyz;
- float3 color = light.Color.xyz;
- float intensity = saturate(light.Intensity);
-
- float3 lightVec = (lightPos - pos);
- float distance = length(lightVec);
-
- if (distance > light.Out)
- return float3(0, 0, 0);
- else
- {
- lightVec = normalize(lightVec);
- float d = saturate(dot(n, lightVec));
-
- float attenuation = 1.0f;
- if (distance > light.In)
- attenuation = 1.0f - saturate((distance - light.In) / (light.Out - light.In));
-
- return saturate(color * intensity * attenuation * d);
- }
+ float3 lightVec = light.Position.xyz - pos;
+ float distance = length(lightVec);
+ float3 lightDir = normalize(lightVec);
+
+ float attenuation = saturate((light.Out - distance) / (light.Out - light.In));
+ float d = saturate(dot(normal, lightDir));
+
+ return saturate(light.Color.xyz * light.Intensity * attenuation * d);
}
-float3 DoShadowLight(float3 pos, float3 n, ShaderLight light)
+float3 DoShadowLight(float3 pos, float3 normal, ShaderLight light)
{
- float3 lightPos = light.Position.xyz;
- float3 color = light.Color.xyz;
- float intensity = light.Intensity;
+ float3 lightVec = light.Position.xyz - pos;
+ float distance = length(lightVec);
+ float3 lightDir = normalize(lightVec);
+
+ float attenuation = saturate((light.Out - distance) / (light.Out - light.In));
+ float d = saturate(dot(normal, lightDir));
- float3 lightVec = (lightPos - pos);
- float distance = length(lightVec);
+ float absolute = light.Color.xyz * light.Intensity * attenuation;
+ float directional = absolute * d;
- if (distance > light.Out)
- return float3(0, 0, 0);
- else
- {
- lightVec = normalize(lightVec);
- float d = saturate(dot(n, lightVec));
-
- float attenuation = 1.0f;
- if (distance > light.In)
- attenuation = 1.0f - saturate((distance - light.In) / (light.Out - light.In));
-
- float absolute = float3(color * intensity * attenuation);
- float directional = absolute * d;
-
- return ((absolute * 0.33f) + (directional * 0.66f)) * 2.0f;
- }
+ return saturate((absolute * 0.33f) + (directional * 0.66f)) * 2.0f;
}
-float3 DoSpotLight(float3 pos, float3 n, ShaderLight light)
+float3 DoSpotLight(float3 pos, float3 normal, ShaderLight light)
{
- float3 lightPos = light.Position.xyz;
- float3 color = light.Color.xyz;
- float intensity = saturate(light.Intensity);
- float3 direction = light.Direction.xyz;
- float innerRange = light.In;
- float outerRange = light.Out;
- float coneIn = light.InRange;
- float coneOut = light.OutRange;
+ float3 lightVec = pos - light.Position.xyz;
+ float distance = length(lightVec);
+ float3 lightDir = normalize(lightVec);
+ float cosine = dot(lightDir, light.Direction.xyz);
- float3 lightVec = pos - lightPos;
- float distance = length(lightVec);
- lightVec = normalize(lightVec);
+ // Angle attenuation
+ float coneInCos = cos(light.InRange * (PI / 180.0f));
+ float coneOutCos = cos(light.OutRange * (PI / 180.0f));
+ float angleAttenuation = saturate((cosine - coneOutCos) / (coneInCos - coneOutCos));
- if (distance > outerRange)
- return float3(0, 0, 0);
- else
- {
- float d = saturate(dot(n, -lightVec));
- if (d < 0)
- return float3(0, 0, 0);
- else
- {
- float cosine = dot(lightVec, direction);
+ // Distance attenuation
+ float distanceAttenuation = saturate((light.Out - distance) / (light.Out - light.In));
- float minCosineIn = cos(coneIn * (PI / 180.0f));
- float attenuationIn = max((cosine - minCosineIn), 0.0f) / (1.0f - minCosineIn);
-
- float minCosineOut = cos(coneOut * (PI / 180.0f));
- float attenuationOut = max((cosine - minCosineOut), 0.0f) / (1.0f - minCosineOut);
-
- float attenuation = saturate(attenuationIn * 2.0f + attenuationOut);
-
- if (attenuation > 0.0f)
- {
- float falloff = saturate((outerRange - distance) / (outerRange - innerRange + 1.0f));
- return saturate(color * intensity * attenuation * falloff * d);
- }
- else
- return float3(0, 0, 0);
- }
- }
+ // Surface lighting
+ float d = saturate(dot(normal, -lightDir));
+ return saturate(light.Color.xyz * light.Intensity * angleAttenuation * distanceAttenuation * d);
}
-float3 DoDirectionalLight(float3 pos, float3 n, ShaderLight light)
+float3 DoDirectionalLight(float3 pos, float3 normal, ShaderLight light)
{
- float3 color = light.Color.xyz;
- float3 intensity = light.Intensity;
- float3 direction = -light.Direction.xyz;
-
- float d = max(dot(direction, n), .0f);
-
- if (d > 0.f)
- {
- return (color * intensity * d);
- }
-
- return float3(0, 0, 0);
+ float d = saturate(dot(-light.Direction.xyz, normal));
+ return light.Color.xyz * light.Intensity * d;
}
float DoFogBulb(float3 pos, ShaderFogBulb bulb)
@@ -369,13 +310,12 @@ float DoFogBulbForSky(float3 pos, ShaderFogBulb bulb)
float DoDistanceFogForVertex(float3 pos)
{
- float fog = 0.0f;
+ float d = length(CamPositionWS.xyz - pos);
+ float fogRange = FogMaxDistance * 1024 - FogMinDistance * 1024;
- if (FogMaxDistance > 0.0f)
- {
- float d = length(CamPositionWS.xyz - pos);
- fog = clamp((d - FogMinDistance * 1024) / (FogMaxDistance * 1024 - FogMinDistance * 1024), 0, 1);
- }
+ float fogEnabled = step(0.0f, FogMaxDistance);
+ float fogFactor = (d - FogMinDistance * 1024) / fogRange;
+ float fog = saturate(fogFactor) * fogEnabled;
return fog;
}
@@ -383,16 +323,16 @@ float DoDistanceFogForVertex(float3 pos)
float4 DoFogBulbsForVertex(float3 pos)
{
float4 fog = float4(0.0f, 0.0f, 0.0f, 0.0f);
-
+
for (int i = 0; i < NumFogBulbs; i++)
{
float fogFactor = DoFogBulb(pos, FogBulbs[i]);
- fog.xyz += FogBulbs[i].Color.xyz * fogFactor;
+ float3 fogColor = FogBulbs[i].Color.xyz * fogFactor;
+
+ fog.xyz += fogColor;
fog.w += fogFactor;
- if (fog.w >= 1.0f)
- {
- break;
- }
+
+ fog.w = saturate(fog.w);
}
return fog;
@@ -405,12 +345,12 @@ float4 DoFogBulbsForSky(float3 pos)
for (int i = 0; i < NumFogBulbs; i++)
{
float fogFactor = DoFogBulbForSky(pos, FogBulbs[i]);
- fog.xyz += FogBulbs[i].Color.xyz * fogFactor;
+ float3 fogColor = FogBulbs[i].Color.xyz * fogFactor;
+
+ fog.xyz += fogColor;
fog.w += fogFactor;
- if (fog.w >= 1.0f)
- {
- break;
- }
+
+ fog.w = saturate(fog.w);
}
return fog;
@@ -425,31 +365,25 @@ float3 CombineLights(float3 ambient, float3 vertex, float3 tex, float3 pos, floa
for (int i = 0; i < numLights; i++)
{
- int lightType = lights[i].Type;
+ float isPoint = step(0.5f, float(lights[i].Type == LT_POINT));
+ float isSpot = step(0.5f, float(lights[i].Type == LT_SPOT));
+ float isSun = step(0.5f, float(lights[i].Type == LT_SUN));
+ float isShadow = step(0.5f, float(lights[i].Type == LT_SHADOW));
- if (lightType == LT_POINT)
- {
- diffuse += DoPointLight(pos, normal, lights[i]);
- spec += DoSpecularPoint(pos, normal, lights[i], sheen);
- }
- else if (lightType == LT_SHADOW)
- {
- shadow += DoShadowLight(pos, normal, lights[i]);
- }
- else if (lightType == LT_SUN)
- {
- diffuse += DoDirectionalLight(pos, normal, lights[i]);
- spec += DoSpecularSun(normal, lights[i], sheen);
- }
- else if (lightType == LT_SPOT)
- {
- diffuse += DoSpotLight(pos, normal, lights[i]);
- spec += DoSpecularSpot(pos, normal, lights[i], sheen);
- }
+ diffuse += isPoint * DoPointLight(pos, normal, lights[i]);
+ spec += isPoint * DoSpecularPoint(pos, normal, lights[i], sheen);
+
+ diffuse += isSpot * DoSpotLight(pos, normal, lights[i]);
+ spec += isSpot * DoSpecularSpot(pos, normal, lights[i], sheen);
+
+ diffuse += isSun * DoDirectionalLight(pos, normal, lights[i]);
+ spec += isSun * DoSpecularSun(normal, lights[i], sheen);
+
+ shadow += isShadow * DoShadowLight(pos, normal, lights[i]);
}
shadow = saturate(shadow);
- diffuse.xyz *= tex.xyz;
+ diffuse *= tex;
float3 ambTex = saturate(ambient - shadow) * tex;
float3 combined = ambTex + diffuse + spec;
diff --git a/TombEngine/Shaders/Shadows.hlsli b/TombEngine/Shaders/Shadows.hlsli
index 72bc0df1b..dfdcbab6a 100644
--- a/TombEngine/Shaders/Shadows.hlsli
+++ b/TombEngine/Shaders/Shadows.hlsli
@@ -1,3 +1,5 @@
+#include "./ShaderLight.hlsli"
+
#define SHADOW_INTENSITY (0.55f)
#define INV_SHADOW_INTENSITY (1.0f - SHADOW_INTENSITY)
@@ -66,48 +68,38 @@ float2 GetCubeUVFromDir(int faceIndex, float3 dir)
return uv * .5 + .5;
}
-void DoPointLightShadow(float3 worldPos, inout float3 lighting)
+float3 DoBlobShadows(float3 worldPos, float3 lighting)
{
float shadowFactor = 1.0f;
+
+ for (int i = 0; i < NumSpheres; i++)
+ {
+ Sphere s = Spheres[i];
+ float dist = distance(worldPos, s.position);
+ float insideSphere = saturate(1.0f - step(s.radius, dist)); // Eliminates branching
+ float radiusFactor = dist / s.radius;
+ float factor = (1.0f - saturate(radiusFactor)) * insideSphere;
+ shadowFactor -= factor * shadowFactor;
+ }
+
+ shadowFactor = saturate(shadowFactor);
+ return lighting * saturate(shadowFactor + SHADOW_INTENSITY);
+}
+
+float3 DoPointLightShadow(float3 worldPos, float3 lighting)
+{
+ float shadowFactor = 1.0f;
+
+ [unroll]
for (int i = 0; i < 6; i++)
{
float3 dir = normalize(worldPos - Light.Position);
- int face = GetCubeFaceIndex(dir);
- //debug coloring
- /*
- switch (face)
- {
- case 0:
- lighting += float3(0.2, 0, 0);
- break;
- case 1:
- lighting += float3(0.1, 0, 0);
-
- break;
- case 2:
- lighting += float3(0, 0.2, 0);
-
- break;
- case 3:
- lighting += float3(0, 0.1, 0);
-
- break;
- case 4:
- lighting += float3(0, 0, 0.2);
-
- break;
- default:
- lighting += float3(0, 0, 0.1);
-
- break;
- }
- */
- float2 uv = GetCubeUVFromDir(face, dir);
float4 lightClipSpace = mul(float4(worldPos, 1.0f), LightViewProjections[i]);
lightClipSpace.xyz /= lightClipSpace.w;
+
if (lightClipSpace.x >= -1.0f && lightClipSpace.x <= 1.0f &&
lightClipSpace.y >= -1.0f && lightClipSpace.y <= 1.0f &&
- lightClipSpace.z >= 0.0f && lightClipSpace.z <= 1.0f)
+ lightClipSpace.z >= 0.0f && lightClipSpace.z <= 1.0f)
{
lightClipSpace.x = lightClipSpace.x / 2 + 0.5;
lightClipSpace.y = lightClipSpace.y / -2 + 0.5;
@@ -115,10 +107,11 @@ void DoPointLightShadow(float3 worldPos, inout float3 lighting)
float sum = 0;
float x, y;
- // Perform PCF filtering on a 4 x 4 texel neighborhood
- // what about borders of cubemap?
+ // Perform PCF filtering on a 4 x 4 texel neighborhood.
+ [unroll]
for (y = -1.5; y <= 1.5; y += 1.0)
{
+ [unroll]
for (x = -1.5; x <= 1.5; x += 1.0)
{
sum += ShadowMap.SampleCmpLevelZero(ShadowMapSampler, float3(lightClipSpace.xy + TexOffset(x, y), i), lightClipSpace.z);
@@ -128,59 +121,50 @@ void DoPointLightShadow(float3 worldPos, inout float3 lighting)
shadowFactor = sum / 16.0;
}
}
+
+ // Compute attenuation and combine lighting contribution with shadow factor
float distanceFactor = saturate(((distance(worldPos, Light.Position)) / (Light.Out)));
- lighting *= saturate((shadowFactor + SHADOW_INTENSITY) + (pow(distanceFactor, 4) * INV_SHADOW_INTENSITY));
+ return lighting * saturate((shadowFactor + SHADOW_INTENSITY) + (pow(distanceFactor, 4) * INV_SHADOW_INTENSITY));
}
-
-void DoBlobShadows(float3 worldPos, inout float3 lighting)
+float3 DoSpotLightShadow(float3 worldPos, float3 normal, float3 lighting)
{
+ float influence = 1.0f - Luma(DoSpotLight(worldPos, normal, Light));
+
float shadowFactor = 1.0f;
- for (int i = 0; i < NumSpheres; i++)
+
+ [unroll]
+ for (int i = 0; i < 6; i++)
{
- Sphere s = Spheres[i];
- float dist = distance(worldPos, s.position);
- if (dist > s.radius)
- continue;
- float radiusFactor = dist / s.radius;
- float factor = 1 - (saturate(radiusFactor));
- shadowFactor -= factor * shadowFactor;
+ float3 dir = normalize(worldPos - Light.Position);
+ float4 lightClipSpace = mul(float4(worldPos, 1.0f), LightViewProjections[i]);
+ lightClipSpace.xyz /= lightClipSpace.w;
- }
- shadowFactor = saturate(shadowFactor);
- lighting *= saturate((shadowFactor + SHADOW_INTENSITY));
-}
-
-void DoSpotLightShadow(float3 worldPos, inout float3 lighting)
-{
- float4 lightClipSpace = mul(float4(worldPos, 1.0f), LightViewProjections[0]);
- lightClipSpace.xyz /= lightClipSpace.w;
- float2 shadowUV = lightClipSpace.xy * 0.5f + 0.5f;
- shadowUV.y = (1 - shadowUV.y);
- float shadowFactor = 1.0f;
- if (lightClipSpace.x >= -1.0f && lightClipSpace.x <= 1.0f &&
- lightClipSpace.y >= -1.0f && lightClipSpace.y <= 1.0f &&
- lightClipSpace.z >= 0.0f && lightClipSpace.z <= 1.0f)
- {
- float sum = 0;
- float x, y;
- //perform PCF filtering on a 4 x 4 texel neighborhood
-
- for (y = -1.5; y <= 1.5; y += 1.0)
+ if (lightClipSpace.x >= -1.0f && lightClipSpace.x <= 1.0f &&
+ lightClipSpace.y >= -1.0f && lightClipSpace.y <= 1.0f &&
+ lightClipSpace.z >= 0.0f && lightClipSpace.z <= 1.0f)
{
- for (x = -1.5; x <= 1.5; x += 1.0)
+ lightClipSpace.x = lightClipSpace.x / 2 + 0.5;
+ lightClipSpace.y = lightClipSpace.y / -2 + 0.5;
+
+ float sum = 0;
+ float x, y;
+
+ // Perform PCF filtering on a 4 x 4 texel neighborhood.
+ [unroll]
+ for (y = -1.5; y <= 1.5; y += 1.0)
{
- sum += ShadowMap.SampleCmpLevelZero(ShadowMapSampler, float3(shadowUV.xy + TexOffset(x, y), 0), lightClipSpace.z);
+ [unroll]
+ for (x = -1.5; x <= 1.5; x += 1.0)
+ {
+ sum += ShadowMap.SampleCmpLevelZero(ShadowMapSampler, float3(lightClipSpace.xy + TexOffset(x, y), i), lightClipSpace.z);
+ }
}
+
+ shadowFactor = sum / 16.0;
}
-
- shadowFactor = sum / 16.0;
-
}
- // Fade out towards the borders of the sampled texture
- // for that we simply compare the distance between the shadow texture UV coordinate we sampled and the center (0.5,0.5) of the shadow texture
- // use pow to "boost" the shadow intensity towards the center
- float angleFactor = saturate(pow(distance(shadowUV.xy, 0.5f) * 2,2.2f));
-
- lighting *= saturate((shadowFactor + SHADOW_INTENSITY) + ((angleFactor) * INV_SHADOW_INTENSITY));
+
+ // Compute attenuation and combine lighting contribution with shadow factor
+ return lighting * saturate((shadowFactor + SHADOW_INTENSITY) + (pow(influence, 4) * INV_SHADOW_INTENSITY));
}
diff --git a/TombEngine/Specific/BitField.cpp b/TombEngine/Specific/BitField.cpp
index 45823a0cc..adfcd5ef2 100644
--- a/TombEngine/Specific/BitField.cpp
+++ b/TombEngine/Specific/BitField.cpp
@@ -38,6 +38,17 @@ namespace TEN::Utils
_bits.push_back(bit == '1');
}
+ bool BitField::IndexIsCorrect(unsigned int index) const
+ {
+ if (index >= _bits.size())
+ {
+ TENLog(std::string("BitField attempted to access bit at invalid index."), LogLevel::Warning);
+ return false;
+ }
+
+ return true;
+ }
+
unsigned int BitField::GetSize() const
{
return (unsigned int)_bits.size();
@@ -59,11 +70,8 @@ namespace TEN::Utils
{
for (const unsigned int& index : indices)
{
- if (index >= _bits.size())
- {
- TENLog(std::string("BitField attempted to set bit at invalid index."), LogLevel::Warning);
+ if (!IndexIsCorrect(index))
continue;
- }
_bits[index] = true;
}
@@ -83,11 +91,8 @@ namespace TEN::Utils
{
for (const unsigned int& index : indices)
{
- if (index >= _bits.size())
- {
- TENLog(std::string("BitField attempted to clear bit at invalid index."), LogLevel::Warning);
+ if (!IndexIsCorrect(index))
continue;
- }
_bits[index] = false;
}
@@ -107,11 +112,8 @@ namespace TEN::Utils
{
for (const unsigned int& index : indices)
{
- if (index >= _bits.size())
- {
- TENLog(std::string("BitField attempted to flip bit at invalid index."), LogLevel::Warning);
+ if (!IndexIsCorrect(index))
continue;
- }
_bits[index].flip();
}
@@ -131,11 +133,8 @@ namespace TEN::Utils
{
for (const unsigned int& index : indices)
{
- if (index >= _bits.size())
- {
- TENLog(std::string("BitField attempted to test bit at invalid index."), LogLevel::Warning);
+ if (!IndexIsCorrect(index))
continue;
- }
// Test if any bits at input indices are set.
if (testAny)
@@ -156,7 +155,10 @@ namespace TEN::Utils
bool BitField::Test(unsigned int index) const
{
- return Test(std::vector{ index });
+ if (!IndexIsCorrect(index))
+ return false;
+
+ return _bits[index];
}
bool BitField::TestAny() const
diff --git a/TombEngine/Specific/BitField.h b/TombEngine/Specific/BitField.h
index 5bd3104e3..ecab1819b 100644
--- a/TombEngine/Specific/BitField.h
+++ b/TombEngine/Specific/BitField.h
@@ -8,29 +8,23 @@ namespace TEN::Utils
class BitField
{
private:
- // Constants
-
static constexpr auto SIZE_DEFAULT = 32;
- // Members
-
std::vector _bits = {};
+ bool IndexIsCorrect(unsigned int index) const;
public:
// Presets
-
static const BitField Empty;
static const BitField Default;
// Constructors
-
BitField();
BitField(unsigned int size);
BitField(unsigned int size, unsigned int packedBits);
BitField(const std::string& bitString);
// Getters
-
unsigned int GetSize() const;
unsigned int GetCount() const;
@@ -46,14 +40,12 @@ namespace TEN::Utils
void FlipAll();
// Inquirers
-
bool Test(const std::vector& indices, bool testAny = true) const;
bool Test(unsigned int index) const;
bool TestAny() const;
bool TestAll() const;
// Converters
-
unsigned int ToPackedBits() const;
std::string ToString() const;
diff --git a/TombEngine/Specific/trutils.cpp b/TombEngine/Specific/trutils.cpp
index 0ed7885f2..dd8cdb28b 100644
--- a/TombEngine/Specific/trutils.cpp
+++ b/TombEngine/Specific/trutils.cpp
@@ -155,6 +155,21 @@ namespace TEN::Utils
return strings;
}
+ int GetHash(const std::string& string)
+ {
+ if (string.empty())
+ return 0;
+
+ uint32_t hash = 2166136261u;
+ for (char c : string)
+ {
+ hash ^= static_cast(c);
+ hash *= 16777619u;
+ }
+
+ return static_cast(hash);
+ }
+
Vector2 GetAspectCorrect2DPosition(const Vector2& pos)
{
constexpr auto DISPLAY_SPACE_ASPECT = DISPLAY_SPACE_RES.x / DISPLAY_SPACE_RES.y;
diff --git a/TombEngine/Specific/trutils.h b/TombEngine/Specific/trutils.h
index 763beb1ec..5b094821c 100644
--- a/TombEngine/Specific/trutils.h
+++ b/TombEngine/Specific/trutils.h
@@ -12,6 +12,7 @@ namespace TEN::Utils
std::wstring ToWString(const std::string& string);
std::wstring ToWString(const char* cString);
std::vector SplitString(const std::wstring& string);
+ int GetHash(const std::string& string);
// 2D space utilities
Vector2 GetAspectCorrect2DPosition(const Vector2& pos);