{
  "name": "Frame Flip",
  "duration": 1.0,
  "curve": "Linear None",
  "type": "fixed-intro",
  "passes": [
    {
      "name": "Frame Flip",
      "inputs": [
        "default"
      ],
      "glsl": "precision highp float;

varying vec2 textureCoordinate;
varying vec2 sourceTextureCoordinate;

// default uniforms for all filters
uniform float time_sec;
uniform float strength;
uniform vec2 outputSize; // size of the output fbo
uniform vec2 inputSize; // size of the original default input texture
uniform vec2 tex0Size; // size of tex0 for this pass

// default uniforms for transitions and video effects
uniform float progress; // normalized 0-1

// default uniforms for video effects
uniform float duration; // in seconds
uniform float clipDuration; // in seconds

uniform float depth;
uniform float shiftCenterX;
uniform float shiftCenterY;

uniform float P0;
uniform float P1;
uniform float P2;
uniform float P3;
uniform float P4;
uniform float P5;

uniform float Y0;
uniform float Y1;
uniform float Y2;
uniform float Y3;
uniform float Y4;
uniform float Y5;

uniform float R0;
uniform float R1;
uniform float R2;
uniform float R3;
uniform float R4;
uniform float R5;

uniform float S0;
uniform float S1;
uniform float S2;
uniform float S3;
uniform float S4;

uniform float SY0;
uniform float SY1;
uniform float SY2;
uniform float SY3;
uniform float SY4;

const float T0 = 0.0;
const float T1 = 4.0/30.0;
const float T2 = 8.0/30.0;
const float T3 = 8.01/30.0;
const float T4 = 15.0/30.0;
const float T5 = 1.0;

float catmullRom(float a, float b, float c, float d, float t){
  float t2 = t*t;
  float t3 = t2 * t;
  return 0.5 * ((2.0 * b) + (-a + c) * t + (2.0 * a - 5.0 * b + 4.0 * c - d) * t2 + (-a + 3.0 * b - 3.0 * c + d) * t3);
}

float getPitch(float p){
  if(p < T1){
    float u = (p - T0) / (T1 - T0);
    return catmullRom(P0,P0,P1,P2,u);
  } else if (p < T2){
    float u = (p - T1) / (T2 - T1);
    return catmullRom(P0,P1,P2,P3,u);
  } else if (p < T3){
    float u = (p - T2) / (T3 - T2);
    return catmullRom(P1,P2,P3,P4,u);
  } else if (p < T4){
    float u = (p - T3) / (T4 - T3);
    return catmullRom(P2,P3,P4,P5,u);
  } else {
    float u = (p - T4) / (T5 - T4);
    return catmullRom(P3,P4,P5,P5,u);
  }
}

float getYaw(float p){
  if(p < T1){
    float u = (p - T0) / (T1 - T0);
    return catmullRom(Y0,Y0,Y1,Y2,u);
  } else if (p < T2){
    float u = (p - T1) / (T2 - T1);
    return catmullRom(Y0,Y1,Y2,Y3,u);
  } else if (p < T3){
    float u = (p - T2) / (T3 - T2);
    return catmullRom(Y1,Y2,Y3,Y4,u);
  } else if (p < T4){
    float u = (p - T3) / (T4 - T3);
    return catmullRom(Y2,Y3,Y4,Y5,u);
  } else {
    float u = (p - T4) / (T5 - T4);
    return catmullRom(Y3,Y4,Y5,Y5,u);
  }
}

float getRoll(float p){
  if(p < T1){
    float u = (p - T0) / (T1 - T0);
    return catmullRom(R0,R0,R1,R2,u);
  } else if (p < T2){
    float u = (p - T1) / (T2 - T1);
    return catmullRom(R0,R1,R2,R3,u);
  } else if (p < T3){
    float u = (p - T2) / (T3 - T2);
    return catmullRom(R1,R2,R3,R4,u);
  } else if (p < T4){
    float u = (p - T3) / (T4 - T3);
    return catmullRom(R2,R3,R4,R5,u);
  } else {
    float u = (p - T4) / (T5 - T4);
    return catmullRom(R3,R4,R5,R5,u);
  }
}

float getScale(float p){
  if(p < T1){
    float u = (p - T0) / (T1 - T0);
    return catmullRom(S0,S0,S1,S2,u);
  } else if (p < T2){
    float u = (p - T1) / (T2 - T1);
    return catmullRom(S0,S1,S2,S3,u);
  } else if (p < T3){
    float u = (p - T2) / (T4 - T2);
    return catmullRom(S1,S2,S3,S4,u);
  } else {
    float u = (p - T4) / (T5 - T4);
    return catmullRom(S2,S3,S4,S4,u);
  }
}

float getScaleY(float p){
  if(p < T1){
    float u = (p - T0) / (T1 - T0);
    return catmullRom(SY0,SY0,SY1,SY2,u);
  } else if (p < T2){
    float u = (p - T1) / (T2 - T1);
    return catmullRom(SY0,SY1,SY2,SY3,u);
  } else if (p < T3){
    float u = (p - T2) / (T4 - T2);
    return catmullRom(SY1,SY2,SY3,SY4,u);
  } else {
    float u = (p - T4) / (T5 - T4);
    return catmullRom(SY2,SY3,SY4,SY4,u);
  }
}



// effect uniforms
void main() {
  vec2 uv = sourceTextureCoordinate;

  float centerShift = mix(shiftCenterX,0.0, progress)/2.0;

  float p = clamp(progress, 0.0, 1.0);
  vec2 scale = vec2(max(getScale(p), 0.001));

  float pitch = radians(getPitch(p));
  float yaw = radians(getYaw(p));
  float roll = radians(getRoll(p));


  vec2 ndc = uv * 2.0 - 1.0;
  vec3 ray = normalize(vec3(ndc, 1.0));

  float crz = cos(roll);
  float srz = sin(roll);

  float cx = cos(pitch);
  float sx = sin(pitch);
  float cy = cos(yaw);
  float sy = sin(yaw);

  mat3 Ry = mat3(cy, 0.0, sy, 0.0, 1.0, 0.0, -sy, 0.0, cy);
  mat3 Rx = mat3(1.0, 0.0, 0.0, 0.0, cx, -sx, 0.0, sx, cx);
  mat3 Rz = mat3(crz, -srz, 0.0, srz, crz, 0.0, 0.0, 0.0, 1.0);
  mat3 R = Rx * Ry * Rz;

  vec3 origin = vec3(0.0,0.0,0.0);
  vec3 camera = vec3(centerShift,shiftCenterY,depth);
  vec3 originNew = R * (origin - camera) + camera;
  vec3 newRay = R * ray;
  float t = (depth - originNew.z) / max(newRay.z, 1e-4);
  vec3 hit = originNew + newRay * t;
  vec3 axisX = R * vec3(1.0,0.0,0.0);
  vec3 axisY = R * vec3(0.0,1.0,0.0);
  vec2 plane = vec2(dot(hit - camera, axisX), dot(hit - camera, axisY));

  scale.x = scale.y * (max(getScaleY(p), 0.001));
  uv = plane * 0.5 + 0.5;
  uv = (uv - 0.5) / scale + 0.5;

  vec4 base =  sampleInput(uv);
  float m = step(0.0, uv.x) * step(uv.x, 1.0)
  * step(0.0, uv.y) * step(uv.y, 1.0);


  vec4 color = vec4((base * m));
  gl_FragColor = toOutputFormat(color);
}
",
      "metal": "
using namespace metal;
struct fragmentIn { float2 textureCoordinate [[user(locn0)]]; float2 sourceTextureCoordinate [[user(locn1)]]; };
struct fragmentOut { float4 _gl_FragColor [[color(0)]]; };
typedef struct {
  float4x4 content_transform;
  float4x4 texture_transform;
  float strength;
  float progress;
  float time_sec;
  float duration_sec;
  float clip_duration_sec;
  float2 input_size;
  float2 output_size;
  float2 tex0Size;
  float depth;
  float shiftCenterX;
  float shiftCenterY;
  float P0;
  float P1;
  float P2;
  float P3;
  float P4;
  float P5;
  float Y0;
  float Y1;
  float Y2;
  float Y3;
  float Y4;
  float Y5;
  float R0;
  float R1;
  float R2;
  float R3;
  float R4;
  float R5;
  float S0;
  float S1;
  float S2;
  float S3;
  float S4;
  float SY0;
  float SY1;
  float SY2;
  float SY3;
  float SY4;
 } Uniforms;
constant float T0 = 0.0;
constant float T1 = 4.0/30.0;
constant float T2 = 8.0/30.0;
constant float T3 = 8.01/30.0;
constant float T4 = 15.0/30.0;
constant float T5 = 1.0;

float catmullRom(float a, float b, float c, float d, float t){
  float t2 = t*t;
  float t3 = t2 * t;
  return 0.5 * ((2.0 * b) + (-a + c) * t + (2.0 * a - 5.0 * b + 4.0 * c - d) * t2 + (-a + 3.0 * b - 3.0 * c + d) * t3);
}

float getPitch(float p, constant Uniforms & uniforms){
  if(p < T1){
    float u = (p - T0) / (T1 - T0);
    return catmullRom(uniforms.P0,uniforms.P0,uniforms.P1,uniforms.P2,u);
  } else if (p < T2){
    float u = (p - T1) / (T2 - T1);
    return catmullRom(uniforms.P0,uniforms.P1,uniforms.P2,uniforms.P3,u);
  } else if (p < T3){
    float u = (p - T2) / (T3 - T2);
    return catmullRom(uniforms.P1,uniforms.P2,uniforms.P3,uniforms.P4,u);
  } else if (p < T4){
    float u = (p - T3) / (T4 - T3);
    return catmullRom(uniforms.P2,uniforms.P3,uniforms.P4,uniforms.P5,u);
  } else {
    float u = (p - T4) / (T5 - T4);
    return catmullRom(uniforms.P3,uniforms.P4,uniforms.P5,uniforms.P5,u);
  }
}

float getYaw(float p, constant Uniforms & uniforms){
  if(p < T1){
    float u = (p - T0) / (T1 - T0);
    return catmullRom(uniforms.Y0,uniforms.Y0,uniforms.Y1,uniforms.Y2,u);
  } else if (p < T2){
    float u = (p - T1) / (T2 - T1);
    return catmullRom(uniforms.Y0,uniforms.Y1,uniforms.Y2,uniforms.Y3,u);
  } else if (p < T3){
    float u = (p - T2) / (T3 - T2);
    return catmullRom(uniforms.Y1,uniforms.Y2,uniforms.Y3,uniforms.Y4,u);
  } else if (p < T4){
    float u = (p - T3) / (T4 - T3);
    return catmullRom(uniforms.Y2,uniforms.Y3,uniforms.Y4,uniforms.Y5,u);
  } else {
    float u = (p - T4) / (T5 - T4);
    return catmullRom(uniforms.Y3,uniforms.Y4,uniforms.Y5,uniforms.Y5,u);
  }
}

float getRoll(float p, constant Uniforms & uniforms){
  if(p < T1){
    float u = (p - T0) / (T1 - T0);
    return catmullRom(uniforms.R0,uniforms.R0,uniforms.R1,uniforms.R2,u);
  } else if (p < T2){
    float u = (p - T1) / (T2 - T1);
    return catmullRom(uniforms.R0,uniforms.R1,uniforms.R2,uniforms.R3,u);
  } else if (p < T3){
    float u = (p - T2) / (T3 - T2);
    return catmullRom(uniforms.R1,uniforms.R2,uniforms.R3,uniforms.R4,u);
  } else if (p < T4){
    float u = (p - T3) / (T4 - T3);
    return catmullRom(uniforms.R2,uniforms.R3,uniforms.R4,uniforms.R5,u);
  } else {
    float u = (p - T4) / (T5 - T4);
    return catmullRom(uniforms.R3,uniforms.R4,uniforms.R5,uniforms.R5,u);
  }
}

float getScale(float p, constant Uniforms & uniforms){
  if(p < T1){
    float u = (p - T0) / (T1 - T0);
    return catmullRom(uniforms.S0,uniforms.S0,uniforms.S1,uniforms.S2,u);
  } else if (p < T2){
    float u = (p - T1) / (T2 - T1);
    return catmullRom(uniforms.S0,uniforms.S1,uniforms.S2,uniforms.S3,u);
  } else if (p < T3){
    float u = (p - T2) / (T4 - T2);
    return catmullRom(uniforms.S1,uniforms.S2,uniforms.S3,uniforms.S4,u);
  } else {
    float u = (p - T4) / (T5 - T4);
    return catmullRom(uniforms.S2,uniforms.S3,uniforms.S4,uniforms.S4,u);
  }
}

float getScaleY(float p, constant Uniforms & uniforms){
  if(p < T1){
    float u = (p - T0) / (T1 - T0);
    return catmullRom(uniforms.SY0,uniforms.SY0,uniforms.SY1,uniforms.SY2,u);
  } else if (p < T2){
    float u = (p - T1) / (T2 - T1);
    return catmullRom(uniforms.SY0,uniforms.SY1,uniforms.SY2,uniforms.SY3,u);
  } else if (p < T3){
    float u = (p - T2) / (T4 - T2);
    return catmullRom(uniforms.SY1,uniforms.SY2,uniforms.SY3,uniforms.SY4,u);
  } else {
    float u = (p - T4) / (T5 - T4);
    return catmullRom(uniforms.SY2,uniforms.SY3,uniforms.SY4,uniforms.SY4,u);
  }
}



// effect uniforms
fragment fragmentOut fragmentShader(fragmentIn in [[stage_in]],
 constant Uniforms & uniforms [[buffer(1)]],
 DefaultInputs defaultInputs) {
  float2 uv = in.sourceTextureCoordinate;

  float centerShift = mix(uniforms.shiftCenterX,0.0, uniforms.progress)/2.0;

  float p = clamp(uniforms.progress, 0.0, 1.0);
  float2 scale = float2(max(getScale(p, uniforms), 0.001));

  float pitch = (getPitch(p, uniforms) * M_PI_F / 180.0);
  float yaw = (getYaw(p, uniforms) * M_PI_F / 180.0);
  float roll = (getRoll(p, uniforms) * M_PI_F / 180.0);


  float2 ndc = uv * 2.0 - 1.0;
  float3 ray = normalize(float3(ndc, 1.0));

  float crz = cos(roll);
  float srz = sin(roll);

  float cx = cos(pitch);
  float sx = sin(pitch);
  float cy = cos(yaw);
  float sy = sin(yaw);

  float3x3 Ry = float3x3(cy, 0.0, sy, 0.0, 1.0, 0.0, -sy, 0.0, cy);
  float3x3 Rx = float3x3(1.0, 0.0, 0.0, 0.0, cx, -sx, 0.0, sx, cx);
  float3x3 Rz = float3x3(crz, -srz, 0.0, srz, crz, 0.0, 0.0, 0.0, 1.0);
  float3x3 R = Rx * Ry * Rz;

  float3 origin = float3(0.0,0.0,0.0);
  float3 camera = float3(centerShift,uniforms.shiftCenterY,uniforms.depth);
  float3 originNew = R * (origin - camera) + camera;
  float3 newRay = R * ray;
  float t = (uniforms.depth - originNew.z) / max(newRay.z, 1e-4);
  float3 hit = originNew + newRay * t;
  float3 axisX = R * float3(1.0,0.0,0.0);
  float3 axisY = R * float3(0.0,1.0,0.0);
  float2 plane = float2(dot(hit - camera, axisX), dot(hit - camera, axisY));

  scale.x = scale.y * (max(getScaleY(p, uniforms), 0.001));
  uv = plane * 0.5 + 0.5;
  uv = (uv - 0.5) / scale + 0.5;

  float4 base =  sampleInput(defaultInputs, uv);
  float m = step(0.0, uv.x) * step(uv.x, 1.0)
  * step(0.0, uv.y) * step(uv.y, 1.0);


  float4 color = float4((base * m));
  return {toOutputFormat(color)};
}
  ",
      "uniforms": {
        "P0": {
          "value": 176.4
        },
        "P1": {
          "value": 206.0
        },
        "P2": {
          "value": 208.0
        },
        "P3": {
          "value": 188.0
        },
        "P4": {
          "value": 180.0
        },
        "P5": {
          "value": 180.0
        },
        "Y0": {
          "value": 271.0
        },
        "Y1": {
          "value": 171.0
        },
        "Y2": {
          "value": 90.0
        },
        "Y3": {
          "value": -90.0
        },
        "Y4": {
          "value": -160.0
        },
        "Y5": {
          "value": -180.0
        },
        "R0": {
          "value": 0.0
        },
        "R1": {
          "value": 37.0
        },
        "R2": {
          "value": 23.0
        },
        "R3": {
          "value": -23.0
        },
        "R4": {
          "value": 0.0
        },
        "R5": {
          "value": 0.0
        },
        "S0": {
          "value": 0.0
        },
        "S1": {
          "value": 0.513
        },
        "S2": {
          "value": 0.725
        },
        "S3": {
          "value": 0.95
        },
        "S4": {
          "value": 1.0
        },
        "SY0": {
          "value": 0.0
        },
        "SY1": {
          "value": 1.0
        },
        "SY2": {
          "value": 0.5
        },
        "SY3": {
          "value": 1.0
        },
        "SY4": {
          "value": 1.0
        },
        "depth": {
          "value": 1.0
        },
        "shiftCenterX": {
          "value": 0.2
        },
        "shiftCenterY": {
          "value": 0.0
        }
      }
    }
  ]
}
