--[[
    jellyfish_scene.lua

    The main file where all the jellyfish-related action occurs.
    Includes all the positions and dance steps
    TODO: move these array decls out into a separate file.
]]
jellyfish_scene = {}

local openGL = require("opengl")
local ffi = require("ffi")
local mm = require("util.matrixmath")

local head_scene = require("scene.jellyfish_head")
local tentacle_scene = require("scene.jellyfish_tentacles")
local font_scene = require("scene.font_scene")

-- Types from:
-- https://github.com/nanoant/glua/blob/master/init.lua
local glIntv     = ffi.typeof('GLint[?]')
local glUintv    = ffi.typeof('GLuint[?]')
local glCharv    = ffi.typeof('GLchar[?]')
local glSizeiv   = ffi.typeof('GLsizei[?]')
local glFloatv   = ffi.typeof('GLfloat[?]')
local glConstCharpp = ffi.typeof('const GLchar *[1]')

jellyfish_scene.wave_ampl = .1

jellyfish_scene.animate = true
jellyfish_scene.conga_t = 0
jellyfish_scene.random_t = 0
jellyfish_scene.standard_kfs_idx = 1
jellyfish_scene.chorusline_kfs_idx = 1
jellyfish_scene.do_random_individual_kfs = true
jellyfish_scene.draw_lead_guy = false
jellyfish_scene.draw_text_labels = true
jellyfish_scene.draw_banner_holders = false
jellyfish_scene.banner1 = {0,0,0}
jellyfish_scene.banner_msg_idx = 1

local scene_names = {
    "__lead guy__",
    "Metoikos",
    "micksam7",
    "Madbrain",
    "impakt",
    "ruinah",
    "sigbit",
    "Dr. Claw",
    "Blackpawn",
    "Luis",
    "coda",
    "Trixter",
    "pointinpolygon",
    "nom de nom",
    "phoenix",
}
local scene_names2 = {
    "spektre",
    "Sigflup",
    "I.C.",
    "polaris",
    "alfred",
    "Kevman",
    "Danukeru",
    "Steph",
    "bathsheba",
    "czery",
    "daverd",
    "nrr",
    "illogicalcat",
    "krue",
    "blacklight",
    "havoc",
    "Ziphoid",
    "BarZoule",
}
local banner_messages = {
    "This demo was made entirely in luajit",
    "It even runs on Linux!",
    "Support open standards! Join your local Khronos chapter!",
    --"How do I do antialiasing with this thing?",
    "@party is made possible by the tireless efforts of Metoikos and Dr. Claw.",
    "If you can read this, buy Metoikos and Dr. Claw each a beer!",
    "If you can read this, buy Metoikos and Dr. Claw each two beers!!",
    "      !",
    "",
}

