Jump to content
Sign in to follow this  
revide

Tracking Shot Script (Smooth Interpolated Spline shots)

Recommended Posts

Hello fellow scripters!

In my course of developing my eXperience Mod I made a tracking shot script, which creates smooth curves out of 3 or more Waypoints.

 

 

For explanation:

Lets say these are your waypoints for your camera movement:

Arma would do something like this with normal linear camera shots               My script can give you all these points: (cubic spline interpolation)

2ff1e1f190ea45a013eabdfee8087393.png               cfb633484a3a548d43207a31fe3c2f7a.png

Live recorded tracking shot, where the points are calculated by the presented algorithm. Includes all latest features. Acceleration, breaking, for instance.

I calculated the cubic spline interpolation of:
  • X/Y/Z
  • Pitch
  • Bank
  • Rotation (Direction)
How to use it:

 

Implemet all methods in your init.sqf for instance and SPAWN like described below the function with your desired params.

trackingShot = {...};
Perform a tracking shot.     ONLY SPAWN THIS METHOD. IT CONTAINS WAITUNTIL AND SLEEP.
     @params:
          0 - Object: The camera that tracks the motion. (can be objNull)
          1 - Array:     Cam point array:
                    [<pos>,<dir>,<duration>,[,2ndpos,2nddir,2ndcommitTime,2ndduration...]] Position(s) of Camera and look dir, after spawn. Multiple ones for camera movements
                    2ndpos/<pos>:  position of the cam [x,y,z]
                    2nddir/<dir>:  [bank,pitch,dir] of camera
                    2ndduration/<duration>: duration of the cam to stay there before comminting any more actions. Last duration determines when the mission starts.
                    2ndcommitTime: time for the cam to move from 1st pos to 2nd pos. (Due to armas slow sqf execution with sleeps, this is roughly 3x higher in reality.) (2 seconds become 6 seconds..)
          2 - Bool (optional) true: function will take care of creating a cam if needed (if _this select 0 = objNull), switch to it, track the shot and fading out properly. (Player is in internal view afterwards)
  

Example:
[objNull, [[5563.38,3905.44,18.0583],[-0.977274,0,354.317],5,[5469.23,3974.36,60.4686],[-0.977274,0,45.4474],2,0,[5669.23,4274.36,20.4686],[-0.977274,0,272.4474],2,0,[4969.23,5174.36,80.4686],[-0.977274,-35,60.4474],3.5,4], true] spawn trackingShot;
Would create a tracking shot similar (not exact the one, I lost the coords I used) to the one in the video.
The function uses true as 3rd param, to auto switch to the newly created cam, perfom the movement, and smoothly fade out to normal view. (The cam actually breaks before the final point to create a smooth ending.)

 

PLEASE

GIVE CREDIT IF YOU USE THIS SCRIPT. LINK MY STEAM ACCOUNT AND THIS THREAD.

 

I really would appreciate if you add me and share your creation with me.

 

http://steamcommunity.com/id/revide/

 

 

