Full-Screen Video Cutscenes: Difference between revisions

From The DarkMod Wiki
Jump to navigationJump to search
Geep (talk | contribs)
m Strengthen warning
Geep (talk | contribs)
Revise audio handling
Line 17: Line 17:
* It provides audio and lighting that is separate from the rest of the map.
* It provides audio and lighting that is separate from the rest of the map.
* It avoids any interactions with AI while the player has no sight control.
* It avoids any interactions with AI while the player has no sight control.
Other pluses for this method are:
* It supports "localsound", the quickest-to-implement audio method;
* If you want a black frame around your cutscene (and to not have to burn it in with your video editing software), that's easy.
* If you want a black frame around your cutscene (and to not have to burn it in with your video editing software), that's easy.


Line 54: Line 56:


Somewhere in your map, create an atdm_guis_message entity, with spawnargs values like:
Somewhere in your map, create an atdm_guis_message entity, with spawnargs values like:
* name atdm_guis_message_1
name atdm_guis_message_1
* show 87                          <i>duration of the video in seconds, minus 1</i>
show 87                          <i>duration of the video in seconds, minus 1</i>
* gui guis/cutscene_video.gui    <i>overrides the standard gui</i>
gui guis/cutscene_video.gui    <i>overrides the standard gui</i>
You can delete any non-default "text" or "lines" spawnargs as irrelevant.
You can delete any non-default "text" or "lines" spawnargs as irrelevant.


Line 83: Line 85:


Be aware that, while this specific code suppresses the appearance of the standard large clock-hand cursor seen in the TDM main menu, small code changes can cause it to re-appear, dead in the center of the screen.
Be aware that, while this specific code suppresses the appearance of the standard large clock-hand cursor seen in the TDM main menu, small code changes can cause it to re-appear, dead in the center of the screen.
=== Handling the Audio ===
Someplace in your map, following the [[Script_objects| tdm_voice]] approach to a narrator's or player's voice, create two objects:
* an atdm:trigger_voice entity (say, atdm_trigger_voice_1)
* an atdm:voice entity (e.g., atdm_voice_1)
Link from the trigger_voice to the voice entity, either by
* adding spawnarg "target0    atdm_voice_1" to atdm_trigger_voice_1; or
* select atdm_trigger_voice_1, select also voice_1, then Control-K.
In atdm_voice_1, leave this spawnarg at it's default of:
s_shader  silence
In atdm_trigger_voice_1, add a spawnarg "snd_say", then use the "Choose sound" button to find your sound shader, e.g.:
snd_say  video/cutscene_video
Note also the spawnarg "as_player", which can left at 1 (the default) to represent the sound as the player's voice, or changed to 0 as a narrator). Either work for our purposes. The difference is presumably:
* sound from the player (but not the narrator) could alert AI; and
* player game volume controls distinguish the two.


=== Building Out the Script Function to Trigger the Playback ===
=== Building Out the Script Function to Trigger the Playback ===
Line 90: Line 111:
  void dovideo(){
  void dovideo(){
     sys.trigger($atdm_gui_message_1); //start the cutscene
     sys.trigger($atdm_gui_message_1); //start the cutscene
    sys.trigger($adtm_trigger_voice_1); //and audio
  }
  }


This function can be called by any triggering device you prefer. (Or bypass it to trigger atdm_gui_message_1 directly. The value of a script function is more apparent in what follows.)
This function can be called by any triggering device you prefer. The value of a script function (as opposed to a triggering relay) is more apparent in what follows.)


=== Optional Player Isolation ===
=== Optional Player Isolation ===
Line 108: Line 130:
     sys.teleport($start_cutscene_spot);
     sys.teleport($start_cutscene_spot);
     sys.trigger($atdm_gui_message_1); //start the cutscene
     sys.trigger($atdm_gui_message_1); //start the cutscene
    sys.trigger($adtm_trigger_voice_1); //and audio
     sys.wait(88); // length of video... then teleport away
     sys.wait(88); // length of video... then teleport away
     sys.teleport($after_cutscene_spot);
     sys.teleport($after_cutscene_spot);