local function floatv(list)
    return glFloatv(#list, list)
end

local rest = {-7,5,0, -5,2,0, 0,0,0, 0,5,0}
pts_rest = floatv(rest)

jellyfish_scene.global_manual = floatv({-7,5,0, -5,2,0, 0,0,0, 0,5,0,})
jellyfish_scene.global_manual_arms = floatv({-7,5,0, -5,2,0, 0,0,0, 0,5,0,})
jellyfish_scene.global_legs = floatv({-7,5,0, -5,2,0, 0,0,0, 0,5,0,})
jellyfish_scene.global_arms = floatv({-7,5,0, -5,2,0, 0,0,0, 0,5,0,})
jellyfish_scene.global_individual_splines = {
    floatv(rest), floatv(rest), floatv(rest), floatv(rest),
    floatv(rest), floatv(rest), floatv(rest), floatv(rest),
}

--[[
    Per-tentacle keyframes
    Determined by manual input to the above arrays, each one represents the 4 points
    of a Catmull-Rom spline to describe a tentacle's pose.
    Sequences are composed of a list of these for each of the 8 tentacles in a jellyfish.
]]
pts_hand_clap1 = floatv({0.5,13,-3.2,-1.1,3.0,1.0,0,0,0,3.0,-16.6,0,0,})
pts_hand_clap2 = floatv({0.8,6.2,-4.8,-1.3,2.8,-0.7,0,0,0,0.4,-10.6,0,0,})
pts_shimmyl = floatv({-9.000,12.600,18.000,-5.000,2.000,0.000,0.000,0.000,0.000,0.000,5.000,0.000,})
pts_shimmyr = floatv({-9.000,12.600,-18.000,-5.000,2.000,0.000,0.000,0.000,0.000,0.000,5.000,0.000,})
pts_barb = floatv({10.600,19.900,0.200,-5.000,2.000,0.000,0.000,0.000,0.000,0.000,5.000,0.000,})
pts_shoul = floatv({-27.100,-14.400,0.000,-5.000,2.000,0.000,0.000,0.000,0.000,0.000,5.000,0.000,})
pts_ankl = floatv({25.300,6.300,0.000,-5.000,2.000,0.000,0.000,0.000,0.000,0.000,5.000,0.000,})
pts_feet_bounce1 = floatv({-13.3,9.9,0,-7,3,0,0,0,0,0,5,0,0,})
pts_feet_bounce2 = floatv({-31.0,-15.8,0,-7,3,0,0,0,0,0,5,0,0,})
pts_elbowup = floatv({-27.6,-0.6, 0.0,-1.4, 2.3, 0.0, 0.0, 0.0, 0.0,17.9,-2.5, 0.0,})
pts_curlback = floatv({-3.8,-13.2, 0.0,-0.3, 0.6, 0.5, 0.0, 0.0, 0.0,21.5,-5.7, 0.0,})
_point = floatv({15.9,-0.1, 7.6, 2.6, 2.7,-0.6, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0,})
_shup = floatv({-36.0,-5.2, 0.0,-5.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0,})
_shdown = floatv({ 6.8, 9.6, 0.0,-5.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0,})
_hipsin = floatv({-11.0,14.4, 0.0,-4.8, 2.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0,})
_hipsout = floatv({-17.4,-24.0, 0.0,-4.8, 2.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0,})
_waveleft = floatv({-7.9,12.7,-10.5,-0.6, 3.6, 0.0, 0.0, 0.0, 0.0,15.0,-13.7,10.2,})
_waveright = floatv({-7.9,12.7, 2.1,-0.6, 3.8,-0.5, 0.0, 0.0, 0.0,15.0,-13.7,-7.8,})
_toein = floatv({-10.3, 3.6, 0.0,-5.2,-0.3, 0.0, 0.0, 0.0, 0.0, 7.9,-13.9, 0.0,})
_toeout = floatv({-16.8, 8.9, 1.4,-4.7, 3.4, 0.0, 0.0, 0.0, 0.0,20.3,-6.9, 0.0,})
_kickup = floatv({ 5.5, 5.9, 0.0, 2.3, 4.5, 0.0, 0.0, 0.0, 0.0,12.2,-13.2, 0.0,})
_kickknee = floatv({-25.2, 5.4, 0.0,-0.9, 4.1, 0.0, 0.0, 0.0, 0.0,12.2,-13.2, 0.0,})
_kickdown = floatv({-10.9, 3.6, 0.0,-5.5, 1.8, 0.0, 0.0, 0.0, 0.0,10.8,-5.4, 0.0,})
_clawclosed = floatv({-8.2,-7.5, 0.0,-4.6,-0.7, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0,})
_clawopen = floatv({-8.2,-7.5, 0.0,-3.5, 2.1, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0,})
_clawwild = floatv({-17.7,-12.7,-0.8,-0.2, 2.8,-3.4, 0.0, 0.0, 0.0,17.8,-8.5, 0.0,})
_discoup = floatv({28.0,-7.3, 0.0, 3.9, 0.5,-2.6, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0,})
_discodown = floatv({-6.9, 4.1, 0.0,-4.2, 0.2, 1.9, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0,})
_swimin = floatv({-15.6,-25.9, 0.0,-3.4, 0.6, 0.0, 0.0, 0.0, 0.0, 1.0, 3.8, 0.0,})
_swimout = floatv({-30.8,-6.5, 0.0,-5.8, 0.3, 0.0, 0.0, 0.0, 0.0, 3.8,-2.4, 0.0,})
_tapered = floatv({-20.3, 0.8, 0.0,-5.9,-0.6, 0.0, 0.0, 0.0, 0.0,-3.3,-5.4, 0.0,})
_tapered2 = floatv({-15.3, 0.5, 0.0,-4.6,-0.1, 0.0, 0.0, 0.0, 0.0, 2.2,-5.9, 0.0,})
_conga_arms_out = floatv({-0.7, 4.6, 3.6,-0.3, 2.3, 0.6, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0,})
_conga_legs_right = floatv({-15.0,-0.4,23.1,-5.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0,})
_conga_legs_left = floatv({-15.0,-0.4,-25.6,-5.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0,})
_conga_barb1 = floatv({-0.8,15.7, 0.0,-5.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0,})
_conga_barb2 = floatv({-9.8,28.4, 0.0,-5.0, 1.6, 0.0, 0.0, 0.0, 0.0, 3.8,-10.8, 0.0,})
_thriller_legs = floatv({-7.2, 6.6, 0.0,-5.0, 2.0, 0.0, 0.0, 0.0, 0.0, 5.7,-5.7, 0.0,})
_thriller_arms = floatv({-13.5, 2.1, 6.7, 0.1, 3.3, 0.0, 0.0, 0.0, 0.0,16.5, 6.0, 0.0,})
_highclap_out_legs = floatv({-6.7, 5.8, 0.0,-5.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0,})
_highclap_out_arms = floatv({ 9.3, 0.8, 0.0,-0.6, 3.6,-2.6, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0,})
_highclap_in_legs = floatv({-6.7, 5.8, 0.0,-5.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0,})
_highclap_in_arms = floatv({ 6.3, 4.2, 4.5, 3.3, 3.1, 1.6, 0.0, 0.0, 0.0, 3.4,-8.2, 0.0,})
_wave_arms_1 = floatv({-20.2,-14.3,-14.5,-1.4, 2.1,-2.6, 0.0, 0.0, 0.0,13.9, 5.8, 0.0,})
_wave_arms_2 = floatv({ 8.5,11.3,-14.5,-0.3, 2.5,-2.6, 0.0, 0.0, 0.0, 1.5,-14.3, 0.0,})
_knee_in = floatv({-7.0, 5.0, 0.0,-5.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0,})
_knee_out = floatv({-36.3,-17.5, 0.0,-5.1, 1.4, 0.0, 0.0, 0.0, 0.0,27.1, 1.5, 0.0,})
_punch_out = floatv({ 0.4, 2.4, 0.1,-0.3, 3.0, 1.2, 0.0, 0.0, 0.0, 0.3, 3.2, 4.2,})
_punch_chamber = floatv({ 1.1, 4.8, 3.8,-0.7, 0.8, 0.4, 0.0, 0.0, 0.0, 2.7, 8.4, 2.6,})
_thigh_out = floatv({-14.8,16.8, 0.0,-5.0, 2.0, 0.0, 0.0, 0.0, 0.0,13.9,-27.9, 0.0,})
_run_down = floatv({-6.0, 4.7, 0.0,-5.3, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0,})
_run_up = floatv({-14.4,-17.8, 0.0,-3.2, 0.4, 0.0, 0.0, 0.0, 0.0, 4.4,-6.2, 0.0,})

jellyfish_scene.standard_kfs = {
    {
        legs={pts_shimmyl, pts_barb, pts_shimmyr, pts_barb, xth=4},
        hands={pts_hand_clap2, pts_hand_clap2, pts_hand_clap1, pts_hand_clap1, xth=8}, -- not sure why this is on 2&4...
    },
    {
        legs={_conga_barb1, _conga_barb2, _conga_barb1, _conga_legs_right,
              _conga_barb1, _conga_barb2, _conga_barb1, _conga_legs_left, xth=4},
        hands={_conga_arms_out,_conga_arms_out,_conga_arms_out,_conga_arms_out, xth=8}, -- not sure why this is on 2&4...
    },
    {
        legs={_swimin, _tapered, xth=4},
        hands={_swimin, _tapered, xth=4},
    },
    {
        legs={_swimout,_swimin,_swimin,_swimin, xth=16},
        hands={_swimout,_swimin,_swimin,_swimin, xth=16},
    },
    {
        legs={_swimin, _swimout, xth=4},
        hands={_swimin, _swimout, xth=4},
    },
}

--[[
    Dance steps
    Per-tentacle arrays of spline arrays
]]
local dance_chorus_line = 
{
    {_kickknee, _tapered2, _kickup, _tapered2, _tapered2, _tapered2, _tapered2, _tapered2, },
    {_tapered2, _tapered2, _tapered2, _tapered2, _kickknee, _tapered2, _kickup, _tapered2, },
    {_tapered2, _tapered2, _tapered2, _tapered2, _kickknee, _tapered2, _kickup, _tapered2, },
    {_tapered2, _tapered2,},
    {_tapered2, _tapered2,},
    {_tapered2, _tapered2,},
    {_tapered2, _tapered2,},
    {_kickknee, _tapered2, _kickup, _tapered2, _tapered2, _tapered2, _tapered2, _tapered2, },
    twist={.5,0,.5,0,-.5,0,-.5,0,},
}

local dance_knee_shuffle = 
{
    {_knee_out,_knee_in,_knee_out,_knee_in,},
    {_knee_out,_knee_in,_knee_out,_knee_in,},
    {_knee_in,_knee_out,_knee_in,_knee_in,},
    {_knee_in,_knee_out,_knee_in,_knee_in,},
    {_knee_in,_knee_in,_knee_in,_knee_in,},
    {_knee_in,_knee_in,_knee_in,_knee_in,},
    {_knee_in,_knee_in,_knee_in,_knee_out,},
    {_knee_in,_knee_in,_knee_in,_knee_out,},
}

local dance_knee_shuffle2 =
{
    {_knee_out,_knee_in,_knee_in,_knee_in,},
    {_knee_in,_knee_out,_knee_in,_knee_in,},
    {_knee_in,_knee_in,_knee_out,_knee_in,},
    {_knee_in,_knee_in,_knee_in,_knee_out,},
    {_knee_out,_knee_in,_knee_in,_knee_in,},
    {_knee_in,_knee_out,_knee_in,_knee_in,},
    {_knee_in,_knee_in,_knee_out,_knee_in,},
    {_knee_in,_knee_in,_knee_in,_knee_out,},
}

local dance_wave_arms =
{
    {_wave_arms_1,_wave_arms_2},
    {_wave_arms_2,_wave_arms_1},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
}

local dance_high_clap =
{
    {_highclap_out_arms,_highclap_in_arms,_highclap_out_arms,_highclap_in_arms,},
    {_highclap_out_arms,_highclap_in_arms,_highclap_out_arms,_highclap_in_arms,},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
}

local dance_thriller =
{
    {_thriller_arms,_thriller_legs,_thriller_arms,_thriller_legs,_thriller_arms,_thriller_legs,_thriller_arms,_thriller_legs, xth=8},
    {_thriller_arms,_thriller_legs,_thriller_arms,_thriller_legs,_thriller_arms,_thriller_legs,_thriller_arms,_thriller_legs, xth=8},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
    {_thriller_legs, _conga_barb1,_thriller_legs, _conga_barb1,},
    twist={0.5,-0.5,0.5,0.5,-0.5,0.5,-0.5,-0.5,},
}

local dance_claps =
{
    -- Two "hand" arrays come first - first one[0] is flipped chirally to [1]
    -- TODO: find out why the arrays seem out of phase for higher xths
    --{pts_hand_clap2, pts_hand_clap2, pts_hand_clap1, pts_hand_clap1, xth=8},
    --{pts_hand_clap2, pts_hand_clap2, pts_hand_clap1, pts_hand_clap1, xth=8},
    {pts_hand_clap1, pts_hand_clap1, pts_hand_clap2, pts_hand_clap2, xth=16},
    {pts_hand_clap1, pts_hand_clap1, pts_hand_clap2, pts_hand_clap2, xth=16},

    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb,},
    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb,},
    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb,},
    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb,},
    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb,},
    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb,},
}