/*
     @params:
          0 - Array: t values (most common timestamps)
          1 - Array: x/y/z Axis coordinates of the points, corresponding to the timestamp


     @return:
          Array - Coefficients _k for the function q that calcs the actual points of a given t.
*/
splineCalcCoefs = {
     private _p = (_this select 1);
     private _v = (_this select 0);
     private _m = [];
     for "_i" from 1 to (count _p) do {
          _m pushBack [];
     };
     private _j = 0;
     private _k = 0;
     private _l = 0;
     private _c = (count _p);
     for "_i" from 0 to (_c-1) do {
          (_m select _i);
          switch(_i) do {
               case 0: {
                    (_m select _i) set [_i,(2/((_v select (_i+1))-(_v select (_i))))];
                    (_m select _i) set [(_i+1),(1/((_v select (_i+1))-(_v select (_i))))];
                    _j = (_v select (_i+1))-(_v select _i);
                    _j = _j*_j;
                    (_m select _i) set [_c,(3*(((_p select (_i+1))-(_p select (_i)))/_j))];
               };
               case (_c-1): {
                    (_m select _i) set [_i,(2/((_v select (_i))-(_v select (_i-1))))];
                    (_m select _i) set [(_i-1),(1/((_v select (_i))-(_v select (_i-1))))];
                    _j = (_v select (_i))-(_v select (_i-1));
                    _j = _j*_j;
                    (_m select _i) set [_c,(3*(((_p select (_i))-(_p select (_i-1)))/_j))];
               };
               default {
                    _j = ((_m select (_i-1)) select _i);
                    _k = (1/((_v select (_i+1))-(_v select (_i))));
                    (_m select _i) set [(_i-1),_j];
                    (_m select _i) set [(_i+1),_k];
                    (_m select _i) set [_i,(2*(_j+_k))];
                    _j = (_v select (_i+1))-(_v select _i);
                    _j = _j*_j;
                    _k = (_v select (_i))-(_v select (_i-1));
                    _k = _k*_k;
                    _l = (_p select _i);
                    (_m select _i) set [_c,(3*(((_l-(_p select (_i-1)))/(_k))+(((_p select (_i+1))-_l)/(_j))))];
               };
          };
     };


     //Calc coef.
     private _cs = [];
     private _ds = [];
     _cs set [0,((_m select 0) select 1)/((_m select 0) select 0)];
     _ds set [0,((_m select 0) select _c)/((_m select 0) select 0)];
     private _row = [];
     _j = 0;
     _k = 0;
     _l = 0;
     for "_i" from 1 to (_c-1) do {
          _row = (_m select _i);
          _j = (_row select (_i-1));
          _k = (_row select _i);
          _l = (_cs select (_i-1));
          _cs set [_i,((_row select _i+1)/(_k-(_l*_j)))];
          _ds set [_i,((_row select _c)-((_ds select (_i-1))*_j))/(_k-_l*_j)];
     };
     _k = [];
     _k set [(_c-1),(_ds select (_c-1))];
     for "_i" from (_c-2) to 0 step -1 do {
          _tmp = ((_ds select _i)-((_cs select _i)*(_k select (_i+1))));
          _k set [_i,_tmp];
     };
     _k
};
/*
     @params:
          0 - Array : Array of the coefficients k. (See cubic spline interpolation for more infos)
          1 - Number : t, the time of the point in question
          2 - Array : x coords of points
          3 - Array : y coords of points
          4 - Number : Current starting index of x;
     @return:
          x/y/z.
*/
calcSplinePoint = {


     private _xi1 = ((_this select 2) select (_this select 4));
     private _xi2 = ((_this select 2) select ((_this select 4)+1));


     private _yi1 = ((_this select 3) select (_this select 4));
     private _yi2 = ((_this select 3) select ((_this select 4)+1));
     private _xs = (_xi2-_xi1);
     private _t = ((_this select 1)-_xi1)/_xs;
     private _t1 = 1-_t;
     private _y = _yi2-_yi1;
     (_t1*_yi1+_t*_yi2+_t*_t1*((((_this select 0) select (_this select 4))*_xs-_y)*_t1+((((-1)*((_this select 0) select ((_this select 4)+1)))*_xs+_y)*_t)))
};

/*
     Perform a tracking shot.
     ONLY SPAWN THIS METHOD. IT CONTAINS WAITUNTIL AND SLEEP.
     @params:
          0 - Object: The camera that tracks the motion.
          1 - Array:     Cam point array:
                    [<pos>,<dir>,<duration>,[,2ndpos,2nddir,2ndcommitTime,2ndduration...]] Position(s) of Camera and look dir, after spawn. Multiple ones for camera movements
                    2ndpos/<pos>:  position of the cam [x,y,z]
                    2nddir/<dir>:  [bank,pitch,dir] of camera
                    2ndduration/<duration>: duration of the cam to stay there before comminting any more actions. Last duration determines when the mission starts.
                    2ndcommitTime: time for the cam to move from 1st pos to 2nd pos. (Due to armas slow sqf execution with sleeps, this is roughly 3x higher in reality.) (2 seconds become 6 seconds..)
          2 - Bool (optional) true: function will take care of creating a cam if needed (if _this select 0 = objNull), switch to it, track the shot and fading out properly.
     @return:
          -
*/

