diff --git a/addons/viewports/$PBOPREFIX$ b/addons/viewports/$PBOPREFIX$ new file mode 100644 index 0000000000..0ed652c6e7 --- /dev/null +++ b/addons/viewports/$PBOPREFIX$ @@ -0,0 +1 @@ +z\ace\addons\viewports diff --git a/addons/viewports/CfgEventHandlers.hpp b/addons/viewports/CfgEventHandlers.hpp new file mode 100644 index 0000000000..2a3f71f852 --- /dev/null +++ b/addons/viewports/CfgEventHandlers.hpp @@ -0,0 +1,15 @@ +class Extended_PreStart_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_SCRIPT(XEH_preStart)); + }; +}; +class Extended_PreInit_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_SCRIPT(XEH_preInit)); + }; +}; +class Extended_PostInit_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_SCRIPT(XEH_postInit)); + }; +}; diff --git a/addons/viewports/CfgVehicles.hpp b/addons/viewports/CfgVehicles.hpp new file mode 100644 index 0000000000..9b6427ff2c --- /dev/null +++ b/addons/viewports/CfgVehicles.hpp @@ -0,0 +1,22 @@ +class CfgVehicles { + class B_MBT_01_base_F; + class B_MBT_01_cannon_F: B_MBT_01_base_F { // Merkava + class ace_viewports { + class SLD_backLeftUpper { + type = "screen"; + camLocation[] = {0,0,0.05}; + maxDistance = 5; + camAttach[] = {0,0}; + screenLocation[] = {-0.925,-3.43459,-1.07}; + roles[]={"cargo"}; + }; + }; + }; + class B_MBT_01_TUSK_F: B_MBT_01_cannon_F { // Merkava TUSK (slightly different model-space because different p3d model) + class ace_viewports: ace_viewports { + class SLD_backLeftUpper: SLD_backLeftUpper { + screenLocation[] = {-0.925,-4.65511,-1.07}; + }; + }; + }; +}; diff --git a/addons/viewports/README.md b/addons/viewports/README.md new file mode 100644 index 0000000000..c4faabaada --- /dev/null +++ b/addons/viewports/README.md @@ -0,0 +1,4 @@ +ace_viewports +========== + +Allows crew to look through periscopes diff --git a/addons/viewports/XEH_PREP.hpp b/addons/viewports/XEH_PREP.hpp new file mode 100644 index 0000000000..ddf32fb5d9 --- /dev/null +++ b/addons/viewports/XEH_PREP.hpp @@ -0,0 +1,8 @@ +LOG("prep"); + +PREP(eachFrame); +PREP(enterVehicle); +PREP(getSeatInfo); +PREP(getViewports); +PREP(viewCleanup); +PREP(viewCreate); diff --git a/addons/viewports/XEH_postInit.sqf b/addons/viewports/XEH_postInit.sqf new file mode 100644 index 0000000000..06984eabf0 --- /dev/null +++ b/addons/viewports/XEH_postInit.sqf @@ -0,0 +1,9 @@ +#include "script_component.hpp" + +if (!hasInterface) exitWith {}; + +GVAR(pfeh) = -1; +["CBA_settingsInitialized", { + TRACE_1("CBA_settingsInitialized",GVAR(enabled)); + ["vehicle", LINKFUNC(enterVehicle), true] call CBA_fnc_addPlayerEventHandler; +}] call CBA_fnc_addEventHandler; diff --git a/addons/viewports/XEH_preInit.sqf b/addons/viewports/XEH_preInit.sqf new file mode 100644 index 0000000000..a8940fc7dd --- /dev/null +++ b/addons/viewports/XEH_preInit.sqf @@ -0,0 +1,15 @@ +#include "script_component.hpp" + +ADDON = false; + +PREP_RECOMPILE_START; +#include "XEH_PREP.hpp" +PREP_RECOMPILE_END; + +#include "initSettings.sqf" + +#ifdef POINT_CONFIG_DEBUG +call compileScript [QPATHTOF(dev\debugPoints.sqf)]; +#endif + +ADDON = true; diff --git a/addons/viewports/XEH_preStart.sqf b/addons/viewports/XEH_preStart.sqf new file mode 100644 index 0000000000..022888575e --- /dev/null +++ b/addons/viewports/XEH_preStart.sqf @@ -0,0 +1,3 @@ +#include "script_component.hpp" + +#include "XEH_PREP.hpp" diff --git a/addons/viewports/config.cpp b/addons/viewports/config.cpp new file mode 100644 index 0000000000..7d303de1b7 --- /dev/null +++ b/addons/viewports/config.cpp @@ -0,0 +1,19 @@ +#include "script_component.hpp" + +class CfgPatches { + class ADDON { + name = COMPONENT_NAME; + units[] = {}; + weapons[] = {}; + requiredVersion = REQUIRED_VERSION; + requiredAddons[] = {"ace_common"}; + author = ECSTRING(common,ACETeam); + authors[] = {"PabstMirror"}; + url = ECSTRING(main,URL); + VERSION_CONFIG; + }; +}; + +#include "CfgEventHandlers.hpp" +#include "CfgVehicles.hpp" +#include "gui.hpp" diff --git a/addons/viewports/data/optic_window_ca.paa b/addons/viewports/data/optic_window_ca.paa new file mode 100644 index 0000000000..18e1aa9ea7 Binary files /dev/null and b/addons/viewports/data/optic_window_ca.paa differ diff --git a/addons/viewports/dev/debugPoints.sqf b/addons/viewports/dev/debugPoints.sqf new file mode 100644 index 0000000000..4d8346bc0a --- /dev/null +++ b/addons/viewports/dev/debugPoints.sqf @@ -0,0 +1,173 @@ +#include "\z\ace\addons\viewports\script_component.hpp" +/* + [] call compileScript ["\z\ace\addons\viewports\dev\debugPoints.sqf"]; + + This is mostly just for placing the mem points in threeden + left-click to place and adjust current point + alt+left-click to place next point + alt+right-click to output + shift+right-click to reset + + // Tweak: + z = (vehicle player) getVariable "ace_viewports_viewports"; + p = z # 0 # 4; z # 0 set [4, p vectorAdd [0,0.1,0]]; + + // Place by view: + v = (positionCameraToWorld [0,0,0.4]); + m = (vehicle player) worldToModel v; + z = (vehicle player) getVariable "ace_viewports_viewports"; + z # 0 set [4, m]; +*/ + +#define IDD_3DEN 313 + +[] spawn { + INFO_2("Pre-Init [is3den %1][3den display: %2]", is3den, !isNull findDisplay IDD_3DEN); + if (!is3den) exitWith {}; + + GVAR(3denIndex) = 0; + GVAR(3denViewports) = []; + + disableSerialization; + private _3den = findDisplay IDD_3DEN; + if (_3den getVariable [QGVAR(setup), false]) exitWith {}; + _3den setVariable [QGVAR(setup), true]; + + _3den displayAddEventHandler ["MouseButtonDown", { + params ["", "_button", "_mouseX", "_mouseY", "_shift", "_ctrl", "_alt"]; + + if (_shift && _button == 1) exitWith { + systemChat "Reset/Reload"; + if ((supportInfo "u:diag_mergeConfigFile") isNotEqualTo []) then { + call compile 'diag_mergeConfigFile ["P:\z\ace\addons\viewports\config.cpp"]'; + }; + { _x setVariable [QGVAR(viewports), nil] } forEach vehicles; + GVAR(3denIndex) = 0; + GVAR(3denViewports) = []; + true + }; + + if (_alt) then { + if (count GVAR(3denViewports) > GVAR(3denIndex)) then { + systemChat "Advance to next index"; + GVAR(3denIndex) = GVAR(3denIndex) + 1; + }; + }; + if (_alt && _button == 1) exitWith { + private _vehicle = (get3DENSelected "object") param [0, objNull]; + private _config = configOf _vehicle; + private _model = getText (_config >> "model"); + while {true} do { + private _parent = inheritsFrom _config; + if ((getText (_parent >> "model")) != _model) exitWith {}; + _config = _parent; + }; + + private _out = []; + _out pushBack format [" class %1: %2 {", configName _config, configName inheritsFrom _config]; + _out pushBack format [" class ace_viewports {"]; + { + _x params ["_name", "", "_camLocation", "_camAttach"]; + _out pushBack format [' class %1 {', _name]; + _out pushBack format [' camLocation[] = {%1, %2, %3};', _camLocation # 0, _camLocation # 1, _camLocation # 2]; + _out pushBack format [' camAttach = %1;', _camAttach]; + // _out pushBack format [' type = "%1";', _type]; + // _out pushBack format [' screenLocation[] = {};']; + // _out pushBack format [' maxDistance = 99;']; + // _out pushBack format [' compartments[]={};']; + // _out pushBack format [' roles[]={};']; + _out pushBack format [' };']; + } forEach GVAR(3denViewports); + + _out pushBack format [' };']; + _out pushBack format [' };']; + + // Some inherited configs might use a different model which uses a different offset - yuck + private _inherited = '((configName _x) isKindOf (configName _config)) && {_model != getText (_x >> "model")}' configClasses (configFile >> "CfgVehicles"); + _out pushBack format ["// Watch out for %1", _inherited apply {configName _x}]; + + copyToClipboard (_out joinString endl); + systemChat format ["copied %1 lines", count _out]; + }; + if (_button == 0) exitWith { + private _vehicle = (get3DENSelected "object") param [0, objNull]; + if (isNull _vehicle) exitWith {}; + + private _start = AGLToASL positionCameraToWorld [0,0,0]; + private _end = AGLToASL screenToWorld [_mouseX, _mouseY]; + private _intersections = lineIntersectsSurfaces [_start, _end]; + private _pointASL = _intersections # 0 # 0; + if (isNil "_pointASL") exitWith {}; + _pointASL = _pointASL vectorAdd [0,0,0.09]; // Add a little bit up because it always sinks into the model + private _pointMS = _vehicle worldToModel ASLtoAGL _pointASL; + + private _name = format ["view_%1",GVAR(3denIndex)]; + // [_name, _type, _camLocation, _camAttach, _screenLocation, _maxDistance, _compartments, _roles] + GVAR(3denViewports) set [GVAR(3denIndex), [_name, "", _pointMS, 0, _pointMS, 99, [], []]]; + true + }; + }]; +}; + +// this runs in both threeden and in-game +addMissionEventHandler ["Draw3D", { + private _vehicle = vehicle player; + private _viewports = _vehicle getVariable [QGVAR(viewports), []]; + + if (is3den) then { + _vehicle = (get3DENSelected "object") param [0, objNull]; + if (isNull _vehicle) exitWith {}; + _viewports = [_vehicle] call FUNC(getViewports); + if (GVAR(3denViewports) isNotEqualTo []) then { + _viewports = GVAR(3denViewports); + }; + }; + if (isNull _vehicle) exitWith {}; + + + drawIcon3D ["#(argb,8,8,3)color(1,1,1,1)", [1,1,0,1], _vehicle modelToWorldVisual [0,0,0], 0.1, 0.1, 0, "", 1, 0.02, "TahomaB"]; + if (alive player) then { // not using ace_player so this works in 3den + drawIcon3D ["#(argb,8,8,3)color(1,1,1,1)", [0,1,0,1], aslToAGL eyepos player, 0.1, 0.1, 0, "eye", 1, 0.02, "TahomaB"]; + drawIcon3D ["#(argb,8,8,3)color(1,1,1,1)", [0,1,0,1], player modelToWorldVisual (player selectionPosition "pilot"), 0.1, 0.1, 0, "pilot", 1, 0.02, "TahomaB"]; + }; + // { + // private _pos = _vehicle modelToWorldVisual (_vehicle selectionPosition [_x, "Memory"]); + // drawIcon3D ["#(argb,8,8,3)color(1,1,1,1)", [0,0,1,0.2], _pos, 0.05, 0.05, 0, _x, 1, 0.02, "TahomaB"]; + // } forEach (_vehicle selectionNames "Memory"); + + + { + _x params ["_name", "_type", "_camLocation", "_camAttach", "_screenLocation", "_maxDistance", "_compartments", "_roles"]; + + if (_camLocation isEqualType "") then { + _camLocation = _vehicle selectionPosition [_camLocation, "Memory"]; + }; + + private _screenAGL = _vehicle modelToWorldVisual _screenLocation; + drawIcon3D ["#(argb,8,8,3)color(0,0,1,1)", [1,0.5,1,1], _screenAGL, 0.05, 0.05, 0, format ["%1:%2",_forEachIndex,_compartments], 1, 0.03, "TahomaB"]; + + private _camAGL = if (_camAttach isEqualType 0) then { + _vehicle modelToWorldVisual _camLocation + } else { + private _turretConfig = [_vehicle, _camAttach] call CBA_fnc_getTurret; + private _memoryPointGunnerOptics = getText(_turretConfig >> "memoryPointGunnerOptics"); + _vehicle modelToWorldVisual (_camLocation vectorAdd (_vehicle selectionPosition _memoryPointGunnerOptics)) + }; + drawIcon3D ["#(argb,8,8,3)color(1,0,0,1)", [0.5,1,1,1], _camAGL, 0.1, 0.1, 0, format ["%1:%2",_forEachIndex,_name], 1, 0.03, "TahomaB"]; + + if (_camAttach isEqualType 0) then { + private _camAGL = _vehicle modelToWorldVisual _camLocation; + drawIcon3D ["#(argb,8,8,3)color(1,0,0,1)", [1,1,1,1], _camAGL, 0.1, 0.1, 0, _name, 1, 0.05, "TahomaB"]; + private _target = _vehicle modelToWorldVisual (_camLocation vectorAdd ([1, _camAttach, 0] call CBA_fnc_polar2vect)); + drawLine3D [_camAGL, _target, [0,1,0,1]]; + private _target = _vehicle modelToWorldVisual (_camLocation vectorAdd ([1, _camAttach, 1] call CBA_fnc_polar2vect)); + drawLine3D [_camAGL, _target, [0,1,0,1]]; + private _target = _vehicle modelToWorldVisual (_camLocation vectorAdd ([1, _camAttach, -1] call CBA_fnc_polar2vect)); + drawLine3D [_camAGL, _target, [0,1,0,1]]; + private _target = _vehicle modelToWorldVisual (_camLocation vectorAdd ([0.2, _camAttach+90, 0] call CBA_fnc_polar2vect)); + drawLine3D [_camAGL, _target, [1,0,1,1]]; + private _target = _vehicle modelToWorldVisual (_camLocation vectorAdd ([0.2, _camAttach-90, 0] call CBA_fnc_polar2vect)); + drawLine3D [_camAGL, _target, [1,0,1.2,1]]; + }; + } forEach _viewports; +}]; diff --git a/addons/viewports/functions/fnc_eachFrame.sqf b/addons/viewports/functions/fnc_eachFrame.sqf new file mode 100644 index 0000000000..247e9e26b5 --- /dev/null +++ b/addons/viewports/functions/fnc_eachFrame.sqf @@ -0,0 +1,77 @@ +#include "script_component.hpp" +/* +* Author: PabstMirror +* Runs each frame while inside of a vehicle with viewports +* +* Arguments: +* 0: PFEH Args +* 0: Player +* 1: Vehicle +* 2: Viewport configuration +* 3: Viewport index shown (-1 for none) +* 4: Last visionmode +* +* Return Value: +* None +* +* Example: +* [] call ace_viewports_fnc_eachFrame +* +* Public: No +*/ + +params ["_args", "_pfID"]; +_args params ["_player", "_vehicle", "_viewports", "_shownIndex", "_lastVisionMode"]; + +private _newIndex = -1; +if (cba_events_control) then { + if (cameraView != "INTERNAL") exitWith {}; + if (isTurnedOut _player) exitWith {}; + if (!([_player, _vehicle, []] call EFUNC(common,canInteractWith))) exitWith {}; + + BEGIN_COUNTER(newIndex); + if ((_shownIndex > -1) && {currentVisionMode _player != _lastVisionMode}) then { + // Vision Mode Changed - Force stop cam and restart + call FUNC(viewCleanup); + _shownIndex = -1; + }; + + ([_player] call FUNC(getSeatInfo)) params ["_role", "", "", "_comparment"]; + + private _newIndexAngle = 45; // Controls the max angle + private _eyesPosASL = AGLtoASL (positionCameraToWorld [0, 0, 0]); + private _eyesDir = (AGLtoASL (positionCameraToWorld [0, 0, 1])) vectorDiff _eyesPosASL; + { + _x params ["", "", "_camLocation", "", "_screenLocation", "_maxDistance", "_compartments", "_roles"]; + + private _viewASL = AGLtoASL (_vehicle modelToWorldVisual _screenLocation); + private _viewDiff = _viewASL vectorDiff _eyesPosASL; + private _viewAngle = acos (_viewDiff vectorCos _eyesDir); + #ifdef DEBUG_MODE_FULL + systemChat format ["%1: %2 @ %3",_forEachIndex,round _viewAngle, vectorMagnitude _viewDiff]; + #endif + if ( + (_viewAngle < _newIndexAngle) + && {(_compartments isEqualTo []) || {(toLower _comparment) in _compartments}} + && {(_roles isEqualTo []) || {(toLower _role) in _roles}} + && {(vectorMagnitude _viewDiff) < _maxDistance} + ) then { + _newIndex = _forEachIndex; + _newIndexAngle = _viewAngle; + }; + } forEach _viewports; + END_COUNTER(newIndex); +}; + +if (_shownIndex == _newIndex) exitWith {}; // No-change - fast exit + +if (_shownIndex > -1) then { + call FUNC(viewCleanup); +}; + +if (_newIndex > -1) then { + [_vehicle, _viewports # _newIndex, currentVisionMode _player] call FUNC(viewCreate); + _args set [4, currentVisionMode _player]; +}; + +_args set [3, _newIndex]; diff --git a/addons/viewports/functions/fnc_enterVehicle.sqf b/addons/viewports/functions/fnc_enterVehicle.sqf new file mode 100644 index 0000000000..4e49be144c --- /dev/null +++ b/addons/viewports/functions/fnc_enterVehicle.sqf @@ -0,0 +1,36 @@ +#include "script_component.hpp" +/* + * Author: PabstMirror + * Handle playerEH for new changing vehicle, check if it has any viewports and start PFEH + * + * Arguments: + * 0: player + * 1: vehicle + * + * Return Value: + * None + * + * Example: + * [player, vehicle player] call ace_viewports_fnc_enterVehicle + * + * Public: No + */ + +params ["_player", "_vehicle"]; +TRACE_2("enterVehicle",_player,_vehicle); + +if (GVAR(pfeh) != -1) then { + TRACE_1("cleaning up",GVAR(pfeh)); + [GVAR(pfeh)] call CBA_fnc_removePerFrameHandler; + GVAR(pfeh) = -1; + call FUNC(viewCleanup); +}; + +if (!GVAR(enabled)) exitWith {}; +if (_player == _vehicle) exitWith {}; + +private _viewports = [_vehicle] call FUNC(getViewports); +if (_viewports isEqualTo []) exitWith {}; + +GVAR(pfeh) = [LINKFUNC(eachFrame), 0, [_player, _vehicle, _viewports, -1, -1]] call CBA_fnc_addPerFrameHandler; +TRACE_3("start pfeh",GVAR(pfeh),typeOf _vehicle,count _viewports); diff --git a/addons/viewports/functions/fnc_getSeatInfo.sqf b/addons/viewports/functions/fnc_getSeatInfo.sqf new file mode 100644 index 0000000000..1254b5f07e --- /dev/null +++ b/addons/viewports/functions/fnc_getSeatInfo.sqf @@ -0,0 +1,46 @@ +#include "script_component.hpp" +/* + * Author: Dystopian, PabstMirror + * Adapted from quickmount's addFreeSeatsActions + * + * Arguments: + * 0: Unit + * + * Return Value: + * ARRAY + * + * Example: + * [player] call ace_viewports_fnc_getSeatInfo + * + * Public: No + */ + +params ["_unit"]; + +private _vehicle = vehicle _unit; +if (_vehicle == _unit) exitWith { [] }; +private _vehicleConfig = configOf _vehicle; + +private _fullCrew = fullCrew [_vehicle, "", false]; +(_fullCrew select (_fullCrew findIf {_unit == _x select 0})) params ["", "_role", "_cargoIndex", "_turretPath"]; + +private _compartment = switch (_role) do { + case "driver": { + (_vehicleConfig >> "driverCompartments") call BIS_fnc_getCfgData + }; + case "cargo": { + // note: cargoNumber is different from the cargoIndex from fullCrew... + private _cargoNumber = fullCrew [_vehicle, "cargo", true] findIf {_unit == _x select 0}; + private _cargoCompartments = getArray (_vehicleConfig >> "cargoCompartments"); + private _cargoCompartmentsLast = count _cargoCompartments - 1; + _cargoCompartments select (_cargoNumber min _cargoCompartmentsLast) + }; + default { + private _turretConfig = [_vehicleConfig, _turretPath] call CBA_fnc_getTurret; + (_turretConfig >> "gunnerCompartments") call BIS_fnc_getCfgData + }; +}; + +if (!(_compartment isEqualType "")) then { _compartment = format ["Compartment%1",_compartment] }; + +[_role, _cargoIndex, _turretPath, _compartment] diff --git a/addons/viewports/functions/fnc_getViewports.sqf b/addons/viewports/functions/fnc_getViewports.sqf new file mode 100644 index 0000000000..ffa82fcc82 --- /dev/null +++ b/addons/viewports/functions/fnc_getViewports.sqf @@ -0,0 +1,74 @@ +#include "script_component.hpp" +/* +* Author: PabstMirror +* Gets viewports for a vehicle from config. Caches results to a setVar on the vic. +* +* Arguments: +* 0: vehicle +* +* Return Value: +* ARRAY +* +* Example: +* [vehicle player] call ace_viewports_fnc_getViewports +* +* Public: No +*/ + +params ["_vehicle"]; + +private _viewports = _vehicle getVariable [QGVAR(viewports), nil]; + +if (isNil "_viewports") then { + _viewports = (configProperties [(configOf _vehicle) >> "ace_viewports", "isClass _x", true]) apply { + // name [STRING] is just used for debug + private _name = configName _x; + // type [STRING] - Optional + private _type = getText (_x >> "type"); + // camLocation [ARRAY or STRING] - Required + private _camLocation = if (isArray (_x >> "camLocation")) then { + getArray (_x >> "camLocation") // modelOffset + } else { + getText (_x >> "camLocation") // memPoint + }; + // camAttach [ARRAY or NUMBER] - Required + private _camAttach = if (isArray (_x >> "camAttach")) then { + getArray (_x >> "camAttach") // turret + } else { + getNumber (_x >> "camAttach") // angle + }; + // screenLocation [ARRAY or STRING] - Optional (will be converted to ARRAY here!) + private _screenLocation = if (isArray (_x >> "screenLocation")) then { + getArray (_x >> "screenLocation") // modelOffset + } else { + getText (_x >> "screenLocation") // memPoint + }; + if (_screenLocation isEqualType "") then { + // screens should be on the hull (IE non-animated) so we can do all the mem-point calculations here + if (_screenLocation == "") exitWith { // use generic periscope drop height from cam + private _camLocArray = if (_camLocation isEqualType []) then { + _camLocation + } else { + _vehicle selectionPosition [_camLocation, "Memory"]; + }; + _screenLocation =_camLocArray vectorAdd [0,0,-0.175] + }; + _screenLocation = _vehicle selectionPosition [_screenLocation, "Memory"]; + }; + // maxDistance [NUMBER] - Optional + private _maxDistance = getNumber (_x >> "maxDistance"); + if (_maxDistance == 0) then { + _maxDistance = 0.8; + }; + // compartments [ARRAY] - Optional + private _compartments = (getArray (_x >> "compartments")) apply {toLower _x}; + // roles [ARRAY] - Optional + private _roles = (getArray (_x >> "roles")) apply {toLower _x}; + + [_name, _type, _camLocation, _camAttach, _screenLocation, _maxDistance, _compartments, _roles] + }; + TRACE_3("getViewports",_vehicle,typeOf _vehicle,count _viewports); + _vehicle setVariable [QGVAR(viewports), _viewports]; +}; + +_viewports diff --git a/addons/viewports/functions/fnc_viewCleanup.sqf b/addons/viewports/functions/fnc_viewCleanup.sqf new file mode 100644 index 0000000000..b24a9cc31b --- /dev/null +++ b/addons/viewports/functions/fnc_viewCleanup.sqf @@ -0,0 +1,28 @@ +#include "script_component.hpp" +/* +* Author: PabstMirror +* Cleans up existing viewport display and camera +* +* Arguments: +* None +* +* Return Value: +* None +* +* Example: +* [] call ace_viewports_fnc_viewCleanup +* +* Public: No +*/ + +TRACE_1("camCleanup",_this); + +if (!isNull (missionNamespace getVariable [QGVAR(camera), objNull])) then { + GVAR(camera) cameraEffect ["terminate", "back", QGVAR(pip0)]; + camDestroy GVAR(camera); +}; + +private _display = uiNamespace getVariable [QGVAR(display), displayNull]; +if (!isNull _display) then { + QGVAR(display) cutText ["", "PLAIN"]; +}; diff --git a/addons/viewports/functions/fnc_viewCreate.sqf b/addons/viewports/functions/fnc_viewCreate.sqf new file mode 100644 index 0000000000..02e44c129d --- /dev/null +++ b/addons/viewports/functions/fnc_viewCreate.sqf @@ -0,0 +1,129 @@ +#include "script_component.hpp" +/* +* Author: PabstMirror +* Creates a viewport display and camera +* +* Arguments: +* 0: Vehicle +* 1: Viewport +* 2: Player's vision mode +* +* Return Value: +* None +* +* Example: +* [...] call ace_viewports_fnc_viewCreate +* +* Public: No +*/ + +params ["_vehicle", "_viewport", "_visionMode"]; +_viewport params ["_name", "_type", "_camLocation", "_camAttach"]; +TRACE_5("camCreate",_vehicle,_name,_type,_camLocation,_camAttach); + +private _usingGoggles = _visionMode > 0; + +if (_camLocation isEqualType "") then { + _camLocation = _vehicle selectionPosition [_camLocation, "Memory"]; + if (_camLocation isEqualTo [0,0,0]) then { WARNING_2("probably bad cam location %1:%2",typeOf _vehicle,_viewport); } +}; + +// Create Cam and attach it to vic +GVAR(camera) = "camera" camCreate getPos _vehicle; +if (_camAttach isEqualType 0) then { + // number - Static attach and set const direction + GVAR(camera) attachTo [_vehicle, _camLocation]; + GVAR(camera) setDir _camAttach; // could do pitch as well, but probably not needed +} else { + // array - Turret path, get gunner optic mem and bone-attach to it + private _turretConfig = [_vehicle, _camAttach] call CBA_fnc_getTurret; + private _memoryPointGunnerOptics = getText(_turretConfig >> "memoryPointGunnerOptics"); + GVAR(camera) attachTo [_vehicle, _camLocation, _memoryPointGunnerOptics, true]; +}; + +// Setup r2texture +GVAR(camera) cameraEffect ["INTERNAL", "BACK", QGVAR(pip0)]; +private _renderTexture = format ["#(argb,512,512,1)r2t(%1,1)", QGVAR(pip0)]; + +// Create blank display +QGVAR(display) cutRsc [QGVAR(display), "PLAIN", 0, false]; +private _display = uiNamespace getVariable [QGVAR(display), displayNull]; + + +// R2T aspect ratio parameter has no effect - it will match main video AR +// better to have ui elements squashed than to squash the R2T +private _screenAR = getResolution select 4; +private _camEffect = [0]; +private _camFov = 0.75; + + +switch (true) do { + case (_type == "screen"): { + // Generic "Squad Leader's Display" monitor showing turret cam + private _desiredAR = 1.25; + private _stretch = (_desiredAR / _screenAR) max 0.8 min 1.25; // define max stretch factor of pip texture (don't stretch more or less than this) + + private _viewHeight = 0.3 * safeZoneH; + private _viewWidth = _stretch * _viewHeight * _screenAR / 1.3333333333333; + + private _ctrlRender = _display ctrlCreate ["RscPicture", -1]; + _ctrlRender ctrlSetText _renderTexture; + _ctrlRender ctrlSetPosition [safezoneX + 0.5 * safezoneW - 0.5 * _viewWidth, safezoneY + 0.5 * safeZoneH - 0.5 * _viewHeight, _viewWidth, _viewHeight]; + _ctrlRender ctrlCommit 0; + + private _ctrlOverlay = _display ctrlCreate ["RscPicture", -1]; + _ctrlOverlay ctrlSetText "\a3\weapons_f\reticle\data\optika_tv_ca.paa"; + _ctrlOverlay ctrlSetPosition [safezoneX + 0.5 * safezoneW - 0.5 * _viewWidth, safezoneY + 0.5 * safeZoneH - 0.5 * _viewHeight, _viewWidth, _viewHeight]; + _ctrlOverlay ctrlCommit 0; + + if (_usingGoggles) then { + // Screen will be out of focus, too bright and not in IR; should be almost impossible to see anything useful + _camEffect = [3,1,1,0.1,0,[0,0,0,0],[1,1,1,0],[1,1,1,1]]; + + private _ctrlNVG = _display ctrlCreate ["RscPicture", -1]; + _ctrlNVG ctrlSetText "#(argb,8,8,3)color(1,1,0.6,0.9)"; + _ctrlNVG ctrlSetPosition [safezoneX + 0.5 * safezoneW - 0.5 * _viewWidth, safezoneY + 0.5 * safeZoneH - 0.5 * _viewHeight, _viewWidth, _viewHeight]; + _ctrlNVG ctrlCommit 0; + } else { + _camEffect = [0]; // 2.08's currentVisionMode change could allow matching real turret's vision mode + }; + _camFov = 0.25; + }; + default { + // Generic periscope viewport + private _desiredAR = 3; + private _stretch = (_desiredAR / _screenAR) max 0.8 min 1.25; // define max stretch factor of pip texture + private _viewHeight = 0.3 * safeZoneH; + + if (_usingGoggles) then { + _camEffect = [_visionMode]; // pass-thru + // _camEffect = [3, true, 0.747773,0.791092,0,[0,0,0,0],[1.3,1.2,0,0.9],[6,1,1,0]]; + // Some periscope glass is IR Laser Safe (~1064nm) which is close to same wavelength as NVGs + // And cannot apply nvg and ace_nightvision effects to pip at same time, so just make it small and shitty... + _viewHeight = 0.45 * _viewHeight; + }; + private _viewWidth = _stretch * _viewHeight * _screenAR / 1.3333333333333; + + private _ctrlRender = _display ctrlCreate ["RscPicture", -1]; + _ctrlRender ctrlSetText _renderTexture; + _ctrlRender ctrlSetPosition [safezoneX + 0.5 * safezoneW - 0.5 * _viewWidth, safezoneY + 0.5 * safeZoneH - 0.5 * _viewHeight, _viewWidth, _viewHeight]; + _ctrlRender ctrlCommit 0; + + if (_usingGoggles) then { + // Roughly try to color match ace_nvg, and make it semi-opaque + private _ctrlNVG = _display ctrlCreate ["RscPicture", -1]; + _ctrlNVG ctrlSetText "#(argb,8,8,3)color(0.25,0.2,0.05,0.75)"; + _ctrlNVG ctrlSetPosition [safezoneX + 0.5 * safezoneW - 0.5 * _viewWidth, safezoneY + 0.5 * safeZoneH - 0.5 * _viewHeight, _viewWidth, _viewHeight]; + _ctrlNVG ctrlCommit 0; + }; + + private _ctrlOverlay = _display ctrlCreate ["RscPicture", -1]; + _ctrlOverlay ctrlSetText QPATHTOF(data\optic_window_ca.paa); + _ctrlOverlay ctrlSetPosition [safezoneX + 0.5 * safezoneW - 0.5 * _viewWidth, safezoneY + 0.5 * safeZoneH - 0.5 * _viewHeight, _viewWidth, _viewHeight]; + _ctrlOverlay ctrlCommit 0; + }; +}; + +GVAR(camera) camSetFov _camFov; +QGVAR(pip0) setPiPEffect _camEffect; +GVAR(camera) camCommit 0; diff --git a/addons/viewports/functions/script_component.hpp b/addons/viewports/functions/script_component.hpp new file mode 100644 index 0000000000..c4fa73f63a --- /dev/null +++ b/addons/viewports/functions/script_component.hpp @@ -0,0 +1 @@ +#include "\z\ace\addons\viewports\script_component.hpp" diff --git a/addons/viewports/gui.hpp b/addons/viewports/gui.hpp new file mode 100644 index 0000000000..50f036f0da --- /dev/null +++ b/addons/viewports/gui.hpp @@ -0,0 +1,11 @@ +class RscTitles { + class GVAR(display) { + idd = -1; + onLoad = QUOTE( with uiNameSpace do { GVAR(display) = _this select 0 }; ); + movingEnable = 0; + duration = 9999999; + fadeIn = 0; + fadeOut = 0; + class controls {}; + }; +}; diff --git a/addons/viewports/initSettings.sqf b/addons/viewports/initSettings.sqf new file mode 100644 index 0000000000..506d3c78a3 --- /dev/null +++ b/addons/viewports/initSettings.sqf @@ -0,0 +1,9 @@ +[ + QGVAR(enabled), "CHECKBOX", + [LELSTRING(common,Enabled), LLSTRING(setting_enabled_description)], + [ELSTRING(common,ACEKeybindCategoryVehicles), LSTRING(addon_displayname)], + true, + true, + {}, + false // Doesn't need full mission restart, but you have to exit and re-enter vic +] call CBA_fnc_addSetting; diff --git a/addons/viewports/script_component.hpp b/addons/viewports/script_component.hpp new file mode 100644 index 0000000000..587a3d3e4e --- /dev/null +++ b/addons/viewports/script_component.hpp @@ -0,0 +1,18 @@ +#define COMPONENT viewports +#define COMPONENT_BEAUTIFIED Viewports +#include "\z\ace\addons\main\script_mod.hpp" + +// #define DEBUG_MODE_FULL +// #define DISABLE_COMPILE_CACHE +// #define ENABLE_PERFORMANCE_COUNTERS +// #define POINT_CONFIG_DEBUG + +#ifdef DEBUG_ENABLED_VIEWPORTS + #define DEBUG_MODE_FULL +#endif + +#ifdef DEBUG_SETTINGS_VIEWPORTS + #define DEBUG_SETTINGS DEBUG_SETTINGS_VIEWPORTS +#endif + +#include "\z\ace\addons\main\script_macros.hpp" diff --git a/addons/viewports/stringtable.xml b/addons/viewports/stringtable.xml new file mode 100644 index 0000000000..9691b29b85 --- /dev/null +++ b/addons/viewports/stringtable.xml @@ -0,0 +1,11 @@ + + + + + Viewports + + + Allows crew to look through periscopes + + + diff --git a/docs/wiki/feature/viewports.md b/docs/wiki/feature/viewports.md new file mode 100644 index 0000000000..c0e626f07b --- /dev/null +++ b/docs/wiki/feature/viewports.md @@ -0,0 +1,24 @@ +--- +layout: wiki +title: Viewports +description: Allows crew to look through periscopes. +group: feature +category: interaction +parent: wiki +mod: ace +version: + major: 3 + minor: x + patch: y +--- + +## 1. Overview +Allows crew to look through periscopes. + +## 2. Usage +- Hold Ctrl +- Look at a supported periscope or display monitor + +## 3. Dependencies + +{% include dependencies_list.md component="viewports" %} diff --git a/docs/wiki/framework/viewports-framework.md b/docs/wiki/framework/viewports-framework.md new file mode 100644 index 0000000000..b9d456e55a --- /dev/null +++ b/docs/wiki/framework/viewports-framework.md @@ -0,0 +1,56 @@ +--- +layout: wiki +title: Viewports Framework +description: How to configure a vehicle for viewports +group: framework +order: 5 +parent: wiki +mod: ace +version: + major: 3 + minor: x + patch: y +--- + +## 1. Config Values + +Reference [ace_viewports_fnc_getViewports](https://github.com/acemod/ACE3/blob/master/addons/viewports/functions/fnc_getViewports.sqf) + +```cpp +class myVehicle { + class ace_viewports { + class Template { + // type [STRING] - Optional + type = ""; + // camLocation [ARRAY or STRING] - Required + camLocation = "memoryPointP1"; + camLocation[] = {1,2,3}; // model offset + // camAttach [ARRAY or NUMBER] - Required + camAttach[] = {0,0}; // Turret path + camAttach = 55; // Direction in degrees + // screenLocation [ARRAY or STRING] - Optional + screenLocation = "memoryPointP1x"; + screenLocation[] = {1,2,3}; + // maxDistance [NUMBER] - Optional + maxDistance = 0.75; + // compartments [ARRAY] - Optional + compartments[]={"Compartment1"}; + // roles [ARRAY] - Optional + roles[]={"cargo"}; + }; + class periscope_0 { + camLocation[] = {0.987915, -0.324707, -0.0673385}; + camAttach = 70; + roles[]={"cargo"}; + }; + class commandersView { + screenLocation[] = {0.729126,-0.191597,-0.573349}; + maxDistance = 5; + type = "screen"; + camLocation[] = {0,0,0.05}; + camAttach[] = {0,0}; + roles[]={"cargo"}; + }; + }; +}; +``` diff --git a/optionals/compat_rhs_afrf3/CfgVehicles.hpp b/optionals/compat_rhs_afrf3/CfgVehicles.hpp index e1cfee0138..0b4190efb5 100644 --- a/optionals/compat_rhs_afrf3/CfgVehicles.hpp +++ b/optionals/compat_rhs_afrf3/CfgVehicles.hpp @@ -226,11 +226,103 @@ class CfgVehicles { }; class rhs_btr70_vmf: rhs_btr_base { EGVAR(refuel,fuelCapacity) = 350; + class ace_viewports { + class view_0 { + camLocation[] = {0.478394, -0.575, -0.145}; + camAttach = 90; + compartments[]={"Compartment1"}; + roles[]={"cargo"}; + }; + class view_1 { + camLocation[] = {-1.38184, -0.575, -0.145}; + camAttach = -90; + compartments[]={"Compartment1"}; + roles[]={"cargo"}; + }; + }; }; class rhs_btr70_msv: rhs_btr70_vmf {}; class rhs_btr80_msv: rhs_btr70_msv { EGVAR(refuel,fuelCapacity) = 300; + class ace_viewports { + class view_0 { + camLocation[] = {0.534424, -0.336914, 0.636819}; + camAttach = 45; + compartments[]={"Compartment1"}; + roles[]={"cargo"}; + }; + class view_1 { + camLocation[] = {0.760254, -0.459473, 0.526328}; + camAttach = 90; + compartments[]={"Compartment1"}; + roles[]={"cargo"}; + }; + class view_2 { + camLocation[] = {0.770508, -1.21924, 0.526954}; + camAttach = 90; + compartments[]={"Compartment1"}; + roles[]={"cargo"}; + }; + class view_3 { + camLocation[] = {-1.13, -1.2085, 0.490339}; + camAttach = -90; + compartments[]={"Compartment1"}; + roles[]={"cargo"}; + }; + class view_4 { + camLocation[] = {-1.14124, -0.416992, 0.460611}; + camAttach = -90; + compartments[]={"Compartment1"}; + roles[]={"cargo"}; + }; + class view_5 { + camLocation[] = {-0.932983, -0.326172, 0.647666}; + camAttach = -45; + compartments[]={"Compartment1"}; + roles[]={"cargo"}; + }; + }; + }; + class rhs_btr80a_msv: rhs_btr80_msv { + class ace_viewports { + class view_0 { + camLocation[] = {0.589844, -0.314941, 0.449678}; + camAttach = 45; + compartments[]={"Compartment1"}; + roles[]={"cargo"}; + }; + class view_1 { + camLocation[] = {0.809082, -0.442871, 0.276865}; + camAttach = 90; + compartments[]={"Compartment1"}; + roles[]={"cargo"}; + }; + class view_2 { + camLocation[] = {0.819092, -1.24414, 0.27857}; + camAttach = 90; + compartments[]={"Compartment1"}; + roles[]={"cargo"}; + }; + class view_3 { + camLocation[] = {-1.1012, -1.22461, 0.341089}; + camAttach = -90; + compartments[]={"Compartment1"}; + roles[]={"cargo"}; + }; + class view_4 { + camLocation[] = {-1.11597, -0.458984, 0.307256}; + camAttach = -90; + compartments[]={"Compartment1"}; + roles[]={"cargo"}; + }; + class view_5 { + camLocation[] = {-0.869995, -0.304688, 0.461181}; + camAttach = -45; + compartments[]={"Compartment1"}; + roles[]={"cargo"}; + }; + }; }; class rhs_2s3tank_base: Tank_F {