local dance_curlback =
{
    {pts_elbowup, pts_rest, pts_rest, pts_rest,},
    {pts_rest, pts_elbowup, pts_rest, pts_rest,},
    {pts_rest, pts_rest, pts_curlback, pts_rest,},
    {pts_rest, pts_rest, pts_rest, pts_curlback,},
    {pts_rest, pts_shimmyl, pts_rest, pts_shimmyr,},
    {pts_rest, pts_shimmyl, pts_rest, pts_shimmyr,},
    {pts_rest, pts_shimmyl, pts_rest, pts_curlback,},
    {pts_rest, pts_shimmyl, pts_curlback, pts_shimmyr,},
}

local dance_toes_slide = 
{
    {_toein, _toeout, _toein, _toeout,},
    {_toein, _toeout, _toein, _toeout,},
    {_toeout, _toein, _toeout, _toein,},
    {_toeout, _toein, _toeout, _toein,},
    {_toein, _toeout, _toein, _toeout,},
    {_toein, _toeout, _toein, _toeout,},
    {_toeout, _toein, _toeout, _toein,},
    {_toeout, _toein, _toeout, _toein,},
}

local dance_hips_sway =
{
    {_waveleft, _waveright, _waveleft, _waveright,},
    {_waveright, _waveleft, _waveright, _waveleft,},
    {_hipsin, _hipsout, _hipsin, _hipsout,},
    {_hipsin, _hipsout, _hipsin, _hipsout,},
    {_hipsin, _hipsout, _hipsin, _hipsout,},

    {_hipsout, _hipsin, _hipsout, _hipsin,},
    {_hipsout, _hipsin, _hipsout, _hipsin,},
    {_hipsout, _hipsin, _hipsout, _hipsin,},
}