trackingShot = {
     private _cam = (_this select 0);
     private _cA = (_this select 1);
     private _nd = false;
     private _tC = "camera" camCreate [0,0,0];
     if((count _this)  == 3) then {
          if(_this select 2) then {
               if(isNull _cam) then {
                    _cam = "camera" camCreate [0,0,0];
                    _nd = true;
               };
          };
     };
     if (((count _cA) - 3) > 1) then { //need movement
          _cam setPosASL (_cA select 0);
          _cam setDir  ((_cA select 1)select 2);
          [_cam, ((_cA select 1)select 0), ((_cA select 1)select 1)] call BIS_fnc_setPitchBank;
          _cam camSetFov 0.7;
          _cam camCommitPrepared 0;
          _cam camCommit 0;
 
          _tC camSetPos (_cA select 0);
          _tC setDir  ((_cA select 1)select 2);
          [_tC, ((_cA select 1)select 0), ((_cA select 1)select 1)] call BIS_fnc_setPitchBank;
 
          _tC camSetFov 0.7;
          _tC camCommitPrepared 0;
          _tC camCommit 0;
          if (((count _cA) - 3) % 4 == 0) then {     // Valid amount of cams.
 
               private _fps = 90;
               if (((count _cA) - 3) > 4) then {     // More than 2 cam spots, need smoothing.
                    private _t   = [];
                    private _x   = [];
                    private _y   = [];
                    private _z   = [];
                    private _cs  = 0;
                    private _d   = [];
                    private _b   = [];
                    private _p   = [];
                    private _sum = 0;
                    _t pushBack 0;
                    _x pushBack ((_cA select 0)select 0);
                    _y pushBack ((_cA select 0)select 1);
                    _z pushBack ((_cA select 0)select 2);
                    _d pushBack ((_cA select 1)select 2);
                    _p pushBack ((_cA select 1)select 1);
                    _b pushBack ((_cA select 1)select 0);
                    private _ld = (_d select 0);
                    for "_i" from 1 to (((count _cA) -3) / 4) do {
                         _cs = _cs + (_cA select (_i * 4 + 1));
                         _t pushBack _cs;
                         _x pushBack ((_cA select (_i * 4 - 1))select 0);
                         _y pushBack ((_cA select (_i * 4 - 1))select 1);
                         _z pushBack ((_cA select (_i * 4 - 1))select 2);
                         if ((_ld - ((_cA select (_i * 4))select 2)) > 180) then {
                                   _d set [(_i - 1), (_ld - 360)];
                                   _d pushBack ((_cA select (_i * 4))select 2);
                                   _ld = ((_cA select (_i * 4))select 2);
                              } else {
                                   if ((_ld - ((_cA select (_i * 4))select 2)) < 180) then {
                                             _d pushBack (((_cA select (_i * 4))select 2) - 360);
                                             _ld = (((_cA select (_i * 4))select 2) - 360);
                                        } else {
                                             _d pushBack ((_cA select (_i * 4))select 2);
                                             _ld = (((_cA select (_i * 4))select 2));
                                        };
                              };
                         _p pushBack ((_cA select (_i * 4))select 1);
                         _b pushBack ((_cA select (_i * 4))select 0);
                    };
                    private _kx = [_t, _x] call splineCalcCoefs;
                    private _ky = [_t, _y] call splineCalcCoefs;
                    private _kz = [_t, _z] call splineCalcCoefs;
                    private _kd = [_t, _d] call splineCalcCoefs;
                    private _kp = [_t, _p] call splineCalcCoefs;
                    private _kb = [_t, _b] call splineCalcCoefs;
 
                    private _ct2   = 0;
                    private _steps = 0;
                    private _st    = 0;
                    private _sd    = 0;
                    private _ps    = [];
                    private _cm    = ((((count _cA) -3) / 4) - 1);
                    private _is    = 0;private _ic = 0;
                    _cam setPosASL (_cA select 0);
                    _cam setDir ( [_kd, 0.01, _t, _d, 0] call calcSplinePoint);
                    [_cam, ( [_kp, 0.01, _t, _p, 0] call calcSplinePoint), ( [_kb, 0.01, _t, _b, 0] call calcSplinePoint)] call BIS_fnc_setPitchBank;
                    if((count _this)  == 3) then {
                         if(_this select 2) then {
                              _cam switchCamera "INTERNAL";
                         };
                    };
                    if((_cA select 2) > 0) then {sleep (_cA select 2);};
                    for "_i" from 0 to _cm do {
                         _ct2   = (_cA select ((_i + 1) * 4 + 1));
                         _sd    = (1 / (_ct2 * 2 * _fps));
                         _steps = _ct2 * _fps;
                         _sum   = _sum + _steps;
                         _is    = ((_cA select ((_i + 1) * 4 + 2)) > 0);
                         _ic    = ((_cA select (_i*4 + 2)) > 0);
                         for "_j" from 1 to _steps do {
                              waitUntil {camCommitted _tC};
                              if (_is) then { //Brake everytime the next point got stay duration.
                                   if (_j >= _steps * 0.9) then { //Brake
                                             _j = _j - (0.1 + 0.86 * (((_j / _steps) * 10) - 9));
                                   };
                              };
                              if(_ic) then { //Accelerate everytime the current point got a wait.
                                   if (_j <= _steps * 0.1) then { //accelerate
                                        _j = _j - (0.2 + 0.76 * (1-((10*_j)/_steps)));
                                   };
                              };
                              _st = ((_t select _i)+(_j * (((_t select (_i + 1)) - (_t select _i)) / _steps)));
                              _cam setDir ( [_kd, _st, _t, _d, _i] call calcSplinePoint);
                              [_cam, ( [_kp, _st, _t, _p, _i] call calcSplinePoint), ( [_kb, _st, _t, _b, _i] call calcSplinePoint)] call BIS_fnc_setPitchBank;
 
                              _ps = [( [_kx, _st, _t, _x, _i] call calcSplinePoint), ( [_ky, _st, _t, _y, _i] call calcSplinePoint), ( [_kz, _st, _t, _z, _i] call calcSplinePoint)];
                              _cam setPosASL _ps;
 
                              _tC camPreparePos _ps;
                              _tC camCommitPrepared _sd;
                         };
                         if ((_cA select (((_i + 1) * 4) + 2)) > 0) then {
                                   sleep (_cA select (((_i + 1) * 4) + 2));
                              };
                    };
               } else {// Linear Cam movement, just rotation, pitch, bank updates.
                    if((count _this)  == 3) then {
                         if(_this select 2) then {
                              _cam switchCamera "INTERNAL";
                         };
                    };
                    if((_cA select 2) > 0) then {sleep (_cA select 2);};
                    private _ct = (_cA select 5);
 
                    private _steps = _ct * _fps;
 
                    private _oP = ((_cA select 1)select 0);
                    private _oB = ((_cA select 1)select 1);
                    private _oD = ((_cA select 1)select 2);
 
                    private _fB = ((_cA select 4)select 0) - _oB;
                    private _fP = ((_cA select 4)select 1) - _oP;
                    private _fD = abs(_oD - ((_cA select 4)select 2));
 
                    private _oX = ((_cA select 0)select 0);
                    private _oY = ((_cA select 0)select 1);
                    private _oZ = ((_cA select 0)select 2);
 
                    private _fX = (((_cA select 3)select 0) - _oX);
                    private _fY = (((_cA select 3)select 1) - _oY);
                    private _fZ = (((_cA select 3)select 2) - _oZ);
 
                    private _sd = (1 / ((_cA select 5) * 2 * _fps));
 
                    private _vz = -1;
                    if (_fD > 180) then {
                              _vz = 1;
                              _fD = 360 - _fD;
                    };
                    private _st = diag_tickTime;
                    for "_i" from 1 to _steps do {
                         waitUntil {camCommitted _cam};
                         _cam setDir (_vz * _i * (_fD / _steps) + _oD);
                         [_cam, (_i * (_fP / _steps) + _oP), (_i * (_fB / _steps) + _oB)] call BIS_fnc_setPitchBank;
                         _tmp = [_i * (_fX / _steps) + _oX, _i * (_fY / _steps) + _oY, _i * (_fZ / _steps) + _oZ];
                         _cam camPreparePos _tmp;
                         _cam camCommitPrepared _sd;
                    };
                    waitUntil {camCommitted _cam};
               };
          };
          sleep (_cA select ((count _cA) - 1));
 
          if((count _this)  == 3) then {
               if(_this select 2) then {
                    titleText ["", "BLACK", 0.5];
                    sleep 0.5;
                    titleText ["", "BLACK IN", 2];
                    player switchCamera "INTERNAL";
               };
          };
     } else {
          _cam setPosASL (_cA select 0);
          _cam setDir  ((_cA select 1)select 2);
          [_cam, ((_cA select 1)select 0), ((_cA select 1)select 1)] call BIS_fnc_setPitchBank;
          _cam camSetFov 0.7;
          _cam camCommitPrepared 0;
          _cam camCommit 0;
          _cam switchCamera "INTERNAL";
          sleep (_cA select ((count _cA) - 1));
          titleText ["", "BLACK", 0.5];
          sleep 0.5;
          titleText ["", "BLACK IN", 2];
          player switchCamera "INTERNAL";
     };
     camDestroy _tC;
     if(_nd) then {
          camDestroy _cam;
     };
};

 

