Files
GTASource/game/camera/scripted/ScriptDirector.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

901 lines
28 KiB
C++

//
// camera/scripted/ScriptDirector.cpp
//
// Copyright (C) 1999-2011 Rockstar Games. All Rights Reserved.
//
#include "camera/scripted/ScriptDirector.h"
#include "camera/CamInterface.h"
#include "camera/cinematic/CinematicDirector.h"
#include "camera/cutscene/AnimatedCamera.h"
#include "camera/cutscene/CutsceneDirector.h"
#include "camera/debug/DebugDirector.h"
#include "camera/gameplay/GameplayDirector.h"
#include "camera\helpers\ControlHelper.h"
#include "camera/helpers/FrameInterpolator.h"
#include "camera/scripted/ScriptedCamera.h"
#include "camera/system/CameraFactory.h"
#include "camera/system/CameraManager.h"
#include "camera/system/CameraMetadata.h"
#include "camera/viewports/ViewportManager.h"
#include "fwanimation/directorcomponentsyncedscene.h"
#include "peds/ped.h"
#include "script/script.h"
#include "script/handlers/GameScriptResources.h"
CAMERA_OPTIMISATIONS()
INSTANTIATE_RTTI_CLASS(camScriptDirector,0x3A15A7C6)
camScriptDirector::camScriptDirector(const camBaseObjectMetadata& metadata)
: camBaseDirector(metadata)
, m_Metadata(static_cast<const camScriptDirectorMetadata&>(metadata))
{
Reset();
}
camScriptDirector::~camScriptDirector()
{
DestroyAllCameras();
}
bool camScriptDirector::Update()
{
CleanUpCameraReferences();
m_UpdatedCamera = NULL;
//Update all active cameras. Note that these cameras are updated in order from the oldest to the most-recently activated.
const s32 numActiveCameras = m_ActiveCameras.GetCount();
for(s32 i=0; i<numActiveCameras; i++)
{
camBaseCamera* camera = m_ActiveCameras[i];
if(camera && camera->BaseUpdate(m_Frame))
{
m_UpdatedCamera = camera;
}
}
#if FPS_MODE_SUPPORTED
// reset the first person flash effect type
SetFirstPersonFlashEffectType(camManager::CAM_PUSH_IN_FX_NEUTRAL);
SetFirstPersonFlashEffectVehicleModelHash(0);
#endif // FPS_MODE_SUPPORTED
SetRenderOptions(0);
return (m_UpdatedCamera.Get() != NULL);
}
void camScriptDirector::UpdateActiveRenderState()
{
//If the cinematic director is currently rendering, we should not interpolate in or out, so skip the interpolation.
const bool isCinematicDirectorRendering = camInterface::GetCinematicDirector().IsRendering();
camFirstPersonAimCamera* pCam = camInterface::GetGameplayDirector().GetFirstPersonAimCamera();
if(isCinematicDirectorRendering || pCam)
{
if(m_ActiveRenderState == RS_INTERPOLATING_IN)
{
m_ActiveRenderState = RS_FULLY_RENDERING;
}
else if(m_ActiveRenderState == RS_INTERPOLATING_OUT)
{
m_ActiveRenderState = RS_NOT_RENDERING;
}
}
camBaseDirector::UpdateActiveRenderState();
}
void camScriptDirector::Reset()
{
BaseReset();
DestroyAllCameras();
m_RenderedThreadIds.Reset();
#if FPS_MODE_SUPPORTED
SetFirstPersonFlashEffectType(camManager::CAM_PUSH_IN_FX_NEUTRAL);
SetFirstPersonFlashEffectVehicleModelHash(0);
#endif // FPS_MODE_SUPPORTED
#if __BANK
m_DebugScriptTriggeredShakeName[0] = '\0';
m_DebugScriptTriggeredShakeAmplitude = 0.0f;
#endif // __BANK
SetRenderOptions(0);
}
void camScriptDirector::Render(u32 interpolationDuration, bool shouldLockInterpolationSourceFrame)
{
if(!cameraVerifyf(CTheScripts::GetUpdatingTheThreads(),"Script cameras can only be rendered from the script thread"))
{
return;
}
const s32 numActiveCameras = m_ActiveCameras.GetCount();
if(!cameraVerifyf(numActiveCameras > 0, "Please ensure a scripted camera is active before attempting to enable rendering"))
{
return; //Don't render with no active cameras.
}
const GtaThread* activeScriptThread = CTheScripts::GetCurrentGtaScriptThread();
if(activeScriptThread)
{
//Ensure the active script thread is represented in the rendered thread ID list.
const scrThreadId activeScriptThreadId = activeScriptThread->GetThreadId();
if(m_RenderedThreadIds.Find(activeScriptThreadId) == -1)
{
m_RenderedThreadIds.Grow() = activeScriptThreadId;
}
}
camManager::AbortPendingFirstPersonFlashEffect("New script camera rendering");
camBaseDirector::Render(interpolationDuration, shouldLockInterpolationSourceFrame);
}
void camScriptDirector::StopRendering(bool gameplayCatchUp, u32 interpolationDuration, bool shouldLockInterpolationSourceFrame, float distanceToBlend, int blendType)
{
const GtaThread* activeScriptThread = CTheScripts::GetCurrentGtaScriptThread();
StopRendering(gameplayCatchUp, interpolationDuration, shouldLockInterpolationSourceFrame, activeScriptThread, distanceToBlend, blendType);
}
void camScriptDirector::StopRendering(bool gameplayCatchUp, u32 interpolationDuration, bool shouldLockInterpolationSourceFrame, const GtaThread* scriptThread, float distanceToBlend, int blendType)
{
if(scriptThread)
{
//Remove any references to the script thread from the rendered thread ID list.
const scrThreadId activeScriptThreadId = scriptThread->GetThreadId();
m_RenderedThreadIds.DeleteMatches(activeScriptThreadId);
}
//NOTE: This request is ignored if any script threads still expect the director to be rendering.
const s32 numRenderedThreadIds = m_RenderedThreadIds.GetCount();
if(numRenderedThreadIds == 0)
{
if (ShouldRequestFlashForFirstPersonExit(gameplayCatchUp, interpolationDuration))
{
camManager::TriggerFirstPersonFlashEffect( (camManager::eFirstPersonFlashEffectType)GetFirstPersonFlashEffectType(), GetFirstPersonFlashEffectDelay(interpolationDuration, gameplayCatchUp), "Script camera blendout");
}
camBaseDirector::StopRendering(interpolationDuration, shouldLockInterpolationSourceFrame);
if (gameplayCatchUp)
{
const camFrame& catchUpSourceFrame = GetFrame();
camInterface::GetGameplayDirector().CatchUpFromFrame(catchUpSourceFrame, distanceToBlend, blendType);
}
}
}
bool camScriptDirector::ShouldRequestFlashForFirstPersonExit(bool isCatchup, u32 interpolationDuration) const
{
const u32 preDelay = 300u;
// can't flash for catchups or blends into vehicles, because they do an instant cut to first person.
// script need to add these flashes as appropriate.
if (GetFirstPersonFlashEffectVehicleModelHash()!=0)
return false;
if(isCatchup)
{
// on foot catchups result in a 1 second blend before the cut to first person.
interpolationDuration = 1000u;
}
/// can't flash if we're too late to handle the predelay
if (interpolationDuration<preDelay)
return false;
// don't trigger if the cutscene director is rendering
if (camInterface::GetCutsceneDirector().IsRendering())
return false;
// no more script cameras running, we're ready to blend out
const s32 numRenderedThreadIds = m_RenderedThreadIds.GetCount();
if(numRenderedThreadIds == 0 && IsRendering() && GetRenderState()!=RS_INTERPOLATING_OUT)
{
return WillExitToFirstPersonCamera(GetFirstPersonFlashEffectVehicleModelHash());
}
return false;
}
bool camScriptDirector::WillExitToFirstPersonCamera(u32 vehicleModelHash) const
{
if(vehicleModelHash > 0)
{
s32 viewMode = camControlHelperMetadataViewMode::IN_VEHICLE;
CBaseModelInfo* pModelInfo = CModelInfo::GetBaseModelInfoFromHashKey(vehicleModelHash, NULL);
cameraAssertf(pModelInfo, "Invalid model hash for players exit %d", vehicleModelHash);
if(pModelInfo)
{
if(pModelInfo->GetModelType() == MI_TYPE_VEHICLE)
{
viewMode = camControlHelper::ComputeViewModeContextForVehicle(vehicleModelHash);
cameraAssertf(viewMode != -1, "Failed to find the correct view mode for %s",
pModelInfo->GetModelName());
}
else
{
cameraAssertf(0, "The vehicle model hash that the player will use when exiting the custcene is not a vehicle model %d",
vehicleModelHash);
}
}
if(viewMode > -1 )
{
if(camControlHelperMetadataViewMode::FIRST_PERSON == camControlHelper::GetViewModeForContext(viewMode))
{
return true;
}
}
bool IsInFirstPesonModeOnFoot = camControlHelperMetadataViewMode::FIRST_PERSON == camControlHelper::GetViewModeForContext(camControlHelperMetadataViewMode::ON_FOOT);
if(IsInFirstPesonModeOnFoot && CPauseMenu::GetMenuPreference( PREF_FPS_PERSISTANT_VIEW ) == FALSE)
{
return true;
}
}
else
{
if(camControlHelperMetadataViewMode::FIRST_PERSON == camControlHelper::GetViewModeForContext(camControlHelperMetadataViewMode::ON_FOOT))
{
if(m_RenderingOptions.IsFlagSet(SCRIPT_DIRECTOR_STOP_RENDERING_OPTION_PLAYER_EXITS_INTO_COVER) && CPauseMenu::GetMenuPreference( PREF_FPS_THIRD_PERSON_COVER ))
{
return false;
}
else
{
return true;
}
}
}
return false;
}
u32 camScriptDirector::GetFirstPersonFlashEffectType() const
{
return m_FirstPersonFlashEffectType;
}
void camScriptDirector::SetFirstPersonFlashEffectType(u32 type)
{
m_FirstPersonFlashEffectType = type;
}
u32 camScriptDirector::GetFirstPersonFlashEffectVehicleModelHash() const
{
return m_FirstPersonFlashEffectVehicleModelHash;
}
void camScriptDirector::SetFirstPersonFlashEffectVehicleModelHash(u32 modelHash)
{
m_FirstPersonFlashEffectVehicleModelHash = modelHash;
}
extern const u32 g_CatchUpHelperRefForFirstPerson;
u32 camScriptDirector::GetFirstPersonFlashEffectDelay(u32 blendOutDuration, bool bCatchup) const
{
const u32 flashDelay = 300u;
if (bCatchup)
{
const camCatchUpHelperMetadata* catchUpHelperMetadata = camFactory::FindObjectMetadata<camCatchUpHelperMetadata>(g_CatchUpHelperRefForFirstPerson);
if(catchUpHelperMetadata)
{
return catchUpHelperMetadata->m_BlendDuration - flashDelay;
}
else
{
// for catchups, we trigger a hard coded 1 second blend out when in first person mode.
return 1000u - flashDelay;
}
}
else
{
if (blendOutDuration>=flashDelay)
{
return blendOutDuration - flashDelay;
}
else
{
return 0u;
}
}
}
void camScriptDirector::ForceStopRendering(bool gameplayCatchUp, u32 interpolationDuration, bool shouldLockInterpolationSourceFrame, float distanceToBlend, int blendType)
{
//Clear the list of rendered thread IDs and force rendering to stop.
m_RenderedThreadIds.Reset();
if (ShouldRequestFlashForFirstPersonExit(gameplayCatchUp, interpolationDuration))
{
camManager::TriggerFirstPersonFlashEffect((camManager::eFirstPersonFlashEffectType)GetFirstPersonFlashEffectType(), GetFirstPersonFlashEffectDelay(interpolationDuration, gameplayCatchUp), "Script camera blendout");
}
camBaseDirector::StopRendering(interpolationDuration, shouldLockInterpolationSourceFrame);
if (gameplayCatchUp)
{
const camFrame& catchUpSourceFrame = GetFrame();
camInterface::GetGameplayDirector().CatchUpFromFrame(catchUpSourceFrame, distanceToBlend, blendType);
}
}
void camScriptDirector::OnScriptTermination(const GtaThread* scriptThread)
{
//If we're not already interpolating out, ensure that we don't continue rendering for this script thread.
//NOTE: Rendering will continue if another script thread still requires it.
if(m_ActiveRenderState != camBaseDirector::RS_INTERPOLATING_OUT)
{
StopRendering(false, 0, false, scriptThread);
}
}
s32 camScriptDirector::CreateCamera(const char* cameraName)
{
if(!cameraVerifyf(CTheScripts::GetUpdatingTheThreads(),"Script cameras can only be created from the script thread"))
{
return -1 ;
}
const u32 nameHash = atStringHash(cameraName);
const s32 poolIndex = CreateCamera(nameHash);
cameraAssertf(poolIndex >= 0, "The script director failed to create a scripted camera (%s). Is it a valid camera name?", SAFE_CSTRING(cameraName));
return poolIndex;
}
s32 camScriptDirector::CreateCamera(u32 cameraNameHash)
{
if(!cameraVerifyf(CTheScripts::GetUpdatingTheThreads(),"Script cameras can only be created from the script thread"))
{
return -1;
}
s32 poolIndex = -1;
camBaseCamera* camera = camFactory::CreateObject<camBaseCamera>(cameraNameHash);
if(cameraVerifyf(camera, "The script director failed to create a scripted camera (hash: %u)", cameraNameHash))
{
//Add a reference to this camera to our array.
m_Cameras.Grow() = camera;
poolIndex = camera->GetPoolIndex();
}
return poolIndex;
}
void camScriptDirector::DestroyCamera(s32 cameraIndex)
{
camBaseCamera* camera = camBaseCamera::GetCameraAtPoolIndex(cameraIndex);
if(camera != NULL)
{
//Verify that this camera is actually managed by the script director.
RegdCamBaseCamera cameraRef(camera);
bool isScriptManaged = (m_Cameras.Find(cameraRef) >= 0);
if(cameraVerifyf(isScriptManaged, "A script has attempted to destroy a camera (name: %s, hash: %u) that was not created by script",
camera->GetName(), camera->GetNameHash()))
{
delete camera;
CleanUpCameraReferences();
}
}
}
void camScriptDirector::DestroyAllCameras()
{
const s32 numCameras = m_Cameras.GetCount();
//NOTE: We delete the cameras in reverse order so we can safely deal with any dependencies.
for(s32 i=numCameras - 1; i>=0; i--)
{
camBaseCamera* camera = m_Cameras[i];
if(camera)
{
delete camera;
}
}
m_Cameras.Reset();
m_ActiveCameras.Reset();
}
void camScriptDirector::ActivateCamera(s32 cameraIndex)
{
DeactivateCamera(cameraIndex); //First ensure that this camera is not present in the list of active cameras.
camBaseCamera* camera = camBaseCamera::GetCameraAtPoolIndex(cameraIndex);
if(camera)
{
//Add this camera to the end of the list of active cameras, so it is updated last and will be the dominant camera.
m_ActiveCameras.Grow() = camera;
}
}
void camScriptDirector::DeactivateCamera(s32 cameraIndex)
{
camBaseCamera* camera = camBaseCamera::GetCameraAtPoolIndex(cameraIndex);
if(camera)
{
//Remove this camera from our list of active cameras.
for(s32 i=0; i<m_ActiveCameras.GetCount(); )
{
if(m_ActiveCameras[i] == camera)
{
//NOTE: We must NULL the camera reference for safety, as Delete just shuffles the array elements.
m_ActiveCameras[i] = NULL;
m_ActiveCameras.Delete(i);
}
else
{
i++;
}
}
}
}
bool camScriptDirector::IsCameraActive(s32 cameraIndex) const
{
bool isActive = false;
camBaseCamera* camera = camBaseCamera::GetCameraAtPoolIndex(cameraIndex);
if(camera)
{
//See if this camera is in our list of active cameras.
RegdCamBaseCamera cameraRef(camera);
isActive = (m_ActiveCameras.Find(cameraRef) >= 0);
}
return isActive;
}
bool camScriptDirector::IsCameraScriptControllable(s32 cameraIndex) const
{
bool isScriptControllable = false;
camBaseCamera* camera = camBaseCamera::GetCameraAtPoolIndex(cameraIndex);
if(camera)
{
//Note: The debug free camera is open to script control. Otherwise the camera must be owned by the script director.
RegdCamBaseCamera cameraRef(camera);
isScriptControllable = ((camBaseCamera*)camInterface::GetDebugDirector().GetFreeCam() == camera) || (m_Cameras.Find(cameraRef) >= 0);
}
return isScriptControllable;
}
void camScriptDirector::SetCameraFrameParameters(s32 cameraIndex, const Vector3& position, const Vector3& orientation, float fov, u32 duration,
int graphTypePos, int graphTypeRot, int rotationOrder)
{
camBaseCamera* camera = camBaseCamera::GetCameraAtPoolIndex(cameraIndex);
if(camera && camera->GetIsClassId(camScriptedCamera::GetStaticClassId()))
{
camScriptedCamera* scriptedCamera = static_cast<camScriptedCamera*>(camera);
if(duration == 0)
{
//Simply apply the frame parameters right away.
//Terminate any existing interpolation, as the script wants to cut to the new parameters.
camera->StopInterpolating();
camFrame& frame = camera->GetFrameNonConst();
if(!scriptedCamera->IsAttached())
{
//The camera position is being modified, so we must report a cut.
frame.GetFlags().SetFlag(camFrame::Flag_HasCutPosition);
frame.SetPosition(position);
}
if(!scriptedCamera->IsLookingAtSomething() && !scriptedCamera->IsInheritingRoll())
{
//The camera orientation is being modified, so we must report a cut.
frame.GetFlags().SetFlag(camFrame::Flag_HasCutOrientation);
Matrix34 worldMatrix;
CScriptEulers::MatrixFromEulers(worldMatrix, orientation, static_cast<EulerAngleOrder>(rotationOrder));
frame.SetWorldMatrix(worldMatrix);
}
frame.SetFov(fov);
}
else
{
//Interpolate to the frame parameters over the specified duration.
RegdCamBaseCamera cameraRef(camera);
int existingCameraIndexInActiveList = m_ActiveCameras.Find(cameraRef);
if(!cameraVerifyf(existingCameraIndexInActiveList >= 0,
"A camera must be activated before it is valid to set its parameters with interpolation"))
{
return;
}
//First clone the existing camera to use as the interpolation source.
const u32 cameraNameHash = camera->GetNameHash();
s32 sourceCameraIndex = CreateCamera(cameraNameHash);
camBaseCamera* sourceCamera = camBaseCamera::GetCameraAtPoolIndex(sourceCameraIndex);
if(!cameraVerifyf(sourceCamera, "Failed to create an interpolation source camera for script (hash: %u)", cameraNameHash))
{
return;
}
//NOTE: This should deal with cloning ALL relevant parameters for each camera implementation.
sourceCamera->CloneFrom(camera);
//Ensure the interpolation source camera starts active (but not rendering.)
//Add the source camera immediately prior to the (existing) destination camera in the list of active cameras, so it is updated first.
//NOTE: atArray does not expose an insert function that handles resizing, so we must implement this manually.
DeactivateCamera(sourceCameraIndex);
m_ActiveCameras.Grow();
m_ActiveCameras.Delete(m_ActiveCameras.GetCount() - 1);
m_ActiveCameras.Insert(existingCameraIndexInActiveList) = sourceCamera;
//Apply the requested frame parameters to the (existing) destination camera.
camFrame& destinationFrame = camera->GetFrameNonConst();
if(!scriptedCamera->IsAttached())
{
destinationFrame.SetPosition(position);
}
if(!scriptedCamera->IsLookingAtSomething() && !scriptedCamera->IsInheritingRoll())
{
Matrix34 worldMatrix;
CScriptEulers::MatrixFromEulers(worldMatrix, orientation, static_cast<EulerAngleOrder>(rotationOrder));
destinationFrame.SetWorldMatrix(worldMatrix);
}
destinationFrame.SetFov(fov);
//Interpolate from the source (clone) camera to the (existing) destination camera.
//NOTE: The source camera will automatically be deleted on completion of the interpolation. The references to the source camera will then
//be cleaned up within CleanUpCameraReferences() during the update.
camera->InterpolateFromCamera(*sourceCamera, duration, true);
camFrameInterpolator* interpolator = camera->GetFrameInterpolator();
if(interpolator)
{
interpolator->SetCurveTypeForFrameComponent(camFrame::CAM_FRAME_COMPONENT_POS, (camFrameInterpolator::eCurveTypes)graphTypePos);
interpolator->SetCurveTypeForFrameComponent(camFrame::CAM_FRAME_COMPONENT_MATRIX, (camFrameInterpolator::eCurveTypes)graphTypeRot);
}
}
}
}
bool camScriptDirector::AnimateCamera(s32 cameraIndex, const strStreamingObjectName animDictName, const crClip& clip, const Matrix34& sceneOrigin, int sceneId /*=-1*/, u32 iFlags /*= 0*/)
{
bool hasSucceeded = false;
camAnimatedCamera* animatedCamera = GetAnimatedCamera(cameraIndex);
if(animatedCamera)
{
animatedCamera->Init();
animatedCamera->SetShouldUseLocalCameraTime(true);
//If the clip contains a scene origin/offset, apply it to the script-specified origin.
Matrix34 finalSceneOrigin(sceneOrigin);
fwAnimHelpers::ApplyInitialOffsetFromClip(clip, finalSceneOrigin);
animatedCamera->SetSceneMatrix(finalSceneOrigin);
float currentTimeInSeconds = 0.0f;
if(fwAnimDirectorComponentSyncedScene::IsValidSceneId((fwSyncedSceneId)sceneId))
{
currentTimeInSeconds = fwAnimDirectorComponentSyncedScene::GetSyncedSceneTime((fwSyncedSceneId)sceneId);
}
else
{
currentTimeInSeconds = fwTimer::GetCamTimeInMilliseconds() * 0.001f;
}
if(cameraVerifyf(animatedCamera->SetCameraClip(VEC3_ZERO, animDictName, &clip, currentTimeInSeconds, iFlags),
"Failed to initialise the animation for a script animated camera"))
{
animatedCamera->SetCurrentTime(currentTimeInSeconds);
animatedCamera->UpdateFrame(0.0f); //Set initial phase (for translation, rotation etc.)
if (fwAnimDirectorComponentSyncedScene::IsValidSceneId((fwSyncedSceneId)sceneId))
{
animatedCamera->SetSyncedScene((fwSyncedSceneId)sceneId);
}
animatedCamera->GetFrameNonConst().GetFlags().SetFlag(camFrame::Flag_HasCutPosition | camFrame::Flag_HasCutOrientation);
hasSucceeded = true;
// #if __BANK
// animatedCamera->DebugSpewAnimationFrames();
// #endif // __BANK
}
}
return hasSucceeded;
}
bool camScriptDirector::IsCameraPlayingAnimation(s32 cameraIndex, const strStreamingObjectName animDictName, const crClip& clip)
{
bool isPlaying = false;
camAnimatedCamera* animatedCamera = GetAnimatedCamera(cameraIndex);
if(animatedCamera)
{
isPlaying = animatedCamera->IsPlayingAnimation(animDictName, &clip);
}
return isPlaying;
}
void camScriptDirector::SetCameraAnimationPhase(s32 cameraIndex, float phase)
{
camAnimatedCamera* animatedCamera = GetAnimatedCamera(cameraIndex);
if(animatedCamera)
{
animatedCamera->SetPhase(phase);
//This interface could be used to implement a cut, so we must report one.
animatedCamera->GetFrameNonConst().GetFlags().SetFlag(camFrame::Flag_HasCutPosition | camFrame::Flag_HasCutOrientation);
}
}
float camScriptDirector::GetCameraAnimationPhase(s32 cameraIndex)
{
float phase = 0.0f;
camAnimatedCamera* animatedCamera = GetAnimatedCamera(cameraIndex);
if(animatedCamera)
{
phase = animatedCamera->GetPhase();
}
return phase;
}
camAnimatedCamera* camScriptDirector::GetAnimatedCamera(s32 cameraIndex)
{
camAnimatedCamera* animatedCamera = NULL;
camBaseCamera* camera = camBaseCamera::GetCameraAtPoolIndex(cameraIndex);
if(camera && cameraVerifyf(camera->GetIsClassId(camAnimatedCamera::GetStaticClassId()),
"Animations must be played using an animated camera, e.g. \"DEFAULT_ANIMATED_CAMERA\""))
{
animatedCamera = static_cast<camAnimatedCamera*>(camera);
}
return animatedCamera;
}
void camScriptDirector::CleanUpCameraReferences()
{
//Remove any NULL camera references from the storage array.
for(s32 i=0; i<m_Cameras.GetCount(); )
{
if(!m_Cameras[i])
{
m_Cameras.Delete(i);
}
else
{
i++;
}
}
//Also remove any NULL camera references from the list of active cameras.
for(s32 i=0; i<m_ActiveCameras.GetCount(); )
{
if(!m_ActiveCameras[i])
{
m_ActiveCameras.Delete(i);
}
else
{
i++;
}
}
}
#if __BANK
const camAnimatedCamera* camScriptDirector::GetAnimatedCamera(s32 cameraIndex) const
{
const camAnimatedCamera* animatedCamera = NULL;
camBaseCamera* camera = camBaseCamera::GetCameraAtPoolIndex(cameraIndex);
if(camera && cameraVerifyf(camera->GetIsClassId(camAnimatedCamera::GetStaticClassId()),
"Animations must be played using an animated camera, e.g. \"DEFAULT_ANIMATED_CAMERA\""))
{
animatedCamera = static_cast<camAnimatedCamera*>(camera);
}
return animatedCamera;
}
void camScriptDirector::AddWidgetsForInstance()
{
if(m_WidgetGroup == NULL)
{
bkBank* bank = BANKMGR.FindBank("Camera");
if(bank != NULL)
{
m_WidgetGroup = bank->PushGroup("Script director", false);
{
bank->AddButton("Dump root camera frame params to script",
datCallback(MFA(camScriptDirector::DebugDumpRootCamFrameParamsToScript), this),
"Dumps the current root camera frame parameters to a script debug file in the form of a set of camera commands");
bank->AddButton("Dump root camera matrix relative to player to script",
datCallback(MFA(camScriptDirector::DebugDumpRootCameraMatrixRelativeToPlayerToScript), this),
"Dumps the current root camera frame parameters (relative to the player ped) to a script debug file in the form of a set of camera commands");
bank->AddButton("Reset the script director", datCallback(MFA(camScriptDirector::Reset), this),
"Resets the script director. Deleting all cameras, killing any interpolation and stopping rendering");
bank->AddButton("Toggle widescreen borders active", datCallback(MFA(camScriptDirector::DebugToggleWidescreenBordersActive), this));
}
bank->PopGroup(); //Script director.
}
}
}
void camScriptDirector::DebugDumpRootCamFrameParamsToScript()
{
const Vector3& position = camInterface::GetPos();
Vector3 orientationEulers = camInterface::GetMat().GetEulers("yxz");
float fov = camInterface::GetFov();
DebugDumpCameraFrameParamsToScript(position, orientationEulers, fov);
}
void camScriptDirector::DebugDumpRootCameraMatrixRelativeToPlayerToScript()
{
Matrix34 camMatrix = camInterface::GetMat();
const CPed* followPed = camInterface::FindFollowPed();
if(followPed)
{
camMatrix.DotTranspose(MAT34V_TO_MATRIX34(followPed->GetMatrix()));
const Vector3& position = camMatrix.d;
Vector3 orientationEulers;
camMatrix.ToEulersYXZ(orientationEulers);
float fov = camInterface::GetFov();
const u32 stringLength = 255;
char str[stringLength];
formatf(str, stringLength, "SPLINE_NODE POS %f %f %f ANG %f %f %f FOV %f\n", position.x, position.y, position.z,
orientationEulers.x, orientationEulers.y, orientationEulers.z, fov);
cameraDisplayf("%s", str);
DebugDumpCameraFrameParamsToScript(position, orientationEulers, fov);
}
}
void camScriptDirector::DebugDumpCameraFrameParamsToScript(const Vector3& position, const Vector3& orientationEulers, const float fov)
{
const u32 lineLength = 255;
char line[lineLength];
FileHandle file;
file = CFileMgr::OpenFileForAppending(CScriptDebug::GetNameOfScriptDebugOutputFile());
if(cameraVerifyf(CFileMgr::IsValidFileHandle(file), "Failed to open the script dump file for camera commands (%s)", CScriptDebug::GetNameOfScriptDebugOutputFile()))
{
formatf(line, lineLength, "SET_CAM_COORD(_cams_index,<<%f,%f,%f>>)\r\n", position.x, position.y, position.z);
if(cameraVerifyf(CFileMgr::Write(file, line, istrlen(line)) > 0, "Failed to write a camera command to the script dump file"))
{
Vector3 rotation = orientationEulers * RtoD;
formatf(line, lineLength, "SET_CAM_ROT(_cams_index,<<%f,%f,%f>>)\r\n", rotation.x, rotation.y, rotation.z);
if(cameraVerifyf(CFileMgr::Write(file, line, istrlen(line)) > 0, "Failed to write a camera command to the script dump file"))
{
if(fov > 0.0f)
{
formatf(line, lineLength, "SET_CAM_FOV(_cams_index,%f)\r\n", fov);
if(CFileMgr::Write(file, line, istrlen(line)) <= 0)
{
cameraErrorf("Failed to write a camera command to the script dump file");
}
}
formatf(line, lineLength, "\nSET_CAM_PARAMS(_cams_index,<<%f,%f,%f>>,<<%f,%f,%f>>,%f)\r\n\n", position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, fov);
(void)cameraVerifyf(CFileMgr::Write(file, line, istrlen(line)) > 0, "Failed to write a camera command to the script dump file");
}
}
CFileMgr::CloseFile(file);
}
}
void camScriptDirector::DebugToggleWidescreenBordersActive()
{
gVpMan.SetWidescreenBorders(!gVpMan.AreWidescreenBordersActive());
}
const char* camScriptDirector::DebugBuildCameraStringForTextDisplay(char* string, u32 maxLength)
{
const bool isRendering = IsRendering();
if(isRendering)
{
safecatf(string, maxLength, " - Script cams: %d (%d active)", m_Cameras.GetCount(), m_ActiveCameras.GetCount());
}
return string;
}
const char* camScriptDirector::DebugAppendScriptControlledCameraDetails(const camBaseCamera& camera, char* string, u32 maxLength) const
{
const s32 cameraPoolIndex = camera.GetPoolIndex();
const scriptHandler* ownerScriptHandler = CTheScripts::GetScriptHandlerMgr().GetScriptHandlerForResource(
CGameScriptResource::SCRIPT_RESOURCE_CAMERA, cameraPoolIndex);
if(ownerScriptHandler)
{
const GtaThread* ownerScriptThread = static_cast<const GtaThread*>(ownerScriptHandler->GetThread());
if(ownerScriptThread)
{
const char* ownerScriptName = const_cast<GtaThread*>(ownerScriptThread)->GetScriptName();
safecatf(string, maxLength, ", ScriptThread=%s, CameraId=%d", ownerScriptName ? ownerScriptName : "Unknown", cameraPoolIndex);
}
}
return string;
}
void camScriptDirector::DebugGetCameras(atArray<camBaseCamera*> &cameraList) const
{
if(m_Cameras.GetCount() > 0)
{
for(s32 i=0; i<m_Cameras.GetCount(); i++)
{
camBaseCamera* camera = m_Cameras[i];
if(camera)
{
cameraList.PushAndGrow(camera);
}
}
}
}
void camScriptDirector::DebugSetScriptEnabledShakeName(const char* shakeName)
{
sprintf(m_DebugScriptTriggeredShakeName, "%s", shakeName);
}
void camScriptDirector::DebugSetScriptEnabledShakeAmplitude(float amplitudeScalar)
{
m_DebugScriptTriggeredShakeAmplitude = amplitudeScalar;
}
void camScriptDirector::DebugSetScriptDisabledShake()
{
m_DebugScriptTriggeredShakeName[0] = '\0';
m_DebugScriptTriggeredShakeAmplitude = 0.0f;
}
const char* camScriptDirector::DebugGetScriptEnabledShakeName() const
{
return m_DebugScriptTriggeredShakeName;
}
float camScriptDirector::DebugGetScriptEnabledShakeAmplitude() const
{
return m_DebugScriptTriggeredShakeAmplitude;
}
#endif // __BANK