local dance_shoulders_and_clap =
{
    {
    pts_hand_clap2, _shdown, pts_hand_clap2, _point,
    pts_hand_clap2, pts_hand_clap1, pts_hand_clap2, _point,
    },
    {
    pts_hand_clap2, _shdown, pts_hand_clap2, _point,
    pts_hand_clap2, pts_hand_clap1, pts_hand_clap2, _point,
    },
    {_shup, _shdown, _shup, _shdown,},
    {_shup, _shdown, _shup, _shdown,},

    {_shup, _shdown, _shup, _shdown,},
    {_shup, _shdown, _shup, _shdown,},
    {_shup, _shdown, _shup, _shdown,},
    {_shup, _shdown, _shup, _shdown,},
}

local dance_claw =
{
    {_clawclosed, _clawopen, _clawclosed, _clawopen, _clawclosed, _clawopen, _clawclosed, _clawwild, xth=8},
    {_clawclosed, _clawopen, _clawclosed, _clawopen, _clawclosed, _clawopen, _clawclosed, _clawwild, xth=8},
    {_clawclosed, _clawopen, _clawclosed, _clawopen, _clawclosed, _clawopen, _clawclosed, _clawwild, xth=8},
    {_clawclosed, _clawopen, _clawclosed, _clawopen, _clawclosed, _clawopen, _clawclosed, _clawwild, xth=8},
    {_clawclosed, _clawopen, _clawclosed, _clawopen, _clawclosed, _clawopen, _clawclosed, _clawwild, xth=8},
    {_clawclosed, _clawopen, _clawclosed, _clawopen, _clawclosed, _clawopen, _clawclosed, _clawwild, xth=8},
    {_clawclosed, _clawopen, _clawclosed, _clawopen, _clawclosed, _clawopen, _clawclosed, _clawwild, xth=8},
    {_clawclosed, _clawopen, _clawclosed, _clawopen, _clawclosed, _clawopen, _clawclosed, _clawwild, xth=8},
}

local dance_knee_ring_punch = {
    {_conga_barb1,_punch_chamber,_punch_out,_thigh_out, xth=8},
    {_conga_barb1,_punch_chamber,_punch_out,_thigh_out, xth=8},

    {_conga_barb1,_conga_barb1,_thigh_out,_conga_barb1, xth=8},
    {_conga_barb1,_thigh_out,_conga_barb1,_conga_barb1, xth=8},
    {_thigh_out,_conga_barb1,_conga_barb1,_conga_barb1, xth=8},
    {_thigh_out,_conga_barb1,_conga_barb1,_conga_barb1, xth=8},
    {_conga_barb1,_thigh_out,_conga_barb1,_conga_barb1, xth=8},
    {_conga_barb1,_conga_barb1,_thigh_out,_conga_barb1, xth=8},
}

local clap_with_tilt =
{
    {pts_hand_clap2, pts_hand_clap2, pts_hand_clap1, pts_hand_clap1, xth=8}, -- not sure why this is on 2&4...
    {pts_hand_clap2, pts_hand_clap2, pts_hand_clap1, pts_hand_clap1, xth=8}, -- not sure why this is on 2&4...
    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb, xth=4},
    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb, xth=4},
    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb, xth=4},
    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb, xth=4},
    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb, xth=4},
    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb, xth=4},
    tilt={.5,0,-.25,0},
}

local clap_with_twist =
{
    {pts_hand_clap2, pts_hand_clap2, pts_hand_clap1, pts_hand_clap1, xth=8}, -- not sure why this is on 2&4...
    {pts_hand_clap2, pts_hand_clap2, pts_hand_clap1, pts_hand_clap1, xth=8}, -- not sure why this is on 2&4...
    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb, xth=4},
    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb, xth=4},
    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb, xth=4},
    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb, xth=4},
    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb, xth=4},
    {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb, xth=4},
    twist={1,0,-1,0,},
}

local dance_running = 
{
    {_run_up,_run_down,_run_down,_run_down,_run_down,_run_down,_run_down,_run_down, xth = 23},
    {_run_down,_run_up,_run_down,_run_down,_run_down,_run_down,_run_down,_run_down, xth = 23},
    {_run_down,_run_down,_run_up,_run_down,_run_down,_run_down,_run_down,_run_down, xth = 23},
    {_run_down,_run_down,_run_down,_run_up,_run_down,_run_down,_run_down,_run_down, xth = 23},
    {_run_down,_run_down,_run_down,_run_down,_run_up,_run_down,_run_down,_run_down, xth = 23},
    {_run_down,_run_down,_run_down,_run_down,_run_down,_run_up,_run_down,_run_down, xth = 23},
    {_run_down,_run_down,_run_down,_run_down,_run_down,_run_down,_run_up,_run_down, xth = 23},
    {_run_down,_run_down,_run_down,_run_down,_run_down,_run_down,_run_down,_run_up, xth = 23},
}

local dance_running2 = 
{
    {_run_up,_run_down, xth=17},
    {_run_down,_run_up, xth=17},
    {_run_down,_run_up, xth=17},
    {_run_down,_run_up, xth=17},
    {_run_down,_run_up, xth=17},
    {_run_up,_run_down, xth=17},
    {_run_up,_run_down, xth=17},
    {_run_up,_run_down, xth=17},
    {_run_up,_run_down, xth=17},
}

local chorus_line_per_tentacle_sequences = {
    dance_chorus_line,
    dance_knee_shuffle,
    dance_thriller,
    dance_knee_shuffle2,
    dance_wave_arms,
    dance_high_clap,
    dance_claps,
    dance_knee_ring_punch,
    dance_running,
    dance_running2,
}