Changelog

  • 06.07.2016 (07/06/16 if your american... :D):
    • [FIXED] Minor bugfix of the temp Cam not getting destroy correctly
    • [ADDED] Breaking of Waypoints, if the next approaching waypoint got a set duration of >0. When approaching, the trackingShot method will auto slow down smoothly before reaching it
    • [ADDED] Acceleration of Waypoints, if a the current Waypoint got a duration >0 and the cam stayed there. Smoothly accelerates.
    • [FIXED] Removed a unused variable which was for performance tests.

 

//Edit: fixed tabs to spaces and code in spoiler

//Edit2: Added wikipedia link

//Edit3: Added youtube video

//Edit4: Added Example Usage

//Edit5: Added a method to preview your shots. Just feed the function with proper arrays.

//Edit6: Released update, check changelog

//Edit7: New Video added, better quality, better frames, showing all latest features.

 

To fully understand why the function does what it does and what the _k array is, give Wikipedia a good read.

 

 

Please leave your feedback / suggestions.

  • Like 10

Share this post


Link to post
Share on other sites

Revamped the Thread and added example function for everybody to test cubic spline interpolated tracking shots for themselves!

Also added/fixed/changed:

- Auto break before the last point to smooth things out in the end.

- Fixed minor unneccessary referencing (improving performance)

