Lua utility module PhotoMode
An in-game photo mode for Tomb Engine.
The player freezes the game, moves a free camera, adjusts Lara's pose and
appearance, sets up a light, applies post-process filters, and can overlay
frame sprites before taking an in-game screenshot.
To enable photo mode in a level, require this module once in your level script or in Autoexec.lua:
local PhotoMode = require("Engine.PhotoMode.PhotoMode")
The module self-registers via callbacks, so no further scripting is needed.
Make sure the following objects exist in your wad:
PHOTOMODE_SPRITES — Sprite sheet for menus.
PHOTOMODE_FRAMES — Frame overlay sprite sheet.
PHOTOMODE_ANIMS — Object that holds all custom photo mode poses.
CAMERA_TARGET — Used internally to drive the object camera.
It is possible to change settings on a per-level basis via PhotoMode.GetSettings and PhotoMode.SetSettings functions, but keep in mind that
Settings.lua is reread every time the level is reloaded. Therefore, you need to implement custom settings management in your level script
if you want to override global settings.
Controls
The active header tab (cycle with Q / E or shoulder buttons) determines which
subject (camera, character or light) the movement controls apply to. The controls remain functional even when the UI is hidden.
| Action |
Keyboard |
Controller |
| Enter Photo Mode |
F3 |
Press Left Stick + Right Stick |
| Move forward / back |
W / S |
Left stick Y |
| Strafe left / right |
A / D |
Left stick X |
| Rotate camera view |
Mouse (move freely) |
Right stick |
| Rotate character Y |
Mouse X |
Right stick X |
| Move subject horizontally |
Hold LMB + Mouse (move freely) |
Hold RT + Right stick Y |
| Move subject vertically |
Hold RMB + Mouse Y |
Hold RT + Right stick Y |
| Navigate menu items |
Up / Down |
D-pad |
| Change option value |
Left / Right |
D-pad Left / Right |
| Confirm (Accept) |
Enter / Action |
Cross/A |
| Switch tabs |
Q / E |
LS / RS |
| Hide UI |
Look (NumPad 0) / F12 |
Triangle / Y |
| Take Photo |
Draw |
LT |
| Exit Photo Mode |
Inventory (Escape) |
Circle / B |
Custom Setup
To override default photo mode content, such as poses, outfits, expressions, accessories and frames, custom setup
may be defined in setup file:
Scripts/PhotoModeSetup.lua. Below is a description of each section that is available in this file.
| name |
Display name shown in the selector. |
| objID |
The object to source the accessory meshes from. |
| meshIndices |
Mesh slot indices on the accessory moveable to make visible. |
| name |
Display name shown in the selector. |
| objID |
The object to source swapped meshes from (nil restores Lara's default expression). |
| meshIndices |
Mesh slot indices on Lara to swap. |
| name |
Display name shown in the Frames selector. |
| spriteID |
Sprite index in the PHOTOMODE_FRAMES object (-1 for no frame). |
| scaleMode |
Scale mode to set for the frame overlay. |
| name |
Display name shown in the selector. |
| skin |
Array of up to 5 ObjIDs for classic skin swap via Lara:SetSkin(). |
| skinnedMesh |
ObjID for skinned mesh swap, or the string "clear" to disable GPU skinning. |
| skinnedMeshIndex |
Optional sub-index passed to Lara:SwapSkinnedMesh(). |
| meshVisible |
Controls classic mesh visibility: "all", "none", or a table of visible slot indices. |
| onEnter |
Optional function called after the outfit is applied. |
| unlocked |
Whether the outfit is visible in the selector menu. |
| ClearOutfits() |
Clear all unlocked outfits so they no longer appear in the PhotoMode outfit selector. |
| UnlockOutfit(name) |
Unlock a named outfit so it appears in the PhotoMode outfit selector. |
| name |
Display name shown in the selector. |
| objID |
The object that contains the animation to apply for this pose. |
| animNumber |
The animation slot number inside that object (0-based). |
| frameNumber |
The starting frame number for that animation (0-based). |
| limitCameraDistance |
Whether to limit camera distance from Lara to prevent clipping through level geometry. |
| distance |
Maximum camera distance from Lara when Settings.Camera.limitCameraDistance is enabled. |
| depthOfFieldEnabled |
All four DOF items (Mode, Focus Distance, Focus Range, Blur Strength) can be hidden at once by setting depthOfFieldEnabled = false. |
| plainText |
Color used for standard body text in the PhotoMode UI. |
| headerText |
Color used for section headers and titles. |
| optionText |
Color used for selectable option text. |
| neutral |
Color used to render the neutral sprites. |
| dimmed |
Color used to render the dimmed sprites. |
| menuRotate |
Sound played when changing the PhotoMode tabs. |
| menuSelect |
Sound played when hovering over or highlighting a menu option. |
| menuChoose |
Sound played when confirming a menu choice. |
| menuOpen |
Sound played when the PhotoMode is opened. |
| menuClose |
Sound played when the PhotoMode is closed. |
| takePhoto |
Sound played when a photo is taken. |
Accessories are drawn on top of Lara. They are defined in
Scripts/PhotoModeData.lua file in Accessories table. To add new accessories add a row to the accessory presets table in that file. The first entry should always be a "None" sentinel (objID = nil, meshIndices = {}) so the player can clear accessories.
To hide the Accessory option entirely set Settings.Character.accessoriesEnabled = false via
PhotoMode.SetSettings.
Usage:
PhotoModeSetup.Accessories =
{
{
name = "Sunglasses",
objID = TEN.Objects.ObjID.ACTOR1_SPEECH_HEAD1,
meshIndices = { 14 },
},
{
name = "Beret",
objID = TEN.Objects.ObjID.ANIMATING5,
meshIndices = { 14 },
}
}
-
name
-
Display name shown in the selector.
- name
string
Display name for this acccessory.
-
objID
-
The object to source the accessory meshes from.
- objID
ObjID
Object that provides the accessory meshes.
-
meshIndices
-
Mesh slot indices on the accessory moveable to make visible.
- meshIndices
table
Array of mesh slot indices to display on the accessory moveable.
Usage:
meshIndices = { 0, 4, 9 } — only the listed slot indices are shown
Expressions swap one or more of Lara's classic mesh slots with meshes sourced from another object. They are defined in Scripts/PhotoModeData.lua file in Expressions table. Use multiple indices to swap more than one mesh at once.
Usage:
PhotoModeSetup.Expressions =
{
{
name = "Scream",
objID = TEN.Objects.ObjID.LARA_SCREAM,
meshIndices = { 14 }, }
}
-
name
-
Display name shown in the selector.
- name
string
Display name for this expression.
-
objID
-
The object to source swapped meshes from (nil restores Lara's default expression).
- objID
ObjID
Object to source the swapped meshes from.
-
meshIndices
-
Mesh slot indices on Lara to swap.
- meshIndices
table
table of mesh slot indices to swap. Use multiple indices to swap more than one mesh at once.
Usage:
meshIndices = { 0, 4, 9 } — only the listed slot indices are swapped
Frames are full-screen sprites drawn from the PHOTOMODE_FRAMES object. A spriteID of -1 means "no frame". The first entry should always be "None". They are defined in Scripts/PhotoModeData.lua file in Frames table.
Usage:
PhotoModeSetup.Frames =
{
{ name = "My Custom Frame", spriteID = 6, scaleMode = TEN.View.ScaleMode.FIT },
}
-
name
-
Display name shown in the Frames selector.
- name
string
Display name for this frame overlay preset.
-
spriteID
-
Sprite index in the PHOTOMODE_FRAMES object (-1 for no frame).
- spriteID
int
Sprite index from PHOTOMODE_FRAMES. Use -1 for no overlay.
-
scaleMode
-
Scale mode to set for the frame overlay.
- scaleMode
ScaleMode
Scale mode for the frame overlay.
Default: TEN.View.ScaleMode.STRETCH.
Outfits can change Lara's Outfit. Both the classic skins and skinned mesh can be used. They are defined in
Scripts/PhotoModeData.lua file in Outfits table. The first entry is always "Default". Set unlocked = false to hide an outfit until the player earns it, then call
PhotoMode.UnlockOutfit to reveal it. Unlocks are saved in GlobalVars.Engine.PhotoModeOutfits.
Usage:
PhotoModeSetup.Outfits =
{
{
name = "Classic TR4",
skin =
{
TEN.Objects.ObjID.ANIMATING1, TEN.Objects.ObjID.ANIMATING2, TEN.Objects.ObjID.ANIMATING3, TEN.Objects.ObjID.ANIMATING4, },
meshVisible = "all",
},
{
name = "Remastered",
skinnedMesh = TEN.Objects.ObjID.ANIMATING14,
skinnedMeshIndex = 0, meshVisible = "none", unlocked = false,
onEnter = function() local s = TEN.Flow.GetSettings()
s.Hair[1].offset = Vec3(-4, 3, -28)
TEN.Flow.SetSettings(s)
end
}
}
-
name
-
Display name shown in the selector.
- name
string
Display name for this outfit.
-
skin
-
Array of up to 5 ObjIDs for classic skin swap via Lara:SetSkin().
- skin
table
Array of up to 5 ObjIDs: skin, skinJoints, skinScream, hair1, hair2. Nil entries leave that slot unchanged.
Default: nil.
Usage:
skin =
{
TEN.Objects.ObjID.ANIMATING1, TEN.Objects.ObjID.ANIMATING2, TEN.Objects.ObjID.ANIMATING3, TEN.Objects.ObjID.ANIMATING4, },
-
skinnedMesh
-
ObjID for skinned mesh swap, or the string "clear" to disable GPU skinning.
- skinnedMesh
ObjID
ObjID passed to Lara:SwapSkinnedMesh(), or "clear" to call Lara:ClearSkinnedMesh().
Default: nil.
Usage:
skinnedMesh = TEN.Objects.ObjID.ANIMATING14,
-
skinnedMeshIndex
-
Optional sub-index passed to Lara:SwapSkinnedMesh().
- skinnedMeshIndex
int
Optional sub-index for SwapSkinnedMesh.
Default: nil.
Usage:
skinnedMeshIndex = 0,
-
meshVisible
-
Controls classic mesh visibility: "all", "none", or a table of visible slot indices.
- meshVisible
string or table
"all" keeps all meshes visible, "none" hides all, or a table of indices keeps only those slots visible.
Default: nil.
Usage:
- "all" - "none" - { 0, 4, 9 }
-
onEnter
-
Optional function called after the outfit is applied.
- onEnter
function
Hook function executed after applying this outfit. Can be used to change hair offsets.
Default: nil.
Usage:
onEnter = function() local s = TEN.Flow.GetSettings()
s.Hair[1].offset = Vec3(-4, 3, -28)
TEN.Flow.SetSettings(s)
end
-
unlocked
-
Whether the outfit is visible in the selector menu.
- unlocked
bool
true or nil makes the outfit visible; false hides it in selection until PhotoMode.UnlockOutfit is called.
Default: true.
-
ClearOutfits()
-
Clear all unlocked outfits so they no longer appear in the PhotoMode outfit selector.
Usage:
PhotoMode.ClearOutfits()
-
UnlockOutfit(name)
-
Unlock a named outfit so it appears in the PhotoMode outfit selector. The outfit remains unlocked in all levels.
Parameters:
- name
string
The outfit name string as defined in Scripts/PhotoModeData.lua file in Outfits table.
Usage:
PhotoMode.UnlockOutfit("Secret Wetsuit")
Poses are defined in the Scripts/PhotoModeData.lua file in Poses table. Each pose applies an animation from the PHOTOMODE_ANIMS object or any other object in the level.
Usage:
PhotoModeSetup.Poses =
{
{
name = "Victory",
objID = TEN.Objects.ObjID.PHOTOMODE_ANIMS,
animNumber = 42, frameNumber = 0, },
{
name = "Running",
objID = TEN.Objects.ObjID.LARA,
animNumber = 17,
frameNumber = 0,
},
}
-
name
-
Display name shown in the selector.
- name
string
Display name for this pose.
-
objID
-
The object that contains the animation to apply for this pose.
- objID
ObjID
The object that contains the animation to apply for this pose.
-
animNumber
-
The animation slot number inside that object (0-based).
- animNumber
int
The animation slot number inside that object (0-based).
-
frameNumber
-
The starting frame number for that animation (0-based).
- frameNumber
int
The frame number for that animation to set as pose (0-based).
-
SetSettings(newSettings)
-
Set settings tables for PhotoMode.
Parameters:
- newSettings
Settings
Required settings table
Usage:
local settings = PhotoMode.GetSettings()
settings.Character.accessoriesEnabled = false
PhotoMode.SetSettings(settings)
-
GetSettings()
-
Get settings tables for PhotoMode.
Returns:
Settings
Current settings table
Camera-related settings.
Usage:
local settings = PhotoMode.GetSettings()
settings.Camera.limitCameraDistance = false
PhotoMode.SetSettings(settings)
-
limitCameraDistance
-
Whether to limit camera distance from Lara to prevent clipping through level geometry. Enabling this will cause the camera to stop moving further away once it reaches the configured distance, but it will not push the camera back if the player moves closer after exceeding the limit.
- limitCameraDistance
bool
true to enable camera distance limiting, false to allow unlimited camera distance.
Default: true.
-
distance
-
Maximum camera distance from Lara when Settings.Camera.limitCameraDistance is enabled. The camera will stop moving further away once it reaches this distance.
- distance
int
Maximum camera distance from Lara when Settings.Camera.limitCameraDistance is enabled, measured in game units.
Default: 4096.
-
depthOfFieldEnabled
-
All four DOF items (Mode, Focus Distance, Focus Range, Blur Strength) can be hidden at once by setting depthOfFieldEnabled = false. This is useful for projects that do not use the DOF post-process effect and want a cleaner Camera menu.
- depthOfFieldEnabled
bool
true to enable depth of field, false to disable it.
Default: true.
Settings related to character customization.
Usage:
local settings = PhotoMode.GetSettings()
settings.Character.weaponsUnlocked = false
PhotoMode.SetSettings(settings)
-
accessoriesEnabled
-
Whether the Accessories menu is enabled. Accessories are mesh swaps parented to a hidden moveable that mirrors Lara's animation, allowing them to move naturally with her skeleton. Set this to false to hide the Accessories menu and disable the accessory system entirely.
- accessoriesEnabled
bool
true to enable the Accessories menu, false to hide it and disable the accessory option in Character menu.
Default: true.
-
outfitsEnabled
-
Whether the Outfits menu is enabled.
- outfitsEnabled
bool
true to enable the Outfits menu, false to hide it and disable the outfit option in Character menu.
Default: true.
-
weaponsUnlocked
-
Whether to show all weapon options in the Character menu regardless of inventory.
- weaponsUnlocked
bool
set this to false to hide unavailable weapons.
Default: true.
These settings define the colors used throughout the PhotoMode UI.
Colors are of type
Color.
Usage:
local settings = PhotoMode.GetSettings()
settings.ColorMap.plainTextColor = TEN.Color(200, 180, 60, 255)
PhotoMode.SetSettings(settings)
-
plainText
-
Color used for standard body text in the PhotoMode UI.
- plainText
Color
Applied to descriptive text.
Default: Flow.GetSettings().UI.plainTextColor.
-
headerText
-
Color used for section headers and titles.
- headerText
Color
Applied to PhotoMode category headings and titles.
Default: Flow.GetSettings().UI.headerTextColor.
-
optionText
-
Color used for selectable option text.
- optionText
Color
Applied to text entries.
Default: Flow.GetSettings().UI.optionTextColor.
-
neutral
-
Color used to render the neutral sprites.
- neutral
Color
Tint applied to the sprites.
Default: Color(255, 255, 255, 255).
-
dimmed
-
Color used to render the dimmed sprites.
- dimmed
Color
Tint applied to the sprites.
Default: Color(120, 120, 120, 255).
These settings map PhotoMode UI events to sound effect IDs.
Sound IDs correspond to entries in the game's sound catalogue.
Usage:
local settings = PhotoMode.GetSettings()
settings.SoundMap.menuOpen = 42
PhotoMode.SetSettings(settings)
-
menuRotate
-
Sound played when changing the PhotoMode tabs.
- menuRotate
int
Sound effect ID triggered while scrolling through PhotoMode.
Default: 108.
-
menuSelect
-
Sound played when hovering over or highlighting a menu option.
- menuSelect
int
Sound effect ID triggered on item selection highlight.
Default: 109.
-
menuChoose
-
Sound played when confirming a menu choice.
- menuChoose
int
Sound effect ID triggered when the player confirms a selected action.
Default: 111.
-
menuOpen
-
Sound played when the PhotoMode is opened.
- menuOpen
int
Sound effect ID triggered when the PhotoMode is opened.
Default: 109.
-
menuClose
-
Sound played when the PhotoMode is closed.
- menuClose
int
Sound effect ID triggered when the PhotoMode is closed.
Default: 109.
-
takePhoto
-
Sound played when a photo is taken.
- takePhoto
int
Sound effect ID triggered when a photo is taken.
Default: 111.