Line 193: Line 216:


=== Alternative Sound Treatments ===
=== Alternative Sound Treatments ===
Using localsound within the GUI, as above, has the skipping drawback noted, but is otherwise easy to do and provides the expected stereo separation.
Above, we have demonstrated two audio techniques:
* localsound within the GUI;
* using a narrator's or player's voice with [[Script_objects| tdm_voice]].
These have the skipping drawback noted, but are otherwise easy to do and provide the expected stereo separation.


Conjecturally, other possible ways to go might be:
Other possible ways to go might be:
* ambient sound through the location system.
* ambient sound through the location system.
* using a narrator's voice with [[Script_objects| tdm_voice]].
* a separate speaker in the isolated room. This is not a great choice. Besides reducing the stereo to mono, the sound source will seem to drift around if the player moves. And the player can move in both methods; even if encased in a playerclip cage around $start_cutscene_spot, rotation still seems possible.
* a separate speaker in the isolated room. This is not a great choice. Besides reducing the stereo to mono, the sound source will seem to drift around if the player moves. And the player can move in both methods; even if encased in a playerclip cage around $start_cutscene_spot, rotation still seems possible.

Revision as of 13:37, 21 March 2020

By Geep, 2020

MARCH 2020 - THIS PAGE UNDER CONSTRUCTION. INFORMATION MAY BE INCOMPLETE OR MISLEADING

Introduction

Suppose you have created a video for a TDM in-game cutscene, using video capture and AV editing software. Your video is compliant with video codecs supported in TDM 2.06+ and recommended for new FMs (e.g., MP4 or AVI). Likewise, TDM-supported sound formats.

You want to play this cutscene back at a particular point in the game. Here are two methods:

1) GUI Message Overlay Method. This method repurposes the standard message entity that overlays the full screen (e.g., as the TDM main menu) to display a video instead. It is the generally-preferred method, because it:

  • is the easiest to implement;
  • avoids one additional pixel resampling that the other method uses, so is likely to be more performant and the highest possible image quality;
  • scales easily to multiple disjoint cutscenes.
  • does not require the construction of a separate room (but there are advantages to doing so, as discussed under the next method);

2) Movie Theatre Method. This requires the construction of a separate, isolated movie-theatre-like room (e.g, in the void). You teleport the player there, play the cutscene with the player's view locked to a camera looking at the screen, then teleport back. The isolation has these advantages:

  • It provides audio and lighting that is separate from the rest of the map.
  • It avoids any interactions with AI while the player has no sight control.

Other pluses for this method are:

  • It supports "localsound", the quickest-to-implement audio method;
  • If you want a black frame around your cutscene (and to not have to burn it in with your video editing software), that's easy.

These methods do not rely on the precisely same software architecture (e.g., same GUIs) as briefing videos.

Information Common to Examples of Both Methods

Video and Sound Files

In our examples here, we assume an MP4 and a separate OGG audio file, specifically:

  • an MPEG 4 video, 16:9 aspect ratio, called <fm>/video/cutscene_video.mp4
  • a separate OGG audio file, stereo, called <fm>/sound/video/cutscene_video.ogg
  • both have a length of 88 seconds.

Can an audio track that is embedded in the video be used directly? Probably not at this time. (Using embedded audio is possible with briefing videos, because special programming was done in TDM 2.06 to allow it.)

Sound Shader

Create a sound shader for the OGG, for instance <fm>/sound/cutscene_video.sndshd, with content:

video/cutscene_video
{
   sound/video/cutscene_video.ogg
}

GUI and Script Setup

Create the start of a custom GUI, here <fm>/guis/cutscene_video.gui:

windowDef Desktop { // more to come }

Also, add a function to <fm>/maps/<fm>.script to trigger the playback, like:

void dovideo() { // more to come }

GUI Message Overlay Method

Somewhere in your map, create an atdm_guis_message entity, with spawnargs values like:

name	atdm_guis_message_1
show	87                          duration of the video in seconds, minus 1
gui	guis/cutscene_video.gui     overrides the standard gui

You can delete any non-default "text" or "lines" spawnargs as irrelevant.

"Show" is the duration of the video in (presumably decimal) seconds, minus 1. This is because the standard code (in scriptobject tdm_gui_message) adds 1 second for a gui-based fade out, but our gui doesn't use a fade out. (And if you set hidden spawnarg "fade_out_time" to 0, it reinterprets it as 1)

Building out the GUI

Fill in cutscene_video.gui with:

windowDef Desktop
{
  rect 0 ,0 ,640 ,480
  backcolor 1,1,1,0
  matcolor 0, 0, 0, 1

  background "video/cutscene_video"; // mp4 shader defined in materials/cutscene_video.mtr file.

  onTime 0
  {
     nocursor 1 // This work ONLY in this location, not in Desktop or after reset/matcolor here
     resetCinematics; // reset Video to start
     Desktop::matcolor 1,1,1,1; // show video
     // Doesn't work: localsound "video/cutscene_video"; // shader as defined in sound/cutscene_video.sndshd
  }
}

Be aware that, while this specific code suppresses the appearance of the standard large clock-hand cursor seen in the TDM main menu, small code changes can cause it to re-appear, dead in the center of the screen.

Handling the Audio

Someplace in your map, following the tdm_voice approach to a narrator's or player's voice, create two objects:

  • an atdm:trigger_voice entity (say, atdm_trigger_voice_1)
  • an atdm:voice entity (e.g., atdm_voice_1)

Link from the trigger_voice to the voice entity, either by

  • adding spawnarg "target0 atdm_voice_1" to atdm_trigger_voice_1; or
  • select atdm_trigger_voice_1, select also voice_1, then Control-K.

In atdm_voice_1, leave this spawnarg at it's default of:

s_shader   silence

In atdm_trigger_voice_1, add a spawnarg "snd_say", then use the "Choose sound" button to find your sound shader, e.g.:

snd_say   video/cutscene_video

Note also the spawnarg "as_player", which can left at 1 (the default) to represent the sound as the player's voice, or changed to 0 as a narrator). Either work for our purposes. The difference is presumably:

  • sound from the player (but not the narrator) could alert AI; and
  • player game volume controls distinguish the two.

Building Out the Script Function to Trigger the Playback

Fill in your <fm>.script function:

void dovideo(){
   sys.trigger($atdm_gui_message_1);	//start the cutscene
   sys.trigger($adtm_trigger_voice_1); //and audio
}

This function can be called by any triggering device you prefer. The value of a script function (as opposed to a triggering relay) is more apparent in what follows.)

Optional Player Isolation

It may be a good idea to create an isolated room for the player to reside in during the cutscene. The advantages of this are largely the same as for separate room used in the Movie Theatre method:

  • It provides audio that is separate from the rest of the map.
  • It avoids any interactions with AI while the player's in the box.

While you can use the same room design as Movie Theatre, the dimensions are more flexible here, so perhaps your existing blue room in the void could be outfitted for the purpose.

While seeing only the cutscene, the player can still move around, so maybe cover the floor with moss or a carpet to make that quiet. Or pin the player, e.g., in player clip. Also, place some named object (e.g., a rug "start_cutscene_spot") on the floor, to use as a convenient teleport target.

Then alter the doVideo function to teleport the player there, play the cutscene, then teleport to where the player needs to be afterwards:

void dovideo(){
   sys.teleport($start_cutscene_spot);
   sys.trigger($atdm_gui_message_1);	//start the cutscene
   sys.trigger($adtm_trigger_voice_1); //and audio
   sys.wait(88); // length of video... then teleport away
   sys.teleport($after_cutscene_spot);
}

Movie Theatre Method

Room Setup

Begin by creating the void-sealing room as a cube with an internal dimension of 512 in all 3 directions. Paint the interior dark. (For debugging, it's useful to have a perceptable texture like dark marble, plus a dim private ambient light source.)

Inside this box, near and parallel to one wall, place an internal wall (from a brush converted to func_static) to serve as a screen. Name it "video_wall". Given that we have a 16:9 video, make the size of this wall 512 wide x 288 high. Move it up to be centered vertically inside the room. Select the front face as the screen surface, then apply and fit an entityGUI texture to it. Specifically:

  • Select one surface as the screen: Control-Shift LMB.
  • Using Texture editor/Media, click on the background, search for "entityGUI". When found (under "common/"), select the text, then right-click and "Apply to Selection".
  • Using the Surface editor, hit the "Fit" button.

You will be linking the screen to a custom GUI further below.

Next, create a func_cameraview entity (named here "func_cameraview_1"), and move it to be centered on the screen, at a perpendicular distance of about 184 units. 90-degree-rotate the camera so its arrow faces the screen. Give it the spawnargs:

  • trigger 1
  • target video_wall

You may have to rotate the camera further during debugging the setup. And the distance can be adjusted to taste, knowing that you are trading off edge clipping on various monitors versus black bars. A distance of 200 will provide a black frame (your darkened room walls) around the entire window.

Finally, as discussed with the other method, quiet the player's footfalls and place a teleport target (e.g., "start_cutscene_spot") on the floor under camera.

Build out the GUI

Fill in your custom cutscene_video.gui:

windowDef Desktop
{
  rect 0 ,0 ,640 ,480
  backcolor 1,1,1,0
  Desktop::matcolor 0, 0, 0, 1
  background "textures/darkmod/paint_paper/wallpaper_fancy_red"
  // or use "tdm_black" or "black_background"... doesn't much matter because you'll never see it

  background "video/cutscene_video"; // mp4 shader defined in materials/cutscene_video.mtr file.

  onTime 99999
  {
     // Delay start time of vid playback
  }

  onTrigger
  {
     resetCinematics; // reset Video to start
     Desktop::matcolor 1,1,1,1; // show video
     localsound "video/cutscene_video"; // shader as defined in sound/cutscene_video.sndshd
  }
}

Then link your screen entity video_wall to it, by adding the spawnarg "gui guis/cutscene_video.gui".

Trigger the Playback

Fill in you <fm>.script function like:

void dovideo(){
   sys.teleport($start_cutscene_spot);
   entity cam = $func_cameraview_1;
   cam.activate($player1);		//start the camera
   sys.trigger($video_wall);	//start the cutscene
   sys.wait(88); // length of video
   cam.activate($player1);		//stop the camera
   sys.teleport($after_cutscene_spot);
}

This can be called by any triggering device you prefer.

Discussion

Interrupting or Skipping a CutScene

Support for this is limited. That may not be a problem, particularly with short cutscenes.

With the GUI Message Overlay Method. If you hit ESC, you will return to the main TDM menu. You can subsequently "Resume Mission", and the video will resume where interrupted, but (due to limitations of the localsound procedure) there will be no audio. The GUI shown above does not support skipping with LMB. (Experiments adding a doAction() function seemed to show no response to LMB, whether or not the TDM clock-hand cursor was visible. Likewise, doEsc() was ineffective. Your mileage may vary.)

With the Movie Theatre Method. [TO DO]

Multiple Non-Sequential Cutscenes in an FM

With the GUI Message Overlay Method. Give each cutscene it's own atdm_gui_message entity and dovideo function. But they should be able to share an isolation room.

With the Movie Theatre Method. Perhaps some way can be found that doesn't require a separate movie theatre for every cutscene.

Alternative Sound Treatments

Above, we have demonstrated two audio techniques:

  • localsound within the GUI;
  • using a narrator's or player's voice with tdm_voice.

These have the skipping drawback noted, but are otherwise easy to do and provide the expected stereo separation.

Other possible ways to go might be:

  • ambient sound through the location system.
  • a separate speaker in the isolated room. This is not a great choice. Besides reducing the stereo to mono, the sound source will seem to drift around if the player moves. And the player can move in both methods; even if encased in a playerclip cage around $start_cutscene_spot, rotation still seems possible.