- Changed minor scripts.

Every cinematic creator, who doesn't have a good controller and good movement skills should check this thread out.

With this, one can create professional cinematics within seconds.

What comes next:

- GUI Mission, to fly around and add/edit camera points

- Preview your spline path in 3D world with drawLine and drawIcon.

- Live preview your shot with your selected properties.

Stay tuned and enjoy!

Share this post


Link to post
Share on other sites

Alfonso Cuarón would be proud!

 

That's a good compliment man, thanks :D

Share this post


Link to post
Share on other sites

Exactly what I was looking for and works great.

 

Would you consider doing an orbit cam option as well?

Share this post


Link to post
Share on other sites

Exactly what I was looking for and works great.
 
Would you consider doing an orbit cam option as well?

 

An orbit cam kind of already exist, if you search for a UAV cinamatic.

Anyway. If I've got enough motivation, im gonna add more options for the points. For instance:

 

  • Should the point be part of spline interpolation or just plain linear (In case someone wants a smooth track to a linear shot, like following a plane)
  • The UAV cam mode, with a target, or more a point as target. (The rotation, pitch, bank would have to be updated everytime, math with cos(x) should be handy in here, but I'm not 100% sure.)
  • If the point got a break to it, the cam should slowly approch it in the last 10% of time to give a feel of a smooth approach [NOW AVIABLE]
  • When exiting a break Waypoint, the cam should accelerate smoothly (Opposite to the brake) [NOW AVIABLE]

 

//Edit the latter 2 are now implemented. With the duration param you can control the acceleration and braking.

 

And like i stated in my post before, a Cinamatic Editor missions, where you can fly around, place, edit and preview your Waypoints and the cam path the algorithm would take :)

Share this post


Link to post
Share on other sites

Sounds good. Thanks for the reply.

  • Like 1

Share this post


Link to post
Share on other sites

Sounds good. Thanks for the reply.

You're welcome.

If you have any further suggestions / feedback you have in mind, please, let me know.

Share this post


Link to post
Share on other sites

First instance I've seen Calculus used in arma 3. Impressive work.

In my study I got quite a bit of math. I thought this would be a nice little challange. Was fun to make!

  • Like 1

Share this post


Link to post
Share on other sites

First instance I've seen Calculus used in arma 3. Impressive work.

 

Quite a lot of stuff requires math, be it CCIP, low angle fire for artillery or anything physics related (object movement, sun angle calculations based on map data come to my mind).

For the most basic stuff you don't need all that.

 

Cheers

Share this post


Link to post
Share on other sites

Quite a lot of stuff requires math, be it CCIP, low angle fire for artillery or anything physics related (object movement, sun angle calculations based on map data come to my mind).

For the most basic stuff you don't need all that.

 

Cheers

 

Well, solving matrix with Thomas Algorithm is another story then executing a single function based on velocity, for instance.

Share this post


Link to post
Share on other sites

Hey,

 

very cool script! Is there any way to add cinematicBorders with 

showCinemaBorder=true;
Cheers

 

Good point. Will implement that to the "Version 2". Feel free to modify the script. Just show the cinematic borders with scripting commands

showCinemaBorder true; //On Start

[...] call trackingShot;

showCinemaBorder false; //On End

That should give the desired effect.

 

 

Cheers

Share this post


Link to post
Share on other sites

Okey guys, I'm working now on:

 

 

  • 3D "Editor" to easily setup,preview,export cinematic shots
  • New modes:
  • Smooth transfer to follow a unit/vehicle at a given angle
  • Smooth movement WHILE following a unit/vehicle
  • UAV Mode
  • Better performance
  • Easier access with clean HUD/GUI Elements in the editor.

 

And I will create a new thread for that "v2" version to give it a better name and promote a bit more 

 

Stay tuned. cheers

  • Like 1

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  

×