diff --git a/addons/spectator/XEH_preInit.sqf b/addons/spectator/XEH_preInit.sqf index d1bf48d106..e26dc27d07 100644 --- a/addons/spectator/XEH_preInit.sqf +++ b/addons/spectator/XEH_preInit.sqf @@ -31,6 +31,7 @@ GVAR(availableSides) = [west,east,resistance,civilian]; GVAR(availableVisions) = [-2,-1,0,1]; GVAR(camAgent) = objNull; +GVAR(camDistance) = 10; GVAR(camMode) = 0; GVAR(camPan) = 0; GVAR(camPos) = ATLtoASL [worldSize * 0.5, worldSize * 0.5, 20]; diff --git a/addons/spectator/functions/fnc_handleCamera.sqf b/addons/spectator/functions/fnc_handleCamera.sqf index db196c8504..000471809e 100644 --- a/addons/spectator/functions/fnc_handleCamera.sqf +++ b/addons/spectator/functions/fnc_handleCamera.sqf @@ -20,29 +20,53 @@ // Kill PFH when not in free cam (or display is closed) if (isNil QGVAR(camHandler)) exitWith { [_this select 1] call CBA_fnc_removePerFrameHandler; }; -private ["_camera","_oldPos","_altMod","_zoomMod","_mX","_mY","_mZ","_pan","_x","_y","_z"]; - -_camera = GVAR(camera); -_oldPos = getPosASL _camera; - -// Dolly/Boom amount should be influnced by zoom level (it should really be exponential) -// Dollying should also slow as the camera gets close to the ground -_zoomMod = (GVAR(camZoom) * 0.8) max 1; -_altMod = ((((getPos _camera) select 2) * 0.05) max 0.1) min 1; - -_mX = (GVAR(camDolly) select 0) * _altMod / _zoomMod; -_mY = (GVAR(camDolly) select 1) * _altMod / _zoomMod; -_mZ = GVAR(camBoom) / _zoomMod; +private ["_camera","_pan","_tilt","_x","_y","_z"]; _pan = (GVAR(camPan) + 360) % 360; -_x = (_oldPos select 0) + (_mX * cos(_pan)) + (_mY * sin(_pan)); -_y = (_oldPos select 1) - (_mX * sin(_pan)) + (_mY * cos(_pan)); -_z = (_oldPos select 2) + _mZ; +_tilt = GVAR(camTilt); -// Prevent camera going under terrain -GVAR(camPos) = [_x,_y,_z max (getTerrainHeightASL [_x,_y])]; +if (GVAR(camMode) == 0) then { + private ["_oldPos","_altMod","_zoomMod","_mX","_mY","_mZ"]; + _camera = GVAR(freeCamera); + _oldPos = GVAR(camPos); -// Update camera position and rotation -_camera setPosASL GVAR(camPos); -_camera setDir GVAR(camPan); -[_camera, GVAR(camTilt), 0] call BIS_fnc_setPitchBank; + // Dolly/Boom amount should be influnced by zoom level (it should really be exponential) + // Dollying should also slow as the camera gets close to the ground + _zoomMod = (GVAR(camZoom) * 0.8) max 1; + _altMod = ((((getPos _camera) select 2) * 0.05) max 0.1) min 1; + + _mX = (GVAR(camDolly) select 0) * _altMod / _zoomMod; + _mY = (GVAR(camDolly) select 1) * _altMod / _zoomMod; + _mZ = GVAR(camBoom) / _zoomMod; + + _x = (_oldPos select 0) + (_mX * cos(_pan)) + (_mY * sin(_pan)); + _y = (_oldPos select 1) - (_mX * sin(_pan)) + (_mY * cos(_pan)); + _z = (_oldPos select 2) + _mZ; + + // Prevent camera going under terrain + GVAR(camPos) = [_x,_y,_z max (getTerrainHeightASL [_x,_y])]; + + // Update camera position and rotation + _camera setPosASL GVAR(camPos); + _camera setDir _pan; + [_camera, _tilt, 0] call BIS_fnc_setPitchBank; +} else { + private ["_unit","_target","_distance"]; + _camera = GVAR(unitCamera); + + _unit = GVAR(camUnit); + _target = GVAR(targetCamera); + _distance = GVAR(camDistance); + + _x = sin(_pan)*_distance*cos(_tilt); + _y = cos(_pan)*_distance*cos(_tilt); + _z = sin(_tilt) * (2 * (0.3 max _distance)); + + // Update the position of the target camera (used for smooth unit tracking) + _target camSetPos ((_unit modelToWorldVisual [0,0,0]) vectorAdd [0,0,1.5]); + _target camCommit 0; + + // Update the relative position of the unit camera + _camera camSetRelPos [_x, _y, _z]; + _camera camCommit 0; +}; diff --git a/addons/spectator/functions/fnc_handleIcons.sqf b/addons/spectator/functions/fnc_handleIcons.sqf index 72c0d7dee6..d1f83690ff 100644 --- a/addons/spectator/functions/fnc_handleIcons.sqf +++ b/addons/spectator/functions/fnc_handleIcons.sqf @@ -21,7 +21,7 @@ if !(GVAR(showIcons)) exitWith {}; private ["_refPoint","_drawVehicles","_leader","_color","_txt","_unit"]; // Draw groups unless leader is within distance -_refPoint = [GVAR(camera),GVAR(camUnit)] select (GVAR(camMode) > 0); +_refPoint = [GVAR(freeCamera),GVAR(camUnit)] select (GVAR(camMode) > 0); _drawVehicles = []; { _leader = leader _x; diff --git a/addons/spectator/functions/fnc_handleInterface.sqf b/addons/spectator/functions/fnc_handleInterface.sqf index 93c6ca3ddd..7c961071c3 100644 --- a/addons/spectator/functions/fnc_handleInterface.sqf +++ b/addons/spectator/functions/fnc_handleInterface.sqf @@ -149,11 +149,16 @@ switch (toLower _mode) do { case "onmousezchanged": { _args params ["_ctrl","_zChange"]; - // Scroll to change speed, modifier for zoom - if (GVAR(ctrlKey)) then { - [nil,nil,nil,nil,nil,nil,nil, GVAR(camSpeed) + _zChange * 0.2] call FUNC(setCameraAttributes); + // Scroll to modify distance value in third person + if (GVAR(camMode) == 0) then { + // Scroll to change speed, modifier for zoom + if (GVAR(ctrlKey)) then { + [nil,nil,nil,nil,nil,nil,nil, GVAR(camSpeed) + _zChange * 0.2] call FUNC(setCameraAttributes); + } else { + [nil,nil,nil,nil,nil,nil, GVAR(camZoom) + _zChange * 0.1] call FUNC(setCameraAttributes); + }; } else { - [nil,nil,nil,nil,nil,nil, GVAR(camZoom) + _zChange * 0.1] call FUNC(setCameraAttributes); + GVAR(camDistance) = ((GVAR(camDistance) - _zChange * 2) max 5) min 50; }; }; case "onmousemoving": { diff --git a/addons/spectator/functions/fnc_handleMap.sqf b/addons/spectator/functions/fnc_handleMap.sqf index 4eeb5fd7e1..fd084f9967 100644 --- a/addons/spectator/functions/fnc_handleMap.sqf +++ b/addons/spectator/functions/fnc_handleMap.sqf @@ -21,7 +21,7 @@ params ["_map"]; private ["_cachedVehicles","_unit","_color","_icon","_txt"]; if (GVAR(camMode) == 0) then { - _map drawIcon ["\A3\UI_F\Data\GUI\Rsc\RscDisplayMissionEditor\iconcamera_ca.paa",[0,0,0,1],GVAR(camera),20,20,GVAR(camPan)]; + _map drawIcon ["\A3\UI_F\Data\GUI\Rsc\RscDisplayMissionEditor\iconcamera_ca.paa",[0,0,0,1],GVAR(freeCamera),20,20,GVAR(camPan)]; }; _cachedVehicles = []; diff --git a/addons/spectator/functions/fnc_setCameraAttributes.sqf b/addons/spectator/functions/fnc_setCameraAttributes.sqf index 8eb6fe51ac..dd8dd3ef6a 100644 --- a/addons/spectator/functions/fnc_setCameraAttributes.sqf +++ b/addons/spectator/functions/fnc_setCameraAttributes.sqf @@ -58,10 +58,10 @@ GVAR(camVision) = _vision; GVAR(camZoom) = (_zoom min 2) max 0.01; // Apply if camera exists -if (isNil QGVAR(camera)) then { +if (GVAR(isSet)) then { + GVAR(freeCamera) setPosATL _position; + [_mode,_unit,_vision] call FUNC(transitionCamera); +} else { GVAR(camMode) = _mode; GVAR(camPos) = (ATLtoASL _position); -} else { - [_mode,_unit,_vision] call FUNC(transitionCamera); - GVAR(camera) setPosATL _position; }; diff --git a/addons/spectator/functions/fnc_setSpectator.sqf b/addons/spectator/functions/fnc_setSpectator.sqf index 621100a5ba..fce2fe829f 100644 --- a/addons/spectator/functions/fnc_setSpectator.sqf +++ b/addons/spectator/functions/fnc_setSpectator.sqf @@ -50,8 +50,14 @@ if (_set) then { // Update units before opening to support pre-set camera unit [] call FUNC(updateUnits); - // Initalize the camera view - GVAR(camera) = "Camera" camCreate (ASLtoATL GVAR(camPos)); + // Initalize the camera objects + GVAR(freeCamera) = "Camera" camCreate (ASLtoATL GVAR(camPos)); + GVAR(unitCamera) = "Camera" camCreate [0,0,0]; + GVAR(targetCamera) = "Camera" camCreate [0,0,0]; + + // Initalize view + GVAR(unitCamera) camSetTarget GVAR(targetCamera); + GVAR(unitCamera) camCommit 0; [] call FUNC(transitionCamera); // Close map and clear radio @@ -85,8 +91,10 @@ if (_set) then { (findDisplay 12249) closeDisplay 0; // Terminate camera - GVAR(camera) cameraEffect ["terminate", "back"]; - camDestroy GVAR(camera); + GVAR(freeCamera) cameraEffect ["terminate", "back"]; + camDestroy GVAR(freeCamera); + camDestroy GVAR(unitCamera); + camDestroy GVAR(targetCamera); clearRadio; @@ -97,10 +105,12 @@ if (_set) then { BIS_fnc_feedback_allowPP = true; // Cleanup camera variables - GVAR(camera) = nil; GVAR(camBoom) = nil; GVAR(camDolly) = nil; GVAR(camGun) = nil; + GVAR(freeCamera) = nil; + GVAR(unitCamera) = nil; + GVAR(targetCamera) = nil; // Cleanup display variables GVAR(ctrlKey) = nil; diff --git a/addons/spectator/functions/fnc_toggleInterface.sqf b/addons/spectator/functions/fnc_toggleInterface.sqf index 4a03de4271..31472702fc 100644 --- a/addons/spectator/functions/fnc_toggleInterface.sqf +++ b/addons/spectator/functions/fnc_toggleInterface.sqf @@ -43,7 +43,7 @@ if (GVAR(showMap)) then { // Centre map on camera/unit upon opening if (_toggleMap) then { - _map ctrlMapAnimAdd [0, 0.5, [GVAR(camUnit),GVAR(camera)] select (GVAR(camMode) == 0)]; + _map ctrlMapAnimAdd [0, 0.5, [GVAR(camUnit),GVAR(freeCamera)] select (GVAR(camMode) == 0)]; ctrlMapAnimCommit _map; }; } else { diff --git a/addons/spectator/functions/fnc_transitionCamera.sqf b/addons/spectator/functions/fnc_transitionCamera.sqf index 72cb9f1a45..edc7fa23a2 100644 --- a/addons/spectator/functions/fnc_transitionCamera.sqf +++ b/addons/spectator/functions/fnc_transitionCamera.sqf @@ -43,15 +43,17 @@ if (_newMode != 1) then { GVAR(camGun) = false; }; +private ["_camera"]; if (_newMode == 0) then { // Free + _camera = GVAR(freeCamera); + // Preserve camUnit value for consistency when manually changing view - GVAR(camera) cameraEffect ["internal", "back"]; + _camera cameraEffect ["internal", "back"]; showCinemaBorder false; - cameraEffectEnableHUD true; // Apply the camera zoom - GVAR(camera) camSetFov -(linearConversion [0.01,2,GVAR(camZoom),-2,-0.01,true]); - GVAR(camera) camCommit 0; + _camera camSetFov -(linearConversion [0.01,2,GVAR(camZoom),-2,-0.01,true]); + _camera camCommit 0; // Agent is switched to in free cam to hide death table and prevent AI chat while allowing icons to draw (also prevents systemChat and unit HUD) // (Why is so much stuff tied into the current camera unit BI?!) @@ -79,20 +81,32 @@ if (_newMode == 0) then { // Free // Handle camera movement if (isNil QGVAR(camHandler)) then { GVAR(camHandler) = [FUNC(handleCamera), 0] call CBA_fnc_addPerFrameHandler; }; } else { + _camera = GVAR(unitCamera); + // When null unit is given choose random if (isNull _newUnit) then { _newUnit = GVAR(unitList) select floor(random(count GVAR(unitList))); }; - if (_newMode == 1) then { // Internal - // Handle gun cam - if (GVAR(camGun)) then { - _newUnit switchCamera "gunner"; - } else { - _newUnit switchCamera "internal"; - }; - } else { // External - _newUnit switchCamera "external"; + // Switch camera view to internal unit view (external uses the camera) + if (GVAR(camGun)) then { + _newUnit switchCamera "gunner"; + } else { + _newUnit switchCamera "internal"; + }; + + // Handle camera differently for internal/external view + if (_newMode == 1) then { + // Terminate camera view + _camera cameraEffect ["terminate", "back"]; + GVAR(camHandler) = nil; + } else { + // Switch to the camera + _camera cameraEffect ["internal", "back"]; + showCinemaBorder false; + + // Handle camera orbit movement + if (isNil QGVAR(camHandler)) then { GVAR(camHandler) = [FUNC(handleCamera), 0] call CBA_fnc_addPerFrameHandler; }; }; // Clear radio if group changed @@ -101,11 +115,6 @@ if (_newMode == 0) then { // Free }; GVAR(camUnit) = _newUnit; - - // Terminate camera view - GVAR(camera) cameraEffect ["terminate", "back"]; - GVAR(camHandler) = nil; - cameraEffectEnableHUD true; }; GVAR(camMode) = _newMode; diff --git a/addons/spectator/functions/fnc_updateCameraModes.sqf b/addons/spectator/functions/fnc_updateCameraModes.sqf index aef51b56c6..de1611823b 100644 --- a/addons/spectator/functions/fnc_updateCameraModes.sqf +++ b/addons/spectator/functions/fnc_updateCameraModes.sqf @@ -41,7 +41,7 @@ if (_newModes isEqualTo []) then { }; // Update camera in case of change -if !(isNil QGVAR(camera)) then { +if (GVAR(isSet)) then { [] call FUNC(transitionCamera); }; diff --git a/addons/spectator/functions/fnc_updateVisionModes.sqf b/addons/spectator/functions/fnc_updateVisionModes.sqf index 2a1e500f3f..c2bbb09167 100644 --- a/addons/spectator/functions/fnc_updateVisionModes.sqf +++ b/addons/spectator/functions/fnc_updateVisionModes.sqf @@ -49,7 +49,7 @@ if (_newModes isEqualTo []) then { }; // Update camera in case of change -if !(isNil QGVAR(camera)) then { +if (GVAR(isSet)) then { [] call FUNC(transitionCamera); };