local id = {} mm.make_identity_matrix(id)
local entities = {
    -- lead in [1] spot
    {
        lead=true,
        label="Lead",
        mtx=id,
        headAmpl=.1, headFreq=10, headSize=1,
        tentThick=1, tentLen=1,
        wave_ampl=0, wave_phase=0,
        num_cubes = 32*4,
        individual_splines={
            floatv(rest),
            floatv(rest),
            floatv(rest),
            floatv(rest),
            floatv(rest),
            floatv(rest),
            floatv(rest),
            floatv(rest),
            },
    }
}

local randoms = {}
local chorusline = {}
local congaline = {}
local bannerholders = {}

local function random_variance(center, variance)
    -- Return a random value centered on center, with += variance
    --variance = 0
    return center - variance + variance*2*math.random()
end

local function randomize_entity(e)
    e.headAmpl = random_variance(.1, .1)
    e.headFreq = random_variance(13, 7)
    e.tentThick = random_variance(.5+.75, .75)
    e.tentLen = random_variance(1, .5)
    e.headSize = random_variance(1.05, .25)
    e.wave_ampl = 0
    e.wave_phase = 0
    e.num_cubes = random_variance(64, 48)
end

local function use_individual_splines(e)
    -- By populating the individual_splines variable, the timestep
    -- function will use them as interpolation targets.
    -- Copies must be made of each array in the list.
    e.individual_splines = {
        floatv(rest), floatv(rest), floatv(rest), floatv(rest),
        floatv(rest), floatv(rest), floatv(rest), floatv(rest),
    }
end

local function add_ring_of_entities(entlist, count, radius)
    for i=0,count-1 do
        local e = {}
        randomize_entity(e)

        -- Line up in a ring
        local m = {}
        mm.make_identity_matrix(m)
        mm.glh_rotate(m, 3.1415927*2*i/count, 0,1,0)
        mm.glh_translate(m, 0,-e.tentLen*2,radius)
        mm.glh_rotate(m, -1.9, 0,1,0) -- fudged to face "hands" "forward"
        e.mtx = m
        --e.label = "Jelly"..i+2
        table.insert(entlist, e)
    end
end

-- Just scatter them around anywhere
local function add_assortment_of_entities(entlist, count, radius)
    for i=0,count-1 do
        local e = {}
        randomize_entity(e)
        --e.tilt = random_variance(0,.4)

        local m = {}
        mm.make_identity_matrix(m)
        mm.glh_rotate(m, random_variance(0, 2*math.pi), 0,1,0)

        -- Move into ring
        local target = {0,0,0}
        if i < 10 then
            local ring_count = 10
            local radius = 16
            target = {
                radius * math.sin(2 * math.pi * i/ring_count),
                0,
                radius * math.cos(2 * math.pi * i/ring_count),
            }
        elseif i < (10+18) then
            local ring_count = 18
            local radius = 24
            target = {
                radius * math.sin(2 * math.pi * (i-10)/ring_count),
                0,
                radius * math.cos(2 * math.pi * (i-10)/ring_count),
            }
        else
            local ring_count = 24
            local radius = 32
            target = {
                radius * math.sin(2 * math.pi * (i-10-18)/ring_count),
                0,
                radius * math.cos(2 * math.pi * (i-10-18)/ring_count),
            }
        end


        local startpos = {
            target[1] + random_variance(0,30),
            target[2] + random_variance(0,30),
            target[3] + random_variance(0,10)
        }

        local gap = 50
        if startpos[1] < 0 and startpos[1] > -gap then
            startpos[1] = startpos[1] - gap
        end
        if startpos[1] > 0 and startpos[1] < gap then
            startpos[1] = startpos[1] + gap
        end

        mm.glh_translate(m, startpos[1], startpos[2], startpos[3])
        e.mtx = m

        -- Move from random towards ring
        local loc = {m[13], m[14], m[15]}
        e.kf_move_start = {t=0, kf=loc}
        e.kf_move_end = {t=30, kf=target}

        e.one_spline_for_all_tentacles = floatv({-7,5,0, -5,2,0, 0,0,0, 0,5,0,})
        e.swiwfreq = random_variance(4, 3.1)

        table.insert(entlist, e)
    end
end

-- Line them up in a row
local function add_line_of_entities(entlist, count, radius)
    for i=0,count-1 do
        local e = {}
        randomize_entity(e)
        e.tilt = random_variance(0,.05)

        local m = {}
        mm.make_identity_matrix(m)
        mm.glh_rotate(m, 1.2, 0,1,0)
        mm.glh_translate(m, random_variance(0,20), random_variance(0,20), random_variance(0,20))
        e.mtx = m

        -- Move line forwards
        e.kf_move_start = {t=0, kf={0,0,9*i}}
        e.kf_move_end = {t=15, kf={0,0,9*i+40}}
        e.conga_x = i / count

        table.insert(entlist, e)
    end
end

-- A chorus line of jellyfish
local function add_row_of_entities(entlist, count)
    for i=0,count-1 do
        local e = {}
        randomize_entity(e)

        local m = {}
        mm.make_identity_matrix(m)
        mm.glh_translate(m, 7*i, 0, 0)
        mm.glh_rotate(m, 3.1415927 + 1.2, 0,1,0)
        e.mtx = m
        e.use_global_per_tentacle_sequence = true
        e.label = scene_names2[i+1]
        table.insert(entlist, e)
    end
end

local function add_banner_holders(entlist)
    local e = {} 
    e.headAmpl = .1
    e.headFreq = 12
    e.tentThick = 1
    e.tentLen = .6
    e.headSize = .8
    e.wave_ampl = 0
    e.wave_phase = 0
    e.num_cubes = 48

    local m = {}
    mm.make_identity_matrix(m)
    mm.glh_rotate(m, 3.1415927 + 1.2, 0,1,0)
    e.mtx = m
    e.use_global_per_tentacle_sequence = true
    e.use_banner_text = true

    table.insert(entlist, e)
