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.

Adding Accessories

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.

Adding Expressions

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.

Adding Frames

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.

Adding Outfits

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.

Unlocking Outfits

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.

Adding Poses

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).

Settings

SetSettings(newSettings) Set settings tables for PhotoMode.
GetSettings() Get settings tables for PhotoMode.

Settings.Camera

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.

Settings.Character

accessoriesEnabled Whether the Accessories menu is enabled.
outfitsEnabled Whether the Outfits menu is enabled.
weaponsUnlocked Whether to show all weapon options in the Character menu regardless of inventory.

Settings.ColorMap

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.

Settings.SoundMap

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.


Adding Accessories

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

Adding Expressions

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 },   -- head mesh
   }
}
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

Adding Frames

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.

Adding Outfits

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 =
{
-- Classic skin swap (uses Lara:SetSkin):

   {
     name = "Classic TR4",
     skin =
         {
            TEN.Objects.ObjID.ANIMATING1,   -- skin
            TEN.Objects.ObjID.ANIMATING2,   -- skinJoints
            TEN.Objects.ObjID.ANIMATING3,   -- skinScream
            TEN.Objects.ObjID.ANIMATING4,   -- hair1
            -- hair2 omitted - unchanged
         },
     meshVisible = "all",
   },

-- Skinned mesh swap (uses Lara:SwapSkinnedMesh):

   {
      name             = "Remastered",
      skinnedMesh      = TEN.Objects.ObjID.ANIMATING14,
      skinnedMeshIndex = 0,      -- Optional sub-index.
      meshVisible      = "none", -- Hide classic meshes
      unlocked         = false,
      onEnter = function() -- Function to call when outfit is selected.
        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,   -- skin
           TEN.Objects.ObjID.ANIMATING2,   -- skinJoints
           TEN.Objects.ObjID.ANIMATING3,   -- skinScream
           TEN.Objects.ObjID.ANIMATING4,   -- hair1
        },
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, -- Optional sub-index.
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"       -- All classic mesh slots remain visible.
    - "none"      -- All classic mesh slots are hidden.
    - { 0, 4, 9 } -- Only the listed slot indices stay visible; rest are hidden.
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() -- Function to call when outfit is selected.
         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.

Unlocking Outfits

Outfits unlocking and locking can be managed using the PhotoMode.UnlockOutfit and PhotoMode.ClearOutfits functions.
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")

Adding Poses

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, -- Animation slot inside PHOTOMODE_ANIMS.
     frameNumber = 0,  -- Starting frame (0 = first frame).
   },

-- You can also reference any other object's animations.

   {
     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).

Settings

Settings can be managed by the two functions PhotoMode.GetSettings and PhotoMode.SetSettings. See those functions for details.
SetSettings(newSettings)
Set settings tables for PhotoMode.

Parameters:

  • newSettings Settings Required settings table

Usage:

    -- In the level's lua file
    local settings = PhotoMode.GetSettings()
    settings.Character.accessoriesEnabled = false
    PhotoMode.SetSettings(settings)
GetSettings()
Get settings tables for PhotoMode.

Returns:

    Settings Current settings table

Settings.Camera

Camera-related settings.

Usage:

-- In the level's lua file
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.Character

Settings related to character customization.

Usage:

-- In the level's lua file
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.

Settings.ColorMap

These settings define the colors used throughout the PhotoMode UI. Colors are of type Color.

Usage:

-- Example of changing the text color
-- In the level's lua file
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).

Settings.SoundMap

These settings map PhotoMode UI events to sound effect IDs. Sound IDs correspond to entries in the game's sound catalogue.

Usage:

-- Example of overriding the PhotoMode open sound
-- In the level's lua file
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.
generated by TEN-LDoc (a fork of LDoc 1.4.6)