end

local function populate_entities_array()
    math.randomSeed = 1 -- deterministic
    add_ring_of_entities(entities, 10,16)
    add_ring_of_entities(entities, 18,24)
    add_ring_of_entities(entities, 24,32)

    add_assortment_of_entities(randoms, 10+18+24)

    add_row_of_entities(chorusline, #scene_names2)
    add_line_of_entities(congaline, 16)

    add_banner_holders(bannerholders)


    --[[
        Custom dance motions
    ]]
    local curl = entities[2]
    curl.label = "Curlback"
    use_individual_splines(curl)
    curl.per_tentacle_sequence = dance_curlback

    local twi = entities[6]
    twi.label = "Twist"
    use_individual_splines(twi)
    twi.per_tentacle_sequence = clap_with_twist


    local tlt = entities[3]
    tlt.label = "Tilt"
    use_individual_splines(tlt)
    tlt.per_tentacle_sequence = clap_with_tilt


    entities[4].feet_kfs = {
        4,
        {
            pts_shoul, pts_barb,
            pts_feet_bounce1, pts_barb
        }
    }
    entities[4].one_spline_for_all_tentacles = pts_feet_bounce2
    entities[4].label = "Bounce"


    local toes = entities[5]
    toes.label = "Toes"
    use_individual_splines(toes)
    toes.per_tentacle_sequence = dance_toes_slide


    local sb = entities[7]
    sb.label = "Punch"
    use_individual_splines(sb)
    sb.per_tentacle_sequence = dance_knee_ring_punch



    local clap = entities[11]
    clap.label = "Disco"
    use_individual_splines(clap)
    clap.per_tentacle_sequence =
    {
    --[[
        -- Two "hand" arrays come first - first one[0] is flipped chirally to [1]
        -- TODO: find out why the arrays seem out of phase for higher xths
        --{pts_hand_clap2, pts_hand_clap2, pts_hand_clap1, pts_hand_clap1, xth=8},
        --{pts_hand_clap2, pts_hand_clap2, pts_hand_clap1, pts_hand_clap1, xth=8},
        {pts_hand_clap1, pts_hand_clap1, pts_hand_clap2, pts_hand_clap2, xth=16},
        {pts_hand_clap1, pts_hand_clap1, pts_hand_clap2, pts_hand_clap2, xth=16},

        {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb,},
        {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb,},
        {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb,},
        {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb,},
        {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb,},
        {pts_shimmyl, pts_barb, pts_shimmyr, pts_barb,},
        ]]

        -- Chorus line animation
        {_kickdown, pts_rest,},
        {_discoup, _discodown,},
        {_discoup, _discodown,},
        {_kickdown, pts_rest,},
        {_kickdown, pts_rest,},
        {_kickdown, pts_rest,},
        {_kickdown, pts_rest,},
        {_kickdown, pts_rest,},
    }

    local hips = entities[10]
    hips.label = "Hips"
    use_individual_splines(hips)
    hips.per_tentacle_sequence = dance_hips_sway

    local shoul = entities[9]
    shoul.label = "Shoulders"
    use_individual_splines(shoul)
    shoul.per_tentacle_sequence = dance_shoulders_and_clap

    local claw = entities[8]
    claw.label = "Dr. Claw"
    use_individual_splines(claw)
    claw.per_tentacle_sequence = dance_claw
    mm.glh_rotate(claw.mtx, 3.1415927*.5, 0,0,1)
end

function name_entities_in_list(es, strs)
    for i=1,#es do
        local e = es[i]
        if strs[i] then
            e.label = strs[i]
        end
    end
end

-- For storing a manually adjusted spline position in the source as an array.
function jellyfish_scene.print_spline_pts(pts)
    io.write("{")
    for i=0,11 do
        io.write(string.format("%4.1f",pts[i])..",")
    end
    print("}")
end

function jellyfish_scene.initGL()
    tentacle_scene.initGL()
    local prog = tentacle_scene.prog_spline
    head_scene.initGL()
    populate_entities_array()
    font_scene.initGL()

    local fogDist = 150
    tentacle_scene.fogDist = fogDist
    head_scene.fogDist = fogDist
    font_scene.fogDist = fogDist/2

    name_entities_in_list(entities, scene_names)
end

function jellyfish_scene.exitGL()
    tentacle_scene.exitGL()
    head_scene.exitGL()
    font_scene.exitGL()
end

local function draw_head(m, proj, e)
    if e.lead and not jellyfish_scene.draw_lead_guy then return end
    if e.tilt then mm.glh_rotate(m, e.tilt, 0,0,1) end
    if e.twist then mm.glh_rotate(m, e.twist, 0,1,0) end
    mm.glh_translate(m, 0,1,0)
    local s = 2 * e.headSize
    mm.glh_scale(m, s,-s,s)
    head_scene.render_for_one_eye(m, proj, e)
end

function draw_text_string(m, proj, str)
    font_scene.render_string(m, proj, str)
end

function draw_text_label(m, proj, e)
    if not jellyfish_scene.draw_text_labels then return end
    if e.lead and not jellyfish_scene.draw_lead_guy then return end

    local label = e.label
    if e.use_banner_text then label = banner_messages[jellyfish_scene.banner_msg_idx] end
    font_scene.render_string(m, proj, label)
end

local function draw_tentacles(m, proj, e)
    if e.lead and not jellyfish_scene.draw_lead_guy then return end
    local prog = tentacle_scene.prog_spline
    local umv_loc = gl.GetUniformLocation(prog, "mvmtx")
    local upr_loc = gl.GetUniformLocation(prog, "prmtx")
    gl.UseProgram(prog)
    gl.BindVertexArray(tentacle_scene.vao)

    -- Draw all 8 tentacles of the jellyfish
    local ts = 8
    for i=0,ts-1 do
        local ml = {}
        for j=1,16 do ml[j] = m[j] end

        --
        -- Determine which spline array we use for this tentacle:
        -- A global manual control array or individually interpolated per-entity arrays
        --
        local s = jellyfish_scene.global_legs

        if i < 2 then s = jellyfish_scene.global_arms end -- Draw two "hands" with separate splines

        local sall = e.one_spline_for_all_tentacles
        if sall then s = sall end

        -- Hand off control to individual splines, if available.
        local ss = e.individual_splines

        local tilt = e.tilt
        local twist = e.twist

        local gpts = e.use_global_per_tentacle_sequence
        local gis = jellyfish_scene.global_individual_splines
        if gpts and gis then
            ss = jellyfish_scene.global_individual_splines
            tilt = gis.tilt
            twist = gis.twist
        end

        if ss and ss[i+1] then s = ss[i+1] end -- Individual tentacle control

        -- Override all for manual control
        if not jellyfish_scene.animate then
            s = jellyfish_scene.global_manual
            if i < 2 then s = jellyfish_scene.global_manual_arms end
        end

        if tilt then mm.glh_rotate(ml, tilt, 0,0,1) end
        if twist then mm.glh_rotate(ml, twist, 0,1,0) end
        mm.glh_scale(ml, 1,-1,1)
        if i == 0 then mm.glh_scale(ml, 1,1,-1) end -- Draw first "hand" spline reflected for chirality
        local twopi = 3.1415927*2
        mm.glh_rotate(ml, twopi*i/ts, 0,1,0)
        mm.glh_translate(ml, 1,-2,0)
        gl.UniformMatrix4fv(umv_loc, 1, GL.FALSE, glFloatv(16, ml))
        gl.UniformMatrix4fv(upr_loc, 1, GL.FALSE, glFloatv(16, proj))

        tentacle_scene.draw_color_cube_row(s, e)
    end
end

local function draw_entity(m, proj, e)
    local ml = {} for j=1,16 do ml[j] = m[j] end
    mm.post_multiply(ml, e.mtx)

    local mh = {} for j=1,16 do mh[j] = ml[j] end
    draw_head(mh, proj, e)
    draw_text_label(mh, proj, e)

    draw_tentacles(ml, proj, e)
end

local function draw_lead_jellyfish(m, proj, animParams)
    if not entities then return end
    draw_entity(m, proj, entities[1])
end

local function draw_all_jellyfish_in_list(m, proj, jellies)
    for _,e in pairs(jellies) do
        local ml = {} for j=1,16 do ml[j] = m[j] end
        mm.post_multiply(ml, e.mtx)
        draw_tentacles(ml, proj, e)
    end

    for _,e in pairs(jellies) do
        local mh = {} for j=1,16 do mh[j] = m[j] end
        mm.post_multiply(mh, e.mtx)
        draw_head(mh, proj, e)
    end

    for _,e in pairs(jellies) do
        local mf = {} for j=1,16 do mf[j] = m[j] end
        mm.post_multiply(mf, e.mtx)
        draw_text_label(mf, proj, e)
    end
end

-- animParams is a table of options passed in by main
function jellyfish_scene.render_for_one_eye(m, proj, animParams)
    local arrays = {
        entities,
        randoms,
        chorusline,
        congaline,
    }
    local d = animParams.drawlist
    local e = arrays[d]
    if not e then e = entities end
    draw_all_jellyfish_in_list(m, proj, e)

    if jellyfish_scene.draw_banner_holders then
        draw_all_jellyfish_in_list(m, proj, bannerholders)
    end

    gl.BindVertexArray(0)
    gl.UseProgram(0)
end


--[[
    Interpolation and beat-related functions
]]
local function get_beat_vals(time, seqlen, xth)
    -- Get beat sequences of x/y time
    -- xth==4 means quarter note(1 beat)
    -- Make sure jellyfish_scene.BPM has been set externally!  
    local bps = jellyfish_scene.BPM / 60
    local bb = xth / 4
    local beat = time * bps * bb -- First beat starts at time 0
    local beati, beatf = math.modf(beat,bb)
    return beati % seqlen, (beati+1) % seqlen, beatf
end

-- interp functions
local function smoothstep(x)
    return x*x*(3 - 2*x)
end

local function mix(a,b,t)
    return (1-t)*a + t*b
end

local function bouncestep(x)
    return mix(math.sqrt(x), 1-math.sqrt(1-x), x)
end

local function popstep(x)
    return mix((x), 1-math.sqrt(1-x), x)
end

local function powstep(x)
    return math.pow(x,5)
end

local function interpolate_spline(pts, a, b, t, curve)
    if curve then t=curve(t) end
    -- Interpolate all values in a spline by the given keyframes and point in the beat
    for i=0,11 do pts[i] = (1-t)*a[i] + t*b[i] end
end

local function interpolate_value(a, b, t, curve)
    if curve then t=curve(t) end
    return (1-t)*a + t*b
end

local function do_spline_sequence(time, target, kf, xth, curve)
    if not kf then return end
    local bt, bt_, t = get_beat_vals(time, #kf, xth)
    local a, b = kf[bt+1], kf[bt_+1]
    interpolate_spline(target, a, b, t, curve)
end

function timestep_entity(e, absTime)
    -- Individual tentacle control
    local ss = e.individual_splines
    local p_t_s = e.per_tentacle_sequence
    local swf = e.swiwfreq
    if p_t_s and ss then
        local time = absTime
        -- Per-tentacle sequence timestep
        for j=1,#ss do
            local s = ss[j]
            local kf = p_t_s[j]
            local xth = 4
            -- This optional beat subdivision quantity we can specify here by just
            -- tossing the "xth" entry into the table because lua's # operator only
            -- considers the "array portion" of the table. Convenient!
            if kf.xth then xth = kf.xth end
            do_spline_sequence(time, s, kf, xth, powstep)
        end

    elseif ss then
        for j=1,#ss do
            local ph = j/8
            local beatp = absTime + ph
            local tt = math.pow(math.abs(math.sin(.25*beatp * 2*math.pi)),.75)
            local s = ss[j]
            if s then
                for i=0,11 do
                    s[i] = (1-tt)*pts_feet_bounce2[i] + tt*pts_feet_bounce1[i]
                end
            end
        end
    elseif swf then
        -- Some random-looking swimming pulsing motions
        local kfs = {_swimout, _swimin,}
        local function impulse( k, x )
            local h = k*x
            return h*math.exp(1-h)
        end
        local a,b = kfs[1], kfs[2]
        local t = impulse(3, absTime%swf)
        local pts = e.one_spline_for_all_tentacles
        for i=0,11 do pts[i] = (1-t)*a[i] + t*b[i] end

    elseif e.feet_kfs and e.one_spline_for_all_tentacles then
        local xth = e.feet_kfs[1]
        local kf = e.feet_kfs[2]
        local time = absTime
        local bt, bt_, t = get_beat_vals(time, #kf, xth)
        local a, b = kf[bt+1], kf[bt_+1]
        do_spline_sequence(absTime, e.one_spline_for_all_tentacles, kf, 4, popstep)
    end

    -- Twist control
    if p_t_s and p_t_s.twist then
        local kf = p_t_s.twist
        local xth = 4
        local time = absTime
        local bt, bt_, t = get_beat_vals(time, #kf, xth)
        local a, b = kf[bt+1], kf[bt_+1]
        e.twist = interpolate_value(a, b, t, powstep)
    end

    -- Tilt control
    if p_t_s and p_t_s.tilt then
        local kf = p_t_s.tilt
        local xth = 4
        local time = absTime
        local bt, bt_, t = get_beat_vals(time, #kf, xth)
        local a, b = kf[bt+1], kf[bt_+1]
        e.tilt = interpolate_value(a, b, t, powstep)
    end

    -- Tentacle wave action
    if e.lead then
        e.wave_ampl = jellyfish_scene.wave_ampl
        e.wave_phase = absTime
    else
        e.wave_ampl = 0
        e.wave_phase = 0
    end
end

function jellyfish_scene.timestep(absTime, dt)
    head_scene.timestep(absTime, dt)

    if not jellyfish_scene.animate then return end

    -- KF dance sequence
    -- Interpolate global array for "legs" and hands
    local idx = jellyfish_scene.standard_kfs_idx
    local kfl = jellyfish_scene.standard_kfs[idx].legs
    local kfh = jellyfish_scene.standard_kfs[idx].hands
    do_spline_sequence(absTime, jellyfish_scene.global_legs, kfl, kfl.xth, popstep)
    do_spline_sequence(absTime, jellyfish_scene.global_arms, kfh, kfh.xth, powstep)

    -- Do global per-tentacle list
    local ss = jellyfish_scene.global_individual_splines
    local p_t_s = chorus_line_per_tentacle_sequences[jellyfish_scene.chorusline_kfs_idx]
    if p_t_s and ss then
        local time = absTime
        -- Per-tentacle sequence timestep
        for j=1,#ss do
            local s = ss[j]
            local kf = p_t_s[j]
            local xth = 4
            -- This optional beat subdivision quantity we can specify here by just
            -- tossing the "xth" entry into the table because lua's # operator only
            -- considers the "array portion" of the table. Convenient!
            if kf.xth then xth = kf.xth end
            do_spline_sequence(time, s, kf, xth, powstep)
        end

        -- Global standard kfs twist/tilt control
        if p_t_s and p_t_s.twist then
            local kf = p_t_s.twist
            local xth = 4
            local time = absTime
            local bt, bt_, t = get_beat_vals(time, #kf, xth)
            local a, b = kf[bt+1], kf[bt_+1]
            ss.twist = interpolate_value(a, b, t, powstep)
        end
        if p_t_s and p_t_s.tilt then
            local kf = p_t_s.tilt
            local xth = 4
            local time = absTime
            local bt, bt_, t = get_beat_vals(time, #kf, xth)
            local a, b = kf[bt+1], kf[bt_+1]
            ss.tilt = interpolate_value(a, b, t, powstep)
        end
    end

    -- Interpolate entities individually
    for k=1,#entities do
        local e = entities[k]
        timestep_entity(e, absTime)
    end

    for k=1,#randoms do
        local e = randoms[k]
        timestep_entity(e, absTime)
        local k0 = e.kf_move_start --= {0, loc} -- time, location pair
        local k1 = e.kf_move_end --= {10, {0,0,0}}
        local t = jellyfish_scene.random_t
        for i=1,3 do
            e.mtx[12+i] = interpolate_value(k0.kf[i], k1.kf[i], t)
        end

        -- This override flag instructs display to not use the randomized individual keyframes
        -- and use the global ones instead - i.e., everybody synchronize.
        if not jellyfish_scene.do_random_individual_kfs then
            local pts = e.one_spline_for_all_tentacles
            do_spline_sequence(absTime, pts, kfl, kfl.xth, popstep)
        end
    end
    
    for k=1,#chorusline do
        local e = chorusline[k]
        timestep_entity(e, absTime)
    end

    -- Separate loop for conga line
    for k=1,#congaline do
        local e = congaline[k]
        local k0 = e.kf_move_start --= {0, loc} -- time, location pair
        local k1 = e.kf_move_end --= {10, {0,0,0}}\
        local t = jellyfish_scene.conga_t
        local function wavy(x)
            return math.sin(4*x)/(2*x)
        end
        local x = t + 12 * e.conga_x
        e.mtx[15] = 10 * x - 20
        e.mtx[14] = 0
        e.mtx[13] = 15 * wavy(.3*x)
    end

    -- Banner holder placed manually by keyframes
    for k=1,#bannerholders do
        local e = bannerholders[k]
        e.mtx[13] = jellyfish_scene.banner1[1]
        e.mtx[14] = jellyfish_scene.banner1[2]
        e.mtx[15] = jellyfish_scene.banner1[3]
    end
end

return jellyfish_scene
