diff src/main.c @ 0:785057719d9b

Import.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 05 Aug 2013 12:25:43 +0300
parents
children acfb339ab87d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main.c	Mon Aug 05 12:25:43 2013 +0300
@@ -0,0 +1,3582 @@
+// MIDISYS-ENGINE for windows and osx
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <fstream>
+#include <map>
+#include <wchar.h>
+#include <time.h>
+
+#include "freetype-gl.h"
+#include "vertex-buffer.h"
+#include "markup.h"
+#include "shader.h"
+#include "mat4.h"
+
+#if defined(_WIN32) || defined(_WIN64)
+#  define wcpncpy wcsncpy
+#  define wcpcpy  wcscpy
+#endif
+
+#ifndef  __APPLE__
+#include <malloc.h>
+#endif
+
+#define XXX 1
+#include "bass.h"
+#include "midifile.h"
+#include "midiutil.h"
+
+int quitflag = 0;
+
+// GLUT window handle (1 for windowed display, 0 for fullscreen gamemode)
+GLuint window = 0;
+// demo running speed multiplier
+float demo_speed_x = 1.0f;
+
+// remove for non-debug build
+int debugmode = 0;
+// jump to demo position; 0 for whole demo
+int jump_to = 0;
+// some debugging flags
+bool load_textures = true;
+
+// midi sync
+
+MIDI_MSG timeline[64][100000] = {NULL};
+char timeline_trackname[64][512] = {-1};
+int timeline_trackindex[64] = { 0 };
+int timeline_tracklength[64] = { -1 };
+int timeline_trackcount = 0;
+
+// midi track number of mapping data
+int mapping_tracknum[1000] = {-1};
+// midi to shader param map from mapping.txt
+int mapping_paramnum[1000] = {-1};
+// track to map from
+char mapping_trackname[1000][512] = {-1};
+// map type: 0 == trig (noteon / off), 1 == param (modwheel / cc value)
+int mapping_type[1000] = {-1};
+// number of active mappings from midi to shader param
+int mapping_count = 0;
+
+// current shader param values 
+int scene_shader_params[16] = {-1};
+int scene_shader_param_type[16] = {-1};
+
+float millis = 0;
+
+// scene globals
+// vhs
+float vhsbeat = 0.0;
+float vhsbeat_start = 0;
+
+#ifdef __APPLE__
+    #include "glew.h"
+    #include <OpenGL/OpenGL.h>
+    #include <OpenGL/glu.h>
+    #include "freeglut.h"
+#else
+    #include <GL/glew.h>
+    #include <GL/glu.h>
+    #include <GL/freeglut.h>
+#endif
+
+#include <assimp/Importer.hpp>
+//#include <assimp/PostProcess.h>
+#include <assimp/postprocess.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/LogStream.hpp>
+
+#include "stb_image.c"
+
+
+#include <oggplayer.h>
+#include <algorithm>
+
+#include "ggets.h"
+#include "ggets.c"
+
+// fbo
+
+GLuint fb;
+GLuint fb_tex;
+GLuint fb2;
+GLuint fb_tex2;
+
+GLuint fake_framebuffer;
+GLuint fake_framebuffer_tex;
+
+
+#define KEYEVENTS_COUNT 507
+unsigned char keyrec[507] = {98,105,108,111,116,114,105,112,32,111,112,101,114,97,116,105,110,103,32,115,121,115,116,101,109,32,52,46,50,48,13,97,108,108,32,108,101,102,116,115,32,97,110,100,32,114,105,103,104,116,115,32,114,101,118,101,114,115,101,100,13,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,13,99,97,116,32,110,111,116,101,46,116,120,116,13,49,54,58,51,48,32,98,101,32,97,116,32,116,104,101,32,97,103,114,101,101,100,32,112,108,97,99,101,13,49,56,58,51,48,32,115,119,97,108,108,111,119,32,99,97,112,115,117,108,101,115,13,97,102,116,101,114,32,101,102,102,101,99,116,58,32,112,114,111,101,99,8,8,116,101,120,8,99,116,32,109,101,116,97,108,115,13,119,97,105,116,32,102,111,114,32,97,109,97,115,8,8,8,8,109,97,115,107,32,115,105,103,110,97,108,13,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,13,99,97,116,32,98,105,108,111,116,114,105,112,95,99,114,101,100,105,116,115,46,116,120,116,13,97,101,103,105,115,13,100,101,112,13,101,101,118,8,8,118,101,97,103,101,110,8,8,8,110,103,101,108,13,104,97,100,100,97,115,13,111,97,115,105,122,13,112,97,104,97,109,111,107,97,13,115,112,105,105,107,107,105,13,118,105,115,121,13,122,111,118,13,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,69,68,69,83,8,65,57,65,105,39,109,13,103,101,116,116,105,110,103,32,115,111,109,101,116,104,105,110,103,32,110,111,119,13,105,8,46,46,46,105,39,109,32,102,101,101,108,105,110,103,32,105,116,32,116,111,111,46,13,105,116,39,115,46,46,46,46,32,105,116,39,115,46,46,46,46,46,46,46,46,46,13,105,116,39,115,32,107,105,99,107,107,107,105,105,105,105,105,105,105,105,105,105,110,110,110,110,110,110,110,110,110,110,103,103,103,103,103,103,103,103,105,105,105,105,105,105,105,105,105,110,110,110,110,110,110,110,110,110,13};
+int keymillis[507] = {0,84,133,278,414,574,624,667,780,1045,1104,1185,1249,1329,1456,1537,1701,1753,1801,1891,2009,2099,2205,2672,2762,2905,3013,3103,3204,3317,3593,3837,3945,4079,4202,4273,4359,4429,4574,4610,4648,4729,4858,4905,4986,5080,5180,5253,5366,5440,5689,5820,6004,6079,6238,6299,6416,6495,6624,6796,7149,7291,7814,7845,7878,7910,7942,7975,8007,8040,8071,8104,8137,8169,8204,8237,8269,8301,8334,8367,8398,8431,8465,8496,8528,8561,8591,8623,8656,8688,8719,9198,9403,9460,9667,9777,9938,10014,10051,10089,10279,10618,10774,10873,11230,15891,16480,16917,17262,17987,18708,21302,21803,21920,22036,22135,22216,22353,22491,22549,22706,22821,22939,23096,23150,23303,23400,23454,23891,23930,23956,24016,24077,24387,28893,29377,29730,29985,30654,30829,33772,34315,34383,34521,34655,34791,34877,34972,35140,35179,35220,35322,35413,35498,35551,35627,36409,43630,44014,44155,44228,44372,44460,44596,44661,44794,44840,44950,45009,45217,45280,45398,45456,45610,45713,45846,46112,46237,46286,46340,46702,46935,47033,47087,47160,47284,47324,47393,47463,47519,47636,47790,49371,49417,49446,49539,49612,49721,49769,49867,49926,49998,50091,50164,50245,50420,50544,50669,50780,50856,50899,50921,51003,51075,51352,51420,51568,51663,51717,51844,52608,52905,53437,53469,53501,53533,53567,53598,53631,53663,53694,53728,53759,53792,53823,53855,53889,53920,53954,53987,54019,54051,54082,54115,54146,54179,54211,54244,54544,54700,54893,55387,56136,56247,56430,56502,56766,56831,56896,57004,57070,57206,57249,57271,57667,57831,57975,58045,58209,58315,58384,58444,58630,58736,58822,58927,59433,59758,59833,59952,60038,60133,60697,60984,61047,61106,61560,61885,62091,62106,62626,62757,62786,62832,63009,63115,63211,63228,63557,63679,63805,63980,64059,64099,64215,64663,65414,65494,65620,65795,65820,65902,66303,66604,66666,66725,66845,66929,67443,67820,67886,68047,68105,68236,68290,68416,68496,68865,69309,69422,69535,69677,69846,69965,70153,70608,71440,71520,71620,71718,71943,72258,72359,72941,73284,73559,74087,74120,74151,74183,74214,74249,74281,74315,74347,74378,74411,74442,74578,74703,74820,75021,75184,75338,75491,75983,76650,76976,77252,77294,77624,78108,78162,78932,79324,79777,80101,80598,80997,81157,81230,81367,81487,81976,82114,82182,82253,82550,82641,82690,82765,83021,83131,83184,83374,83422,83518,83829,83895,84017,84901,85534,85972,86047,86189,86325,86642,87409,87637,87710,87790,87866,87986,88086,88134,88307,88350,88405,88894,89235,89320,89406,89571,89715,90247,91658,91929,92036,92609,92713,93112,93263,93393,93539,94091,94249,94433,94711,94806,95220,95405,95541,95707,95992,96150,96306,96442,96600,97012,98638,98805,99329,99462,100393,100771,101191,102156,102308,102553,102984,103505,103675,103864,104079,104212,104361,104545,104890,105043,105235,105456,105682,105811,105979,106157,106330,106509,106670,106838,106997,107165,107573,107753,107952,108102,108259,108415,108591,108696,108847,108987,109123,109261,109418,109553,109665,109864,109969,110112,110232,110372,110506,110648,110781,110936,111062,111063};
+
+
+#define KEYEVENTS_COUNT2 212
+unsigned char keyrec2[212] = {85,115,32,104,105,103,104,32,112,114,101,99,105,115,105,111,110,32,116,111,111,115,108,44,32,101,8,8,8,8,8,108,115,32,44,8,8,44,32,101,110,103,105,110,101,101,114,101,100,32,116,111,32,101,120,101,99,117,116,101,13,116,104,101,32,115,104,111,114,116,45,115,105,103,8,103,104,116,101,100,32,105,100,101,111,108,111,103,105,99,97,108,32,102,108,97,118,111,117,114,32,111,102,32,116,111,100,97,121,13,65,115,32,108,111,110,103,32,97,115,32,116,104,101,114,101,32,105,115,32,97,32,99,111,109,109,111,110,32,101,118,8,110,121,8,101,109,121,32,116,111,32,117,110,105,116,101,32,117,115,13,119,101,39,108,108,32,107,101,101,108,8,112,32,109,97,114,99,104,105,110,103,44,32,108,105,107,101,32,97,32,119,101,108,108,45,111,105,108,101,100,32,109,97,99,104,105,110,101,46,46,46};
+int keymillis2[212] = {0,157,253,413,518,573,654,794,940,1045,1110,1315,1406,1502,1599,1666,2283,2362,2972,3058,3189,3358,3459,3677,3747,3859,4049,4184,4320,4475,4589,4628,4677,4762,4907,5201,5327,5375,5456,5556,5802,5911,6008,6244,6380,6536,6855,6920,7061,7142,7254,7338,7535,8560,8680,8812,9067,9201,9284,9357,10544,11841,11961,12027,12091,12216,12402,12514,12634,12791,12933,13357,13472,13589,13857,14062,14490,14688,14751,15278,15395,15707,15812,15893,15953,16162,16316,16359,16463,16577,16632,16762,16899,17085,17180,17643,17815,17911,17972,18791,18888,19011,19219,19303,19516,19628,19771,19876,19895,20575,21102,21344,21518,21926,22072,22158,22208,22559,22659,22756,22866,23123,23290,23374,23457,23539,23605,23691,23767,23859,23955,24060,24357,24433,24502,24651,24700,24839,24981,25542,25672,25903,26080,26194,26632,26738,26835,26920,27000,27101,27164,27233,27448,27785,27899,27969,28050,28118,28234,28355,28892,29245,29372,29555,29730,29871,29973,30140,30219,30360,30476,30773,30817,30886,31118,31193,31264,31454,31494,31542,31893,31955,32193,32267,32405,32485,32638,32661,32745,32834,32927,33070,33166,33273,33401,33648,33995,34047,34253,34327,34421,34500,34629,34725,34861,34936,34999,35231,35315,35415,35515,35615};
+
+
+
+const int __SIGNAL_ACTIVATE__     = 0;
+const int __SIGNAL_COMPLETE__     = 1;
+const int __SIGNAL_HISTORY_NEXT__ = 2;
+const int __SIGNAL_HISTORY_PREV__ = 3;
+#define MAX_LINE_LENGTH  511
+
+
+const int MARKUP_NORMAL      = 0;
+const int MARKUP_DEFAULT     = 0;
+const int MARKUP_ERROR       = 1;
+const int MARKUP_WARNING     = 2;
+const int MARKUP_OUTPUT      = 3;
+const int MARKUP_BOLD        = 4;
+const int MARKUP_ITALIC      = 5;
+const int MARKUP_BOLD_ITALIC = 6;
+const int MARKUP_FAINT       = 7;
+#define   MARKUP_COUNT         8
+
+
+// ------------------------------------------------------- typedef & struct ---
+typedef struct {
+    float x, y, z;
+    float s, t;
+    float r, g, b, a;
+} vertex_t;
+
+struct _console_t {
+    vector_t *     lines;
+    wchar_t *      prompt;
+    wchar_t        killring[MAX_LINE_LENGTH+1];
+    wchar_t        input[MAX_LINE_LENGTH+1];
+    size_t         cursor;
+    markup_t       markup[MARKUP_COUNT];
+    vertex_buffer_t * buffer;
+    vec2           pen; 
+    void (*handlers[4])( struct _console_t *, wchar_t * );
+};
+typedef struct _console_t console_t;
+
+// ------------------------------------------------------- global variables ---
+static console_t * console;
+texture_atlas_t *atlas;
+GLuint shader;
+mat4   model, view, projection;
+
+
+// ------------------------------------------------------------ console_new ---
+console_t *
+console_new( void )
+{
+    console_t *self = (console_t *) malloc( sizeof(console_t) );
+    if( !self )
+    {
+        return self;
+    }
+    self->lines = vector_new( sizeof(wchar_t *) );
+    self->prompt = (wchar_t *) wcsdup( L"" );
+    self->cursor = 0;
+    self->buffer = vertex_buffer_new( "vertex:3f,tex_coord:2f,color:4f" );
+    self->input[0] = L'\0';
+    self->killring[0] = L'\0';
+    self->handlers[__SIGNAL_ACTIVATE__]     = 0;
+    self->handlers[__SIGNAL_COMPLETE__]     = 0;
+    self->handlers[__SIGNAL_HISTORY_NEXT__] = 0;
+    self->handlers[__SIGNAL_HISTORY_PREV__] = 0;
+    self->pen.x = self->pen.y = 0;
+
+    atlas = texture_atlas_new( 512, 512, 1 );
+
+    vec4 white = {{0.2,1,0.2,0.7}};
+    vec4 black = {{0,0,0,1}};
+    vec4 none = {{0,0,1,0}};
+
+    markup_t normal;
+    normal.family  = "data/fonts/VeraMono.ttf";
+    normal.size    = 23.0;
+    normal.bold    = 0;
+    normal.italic  = 0;
+    normal.rise    = 0.0;
+    normal.spacing = 0.0;
+    normal.gamma   = 1.0;
+    normal.foreground_color    = white;
+    normal.foreground_color.r = 0.45;
+    normal.foreground_color.g = 0.65;
+    normal.foreground_color.b = 0.45;
+
+    normal.font = texture_font_new( atlas, "data/fonts/term.ttf", 33 );
+
+    markup_t bold = normal;
+    bold.bold = 1;
+    bold.font = texture_font_new( atlas, "data/fonts/VeraMoBd.ttf", 23 );
+
+    markup_t italic = normal;
+    italic.italic = 1;
+    bold.font = texture_font_new( atlas, "data/fonts/VeraMoIt.ttf", 23 );
+
+    markup_t bold_italic = normal;
+    bold.bold = 1;
+    italic.italic = 1;
+    italic.font = texture_font_new( atlas, "data/fonts/VeraMoBI.ttf", 13 );
+
+    markup_t faint = normal;
+    faint.foreground_color.r = 0.25;
+    faint.foreground_color.g = 0.45;
+    faint.foreground_color.b = 0.25;
+
+    markup_t error = normal;
+    error.foreground_color.r = 1.00;
+    error.foreground_color.g = 0.00;
+    error.foreground_color.b = 0.00;
+
+    markup_t warning = normal;
+    warning.foreground_color.r = 1.00;
+    warning.foreground_color.g = 0.50;
+    warning.foreground_color.b = 0.50;
+
+    markup_t output = normal;
+    output.foreground_color.r = 0.00;
+    output.foreground_color.g = 0.00;
+    output.foreground_color.b = 1.00;
+
+    self->markup[MARKUP_NORMAL] = normal;
+    self->markup[MARKUP_ERROR] = error;
+    self->markup[MARKUP_WARNING] = warning;
+    self->markup[MARKUP_OUTPUT] = output;
+    self->markup[MARKUP_FAINT] = faint;
+    self->markup[MARKUP_BOLD] = bold;
+    self->markup[MARKUP_ITALIC] = italic;
+    self->markup[MARKUP_BOLD_ITALIC] = bold_italic;
+
+    return self;
+}
+
+
+
+// -------------------------------------------------------- console_delete ---
+void
+console_delete( console_t *self )
+{ }
+
+
+
+// ----------------------------------------------------- console_add_glyph ---
+void
+console_add_glyph( console_t *self,
+                   wchar_t current,
+                   wchar_t previous,
+                   markup_t *markup )
+{
+    texture_glyph_t *glyph  = texture_font_get_glyph( markup->font, current );
+    if( previous != L'\0' )
+    {
+        self->pen.x += texture_glyph_get_kerning( glyph, previous );
+    }
+    float r = markup->foreground_color.r;
+    float g = markup->foreground_color.g;
+    float b = markup->foreground_color.b;
+    float a = markup->foreground_color.a;
+    float x0  = self->pen.x + glyph->offset_x;
+    float y0  = self->pen.y + glyph->offset_y;
+    float x1  = x0 + glyph->width;
+    float y1  = y0 - glyph->height;
+    float s0 = glyph->s0;
+    float t0 = glyph->t0;
+    float s1 = glyph->s1;
+    float t1 = glyph->t1;
+
+    GLuint indices[] = {0,1,2, 0,2,3};
+    vertex_t vertices[] = { { x0,y0,0,  s0,t0,  r,g,b,a },
+                            { x0,y1,0,  s0,t1,  r,g,b,a },
+                            { x1,y1,0,  s1,t1,  r,g,b,a },
+                            { x1,y0,0,  s1,t0,  r,g,b,a } };
+    vertex_buffer_push_back( self->buffer, vertices, 4, indices, 6 );
+    
+    self->pen.x += glyph->advance_x;
+    self->pen.y += glyph->advance_y;
+}
+
+
+
+// -------------------------------------------------------- console_render ---
+void
+console_render( console_t *self )
+{
+    int viewport[4];
+    glGetIntegerv( GL_VIEWPORT, viewport );
+
+    size_t i, index;
+    self->pen.x = 0;
+    self->pen.y = viewport[3];
+    vertex_buffer_clear( console->buffer );
+
+    int cursor_x = self->pen.x;
+    int cursor_y = self->pen.y;
+
+    markup_t markup;
+
+    // console_t buffer
+    markup = self->markup[MARKUP_FAINT];
+    self->pen.y -= markup.font->height;
+
+    for( i=0; i<self->lines->size; ++i )
+    {
+        wchar_t *text = * (wchar_t **) vector_get( self->lines, i ) ;
+        if( wcslen(text) > 0 )
+        {
+            console_add_glyph( console, text[0], L'\0', &markup );
+            for( index=1; index < wcslen(text)-1; ++index )
+            {
+                console_add_glyph( console, text[index], text[index-1], &markup );
+            }
+        }
+        self->pen.y -= markup.font->height - markup.font->linegap;
+        self->pen.x = 0;
+        cursor_x = self->pen.x;
+        cursor_y = self->pen.y;
+    }
+
+    // Prompt
+    markup = self->markup[MARKUP_BOLD];
+    if( wcslen( self->prompt ) > 0 )
+    {
+        console_add_glyph( console, self->prompt[0], L'\0', &markup );
+        for( index=1; index < wcslen(self->prompt); ++index )
+        {
+            console_add_glyph( console, self->prompt[index], self->prompt[index-1], &markup );
+        }
+    }
+    cursor_x = (int) self->pen.x;
+
+    // Input
+    markup = self->markup[MARKUP_NORMAL];
+    if( wcslen(self->input) > 0 )
+    {
+        console_add_glyph( console, self->input[0], L'\0', &markup );
+        if( self->cursor > 0)
+        {
+            cursor_x = (int) self->pen.x;
+        }
+        for( index=1; index < wcslen(self->input); ++index )
+        {
+            console_add_glyph( console, self->input[index], self->input[index-1], &markup );
+            if( index < self->cursor )
+            {
+                cursor_x = (int) self->pen.x;
+            }
+        }
+    }
+
+    float cursorblink = 0.7+abs(cos(millis*0.05)+0.3);
+
+    // Cursor (we use the black character (-1) as texture )
+    texture_glyph_t *glyph  = texture_font_get_glyph( markup.font, -1 );
+    float r = markup.foreground_color.r;
+    float g = markup.foreground_color.g;
+    float b = markup.foreground_color.b;
+    float a = markup.foreground_color.a*cursorblink;
+    float x0  = cursor_x+1;
+    float y0  = cursor_y + markup.font->descender;
+    float x1  = cursor_x+14;
+    float y1  = y0 + markup.font->height - markup.font->linegap;
+
+    if (y0 == 714) { y0 = 648+33; y1 = y0 + markup.font->height - markup.font->linegap; }
+
+    float s0 = glyph->s0;
+    float t0 = glyph->t0;
+    float s1 = glyph->s1;
+    float t1 = glyph->t1;
+    GLuint indices[] = {0,1,2, 0,2,3};
+    vertex_t vertices[] = { { x0,y0,0,  s0,t0,  r,g,b,a },
+                            { x0,y1,0,  s0,t1,  r,g,b,a },
+                            { x1,y1,0,  s1,t1,  r,g,b,a },
+                            { x1,y0,0,  s1,t0,  r,g,b,a } };
+    vertex_buffer_push_back( self->buffer, vertices, 4, indices, 6 );
+    glEnable( GL_TEXTURE_2D );
+    glEnable(GL_BLEND);
+    glActiveTexture(GL_TEXTURE0);
+    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+    glBindTexture(GL_TEXTURE_2D, atlas->id);
+
+    glUseProgram( shader );
+    {
+        glUniform1i( glGetUniformLocation( shader, "texture" ),
+                     0 );
+        glUniformMatrix4fv( glGetUniformLocation( shader, "model" ),
+                            1, 0, model.data);
+        glUniformMatrix4fv( glGetUniformLocation( shader, "view" ),
+                            1, 0, view.data);
+        glUniformMatrix4fv( glGetUniformLocation( shader, "projection" ),
+                            1, 0, projection.data);
+        vertex_buffer_render( console->buffer, GL_TRIANGLES );
+    }
+
+
+}
+
+
+
+// ------------------------------------------------------- console_connect ---
+void
+console_connect( console_t *self,
+                  const char *signal,
+                  void (*handler)(console_t *, wchar_t *))
+{
+    if( strcmp( signal,"activate" ) == 0 )
+    {
+        self->handlers[__SIGNAL_ACTIVATE__] = handler;
+    }
+    else if( strcmp( signal,"complete" ) == 0 )
+    {
+        self->handlers[__SIGNAL_COMPLETE__] = handler;
+    }
+    else if( strcmp( signal,"history-next" ) == 0 )
+    {
+        self->handlers[__SIGNAL_HISTORY_NEXT__] = handler;
+    }
+    else if( strcmp( signal,"history-prev" ) == 0 )
+    {
+        self->handlers[__SIGNAL_HISTORY_PREV__] = handler;
+    }
+}
+
+
+
+// --------------------------------------------------------- console_print ---
+void
+console_print( console_t *self, wchar_t *text )
+{
+    // Make sure there is at least one line
+    if( self->lines->size == 0 )
+    {
+        wchar_t *line = wcsdup( L"" );
+        vector_push_back( self->lines, &line );
+    }
+
+    if (self->lines->size > 10)
+    {
+        vector_erase(self->lines,0);
+    }
+
+    // Make sure last line does not end with '\n'
+    wchar_t *last_line = *(wchar_t **) vector_get( self->lines, self->lines->size-1 ) ;
+    if( wcslen( last_line ) != 0 )
+    {
+        if( last_line[wcslen( last_line ) - 1] == L'\n' )
+        {
+            wchar_t *line = wcsdup( L"" );
+            vector_push_back( self->lines, &line );
+        }
+    }
+    last_line = *(wchar_t **) vector_get( self->lines, self->lines->size-1 ) ;
+
+    wchar_t *start = text;
+    wchar_t *end   = wcschr(start, L'\n');
+    size_t len = wcslen( last_line );
+    if( end != NULL)
+    {
+        wchar_t *line = (wchar_t *) malloc( (len + end - start + 2)*sizeof( wchar_t ) );
+        wcpncpy( line, last_line, len );
+        wcpncpy( line + len, text, end-start+1 );
+
+        line[len+end-start+1] = L'\0';
+
+        vector_set( self->lines, self->lines->size-1, &line );
+        free( last_line );
+        if( (end-start)  < (wcslen( text )-1) )
+        {
+            console_print(self, end+1 );
+        }
+        return;
+    }
+    else
+    {
+        wchar_t *line = (wchar_t *) malloc( (len + wcslen(text) + 1) * sizeof( wchar_t ) );
+        wcpncpy( line, last_line, len );
+        wcpcpy( line + len, text );
+        vector_set( self->lines, self->lines->size-1, &line );
+        free( last_line );
+        return;
+    }
+}
+
+
+
+// ------------------------------------------------------- console_process ---
+void
+console_process( console_t *self,
+                  const char *action,
+                  const unsigned char key )
+{
+    size_t len = wcslen(self->input);
+
+    //printf("console_process:%d\n", key);
+
+    if( strcmp(action, "type") == 0 )
+    {
+        if( len < MAX_LINE_LENGTH )
+        {
+            memmove( self->input + self->cursor+1,
+                     self->input + self->cursor, 
+                     (len - self->cursor+1)*sizeof(wchar_t) );
+            self->input[self->cursor] = (wchar_t) key;
+            self->cursor++;
+        }
+        else
+        {
+            fprintf( stderr, "Input buffer is full\n" );
+        }
+    }
+    else
+    {
+        if( strcmp( action, "enter" ) == 0 )
+        {
+            if( console->handlers[__SIGNAL_ACTIVATE__] )
+            {
+                (*console->handlers[__SIGNAL_ACTIVATE__])(console, console->input);
+            }
+            console_print( self, self->prompt );
+            console_print( self, self->input );
+            console_print( self, L"\n" );
+            self->input[0] = L'\0';
+            self->cursor = 0;
+        }
+        else if( strcmp( action, "right" ) == 0 )
+        {
+            if( self->cursor < wcslen(self->input) )
+            {
+                self->cursor += 1;
+            }
+        }
+        else if( strcmp( action, "left" ) == 0 )
+        {
+            if( self->cursor > 0 )
+            {
+                self->cursor -= 1;
+            }
+        }
+        else if( strcmp( action, "delete" ) == 0 )
+        {
+            memmove( self->input + self->cursor,
+                     self->input + self->cursor+1, 
+                     (len - self->cursor)*sizeof(wchar_t) );
+        }
+        else if( strcmp( action, "backspace" ) == 0 )
+        {
+            if( self->cursor > 0 )
+            {
+                memmove( self->input + self->cursor-1,
+                         self->input + self->cursor, 
+                         (len - self->cursor+1)*sizeof(wchar_t) );
+                self->cursor--;
+            }
+        }
+        else if( strcmp( action, "kill" ) == 0 )
+        {
+            if( self->cursor < len )
+            {
+                wcpcpy(self->killring, self->input + self->cursor );
+                self->input[self->cursor] = L'\0';
+                fwprintf(stderr, L"Kill ring: %ls\n", self->killring);
+            }
+            
+        }
+        else if( strcmp( action, "yank" ) == 0 )
+        {
+            size_t l = wcslen(self->killring);
+            if( (len + l) < MAX_LINE_LENGTH )
+            {
+                memmove( self->input + self->cursor + l,
+                         self->input + self->cursor, 
+                         (len - self->cursor)*sizeof(wchar_t) );
+                memcpy( self->input + self->cursor,
+                        self->killring, l*sizeof(wchar_t));
+                self->cursor += l;
+            }
+        }
+        else if( strcmp( action, "home" ) == 0 )
+        {
+            self->cursor = 0;
+        }
+        else if( strcmp( action, "end" ) == 0 )
+        {
+            self->cursor = wcslen( self->input );
+        }
+        else if( strcmp( action, "clear" ) == 0 )
+        {
+        }
+        else if( strcmp( action, "history-prev" ) == 0 )
+        {
+            if( console->handlers[__SIGNAL_HISTORY_PREV__] )
+            {
+                (*console->handlers[__SIGNAL_HISTORY_PREV__])(console, console->input);
+            }
+        }
+        else if( strcmp( action, "history-next" ) == 0 )
+        {
+            if( console->handlers[__SIGNAL_HISTORY_NEXT__] )
+            {
+                (*console->handlers[__SIGNAL_HISTORY_NEXT__])(console, console->input);
+            }
+        }
+        else if( strcmp( action, "complete" ) == 0 )
+        {
+            if( console->handlers[__SIGNAL_COMPLETE__] )
+            {
+                (*console->handlers[__SIGNAL_COMPLETE__])(console, console->input);
+            }
+        }
+    }
+}
+
+
+
+
+
+
+
+
+// debug
+
+int mouseX;
+int mouseY;
+
+// shaders
+
+GLuint shaders[10];
+const char* shaderss[] = {  "data/shaders/v3f-t2f-c4f.frag",
+                            "data/shaders/projector",
+                            "data/shaders/eye",
+                            "data/shaders/eye_post",
+                            "data/shaders/fsquad",
+                            "data/shaders/copquad",
+                            "data/shaders/redcircle",
+                            "data/shaders/vhs",
+                            "data/shaders/yuv2rgb",
+                            "data/shaders/hex"};
+enum shaderi { nannanna, projector, eye, eye_post, fsquad, copquad, redcircle, vhs, yuv2rgb, hex };
+
+GLuint depth_rb = 0;
+GLuint depth_rb2 = 0;
+GLuint depth_rb3 = 0;
+// textures
+
+int textures[65] = {-1};
+const char* texturess[] = {"data/gfx/scene.jpg",
+                    "data/gfx/dude1.jpg",
+                    "data/gfx/dude2.jpg",
+                    "data/gfx/mask.jpg",
+                    "data/gfx/note.jpg",
+                    "data/gfx/exit.jpg",
+                    "data/gfx/v0.png",
+                    "data/gfx/v1.png",
+                    "data/gfx/v2.png",
+                    "data/gfx/v3.png",
+                    "data/gfx/v4.png",
+                    "data/gfx/v5.png",
+                    "data/gfx/v6.png",
+                    "data/gfx/v7.png",
+                    "data/gfx/v8.png",
+                    "data/gfx/v9.png",
+                    "data/gfx/v9a.png",
+                    "data/gfx/v9b.png",
+                    "data/gfx/v9c.png",
+                    "data/gfx/copkiller1.jpg",
+                    "data/gfx/prip1.jpg",
+                    "data/gfx/copkiller2.jpg",
+                    "data/gfx/prip2.jpg",
+                    "data/gfx/copkiller3.jpg",
+                    "data/gfx/prip3.jpg",
+                    "data/gfx/copkiller4.jpg",
+                    "data/gfx/prip4.jpg",
+                    "data/gfx/copkiller5.jpg",
+                    "data/gfx/prip5.jpg",
+                    "data/gfx/copkiller6.jpg",
+                    "data/gfx/prip6.jpg",
+                    "data/gfx/copkiller7.jpg",
+                    "data/gfx/prip7.jpg",
+                    "data/gfx/copkiller8.jpg",
+                    "data/gfx/prip8.jpg",
+                    "data/gfx/copkiller9.jpg",
+                    "data/gfx/prip9.jpg",
+                    "data/gfx/copkiller10.jpg",
+                    "data/gfx/prip10.jpg",
+                    "data/gfx/copkiller11.jpg",
+                    "data/gfx/prip11.jpg",
+                    "data/gfx/copkiller12.jpg",
+                    "data/gfx/prip12.jpg",
+                    "data/gfx/copkiller13.jpg",
+                    "data/gfx/prip13.jpg",
+                    "data/gfx/copkiller14.jpg",
+                    "data/gfx/prip14.jpg",
+                    "data/gfx/copkiller15.jpg",
+                    "data/gfx/prip15.jpg",
+                    "data/gfx/aegis.jpg",
+                    "data/gfx/ll1.png",
+                    "data/gfx/ll2.png",
+                    "data/gfx/ll3.png",
+                    "data/gfx/ll4.png",
+                    "data/gfx/ll5.png",
+                    "data/gfx/grayeye.jpg",
+                    "data/gfx/room1.jpg",
+                    "data/gfx/room2.jpg",
+                    "data/gfx/room3.jpg",
+                    "data/gfx/majic1.jpg",
+                    "data/gfx/majic2.jpg",
+                    "data/gfx/majic3.jpg",
+                    "data/gfx/majic4.jpg",
+                    "data/gfx/bilogon.png",
+                    "data/gfx/noise.jpg"};
+enum texturi { tex_scene, tex_dude, tex_dude2, tex_mask, tex_note, tex_exit,
+                tex_v0,tex_v1,tex_v2,tex_v3,tex_v4,tex_v5,tex_v6,tex_v7,tex_v8,tex_v9,tex_v9a,tex_v9b,tex_v9c,
+                tex_copkiller, tex_prip,
+                tex_copkiller2, tex_prip2,
+                tex_copkiller3, tex_prip3,
+                tex_copkiller4, tex_prip4,
+                tex_copkiller5, tex_prip5,
+                tex_copkiller6, tex_prip6,
+                tex_copkiller7, tex_prip7,
+                tex_copkiller8, tex_prip8,
+                tex_copkiller9, tex_prip9,
+                tex_copkiller10,tex_prip10,
+                tex_copkiller11,tex_prip11,
+                tex_copkiller12,tex_prip12,
+                tex_copkiller13,tex_prip13,
+                tex_copkiller14,tex_prip14,
+                tex_copkiller15,tex_prip15,
+
+                tex_aegis, tex_ll1,tex_ll2,tex_ll3,tex_ll4,tex_ll5,
+                tex_grayeye, tex_room, tex_room2, tex_room3,
+                tex_majestic1, tex_majestic2, tex_majestic3, tex_majestic4,
+                tex_bilogon,
+                tex_noise};
+
+// texture switchers
+
+int room_texnum = 0;
+
+// assimp scenes
+
+Assimp::Importer importer[7];
+
+const aiScene *kapsule, *bilothree, *brieflycase,
+              *bilothorn, *biloflat, *bilotetra;
+
+std::map<std::string, GLuint*> textureIdMap; // map image filenames to textureIds
+GLuint* textureIds; // pointer to texture array
+
+static float kujalla_angle = 0.f;
+int beatmode = -1;
+
+// assimp defines
+
+#define aisgl_min(x,y) (x<y?x:y)
+#define aisgl_max(x,y) (y>x?y:x)
+
+// misc. gfx system
+
+static GLfloat g_nearPlane = 0.001;
+static GLfloat g_farPlane = 1000;
+static int c_Width = 640;
+static int c_Height = 360;
+static int g_Width = 1280;
+static int g_Height = 720;
+
+class YUVFrame {
+public:
+    YUVFrame(OggPlayer oggstream):ogg(oggstream) {
+        width = ogg.width(); height = ogg.height();
+        // The textures are created when rendering the first frame
+        y_tex = u_tex = v_tex = -1 ;
+    }
+    ~YUVFrame() {
+        glDeleteTextures(1,&y_tex);
+        glDeleteTextures(1,&u_tex);
+        glDeleteTextures(1,&v_tex);
+    }
+    void play() {
+        ogg.play();
+    }
+    void close()
+    {
+        ogg.close();
+    }
+    void render() {
+        update();
+        if(-1==y_tex) return; // not ready yet
+        glUseProgram(shaders[yuv2rgb]);
+
+        GLint widthLoc5 = glGetUniformLocation(shaders[yuv2rgb], "width");
+        GLint heightLoc5 = glGetUniformLocation(shaders[yuv2rgb], "height");
+        glUniform1f(widthLoc5, g_Width);
+        glUniform1f(heightLoc5, g_Height);
+
+        GLint y_pos = glGetUniformLocation(shaders[yuv2rgb],"y_tex");
+        GLint u_pos = glGetUniformLocation(shaders[yuv2rgb],"u_tex");
+        GLint v_pos = glGetUniformLocation(shaders[yuv2rgb],"v_tex");
+
+        glDisable(GL_BLEND);
+
+        glActiveTexture(GL_TEXTURE0);
+        glBindTexture(GL_TEXTURE_2D, y_tex);
+        glUniform1i(y_pos, 0);
+        glActiveTexture(GL_TEXTURE1);
+        glBindTexture(GL_TEXTURE_2D, u_tex);
+        glUniform1i(u_pos, 1);
+        glActiveTexture(GL_TEXTURE2);
+        glBindTexture(GL_TEXTURE_2D, v_tex);
+        glUniform1i(v_pos, 2);
+
+        glLoadIdentity();
+
+        glTranslatef(-1.2, -1.0, -1.0);
+
+        int i=0;
+        int j=0;
+        glBegin(GL_QUADS);
+        glVertex2f(i, j);
+        glVertex2f(i + 100, j);
+        glVertex2f(i + 100, j + 100);
+        glVertex2f(i, j + 100);
+        glEnd();
+
+    }
+private:
+    void update() {
+        YUVBuffer yuv;
+        // yuv_buffer_try_lock(...) returns false if the last read frame is
+        // still up to date, in this case we can simply retender the 
+        // last frame without an update
+        // We don't need to call unlock unless the lock operation was successfull
+        if(!ogg.yuv_buffer_try_lock(&yuv)) return;
+        // Create the textures if needed, at this point we 
+        // know how big the textures should be.
+        // The sample plyer that comes with the official SDK
+        // assummes uv_width=y_width/2 , uv_height=y_height/2
+        // but I'm not sure whether that is always true
+        if(-1==y_tex){
+            y_tex = gen_texture(yuv.y_width,yuv.y_height);
+            u_tex = gen_texture(yuv.uv_width,yuv.uv_height);
+            v_tex = gen_texture(yuv.uv_width,yuv.uv_height);
+        }
+        int y_offset = ogg.offset_x() + yuv.y_stride * ogg.offset_y();
+        int uv_offset = ogg.offset_x()/(yuv.y_width/yuv.uv_width)+
+            yuv.uv_stride * ogg.offset_y()/(yuv.y_height/yuv.uv_height);
+        update_texture(y_tex,yuv.y+y_offset,yuv.y_width,yuv.y_height,yuv.y_stride);
+        update_texture(u_tex,yuv.u+uv_offset,yuv.uv_width,yuv.uv_height,yuv.uv_stride);
+        update_texture(v_tex,yuv.v+uv_offset,yuv.uv_width,yuv.uv_height,yuv.uv_stride);
+        ogg.yuv_buffer_unlock();
+    }
+    GLuint gen_texture(int w,int h) {
+        GLuint tex;
+        glGenTextures(1,&tex);
+        glBindTexture(GL_TEXTURE_2D,tex);
+        glTexImage2D(GL_TEXTURE_2D,0,1,w,h,0,GL_LUMINANCE,GL_UNSIGNED_BYTE,NULL);
+        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT );
+        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT );
+        return tex;
+    }
+    void update_texture(GLuint tex, unsigned char* buf,int w,int h, int stride) {
+        glPixelStorei(GL_UNPACK_ROW_LENGTH,stride);
+        glBindTexture(GL_TEXTURE_2D,tex);
+        glTexSubImage2D(GL_TEXTURE_2D,0,0,0,w,h,GL_LUMINANCE,GL_UNSIGNED_BYTE,buf);
+        // set GL_UNPACK_ROW_LENGTH back to 0 to avoid bugs 
+        glPixelStorei(GL_UNPACK_ROW_LENGTH,0);
+    }
+    OggPlayer ogg;
+    GLuint y_tex,u_tex,v_tex;
+    int width,height;
+};
+
+YUVFrame* myVideoFrame;
+
+void InitFBO();
+
+int frame = 0;
+
+// effect pointers && logic && state
+
+int current_scene;
+
+void dummy(float dt) {}
+
+void Loader();
+void LeadMaskScene();
+void CopScene();
+void MarssiScene();
+void LongScene();
+void EyeScene();
+void RedCircleScene();
+void BiloThreeScene();
+void KolmeDeeScene();
+
+void KolmeDeeLogic(float dt);
+//void LoaderLogic(float dt);
+void ConsoleLogic(float dt);
+void ConsoleLogic2(float dt);
+
+typedef void (*SceneRenderCallback)();
+SceneRenderCallback scene_render[] = {
+                                        &Loader,
+                                        &LeadMaskScene,
+                                        &CopScene,
+                                        &MarssiScene,
+                                        &LongScene,
+                                        &EyeScene, 
+                                        &RedCircleScene,
+                                        &BiloThreeScene
+                                     };
+
+typedef void (*SceneLogicCallback)(float);
+SceneLogicCallback scene_logic[] = {
+                                        &dummy,
+                                        &ConsoleLogic,
+                                        &dummy,
+                                        &ConsoleLogic2,
+                                        &dummy,
+                                        &dummy,
+                                        &dummy,
+                                        &dummy
+                                     };
+
+// audio
+
+float scene_start_millis = 0;
+
+int music_started = -1;
+
+DWORD music_channel = 0;
+
+
+/////////////////////////////////////////////////////////
+//////////////////////// PLAYLIST ////////////////////////
+///////////////////////////////////////////////////////////
+
+void reshape(GLint width, GLint height)
+{
+    g_Width = width;
+    g_Height = height;
+
+    printf("--- MIDISYS ENGINE: reshape event: %dx%d\n", (int)width, (int)height);
+
+    glViewport(0, 0, g_Width, g_Height);
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    gluPerspective(65.0, (float)g_Width / g_Height, g_nearPlane, g_farPlane);
+    glMatrixMode(GL_MODELVIEW);
+
+    glDeleteTextures(1, &fb_tex);
+    glDeleteRenderbuffersEXT(1, &depth_rb);
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+    glDeleteFramebuffersEXT(1, &fb);
+
+    glDeleteTextures(1, &fb_tex2);
+    glDeleteRenderbuffersEXT(1, &depth_rb2);
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+    glDeleteFramebuffersEXT(1, &fb2);
+
+    glDeleteTextures(1, &fake_framebuffer_tex);
+    glDeleteRenderbuffersEXT(1, &depth_rb3);
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+    glDeleteFramebuffersEXT(1, &fake_framebuffer);
+
+    InitFBO();
+}
+
+
+int demo_playlist()
+{
+    int sc = current_scene;
+    if (millis >= 0 && millis < 111844) // 55922
+    {
+        current_scene = 1; // lead masks
+    }
+    else if (millis >= 111844 && millis < 148800)
+    {
+        current_scene = 2; // cops
+    }
+    else if (millis >= 148800 && millis < 184000)
+    {
+        current_scene = 3; // marssi
+    }
+    else if (millis >= 184000 && millis < 188737)
+    {
+        current_scene = 4; // "long live the new flesh"
+    }
+    else if (millis >= 188737 && millis < 264000)
+    {
+        current_scene = 5; // eye horror
+    }
+    else if (millis >= 264000 && millis < 300200)
+    {
+        current_scene = 6; // outro 1 / redcircle
+    }
+    else if (millis >= 300200 && millis < 320000)
+    {
+        current_scene = 7; // outro 2 / bilothree
+    }
+
+    if (sc != current_scene)
+    {
+        if (current_scene == 4) {
+            myVideoFrame->close();
+        }
+
+        if (current_scene == 6)
+        {
+            reshape(g_Width, g_Height);
+        }
+        /*if(current_scene == 1) 
+        {
+            //printf("xx XX xxxxxxxRESHAPING HAXXXXxxxxx xxx XX xx");
+            //glutLeaveMainLoop();
+            //glutReshapeWindow(g_Width, g_Height);
+            //glutFullScreen();
+            //glutMainLoop();
+         }*/
+        scene_start_millis = millis;
+        vector_clear(console->lines);
+    }
+}
+
+///////////////////////////////////////////////////// HELPER FUNCTIONS
+
+void BassError(const char *text) 
+{
+    printf("BASS error(%d): %s\n",BASS_ErrorGetCode(),text);
+    BASS_Free();
+    exit(0);
+}
+
+void InitAudio(const char *pFilename)
+{
+    DWORD chan;
+    QWORD pos;
+
+    printf("--- MIDISYS-ENGINE: InitAudio()\n");
+
+    if (!BASS_Init(-1,44100,0,0,NULL)) BassError("InitAudio() - can't initialize device\n");
+
+    printf("\tInitAudio() - loading soundtrack from file \"%s\"\n", pFilename);
+
+    int success = 0;
+
+    if ((chan=BASS_StreamCreateFile(FALSE,pFilename,0,0,BASS_STREAM_PRESCAN))
+        || (chan=BASS_StreamCreateURL(pFilename,0,BASS_STREAM_PRESCAN,0,0))) {
+        pos=BASS_ChannelGetLength(chan,BASS_POS_BYTE);
+        if (BASS_StreamGetFilePosition(chan,BASS_FILEPOS_DOWNLOAD)!=-1) {
+            // streaming from the internet
+            if (pos!=-1)
+            {
+                printf("\tInitAudio() - streaming internet file [%I64u bytes]\n",pos);
+                success = 1;
+            }
+            else
+            {
+                printf("\tstreaming internet file\n");
+                success = 1;
+            }
+        } else
+            {
+                printf("\tstreaming file [%I64u bytes]\n",pos);
+                success = 1;
+            }
+    }
+
+    if (success == 1)
+    {
+        music_channel = chan;
+    }
+    else
+    {
+        printf("InitAudio() error! could not load file.\n");
+        exit(1);
+    }
+}
+
+void HexList(BYTE *pData, int iNumBytes)
+{
+    int i;
+
+    for(i=0;i<iNumBytes;i++)
+        printf("%.2x ", pData[i]);
+}
+
+char *File2String(const char *path)
+{
+    FILE *fd;
+    long len,
+         r;
+    char *str;
+ 
+    if (!(fd = fopen(path, "r")))
+    {
+        fprintf(stderr, "\terror, can't open file '%s' for reading\n", path);
+        return NULL;
+    }
+ 
+    fseek(fd, 0, SEEK_END);
+    len = ftell(fd);
+ 
+    #ifdef SUPERVERBOSE
+    printf("\tshader source file \"%s\" is %ld bytes long\n", path, len);
+    #endif
+
+    fseek(fd, 0, SEEK_SET);
+ 
+    if (!(str = (char*) malloc(len * sizeof(char))))
+    {
+        fprintf(stderr, "\terror, can't malloc space for file \"%s\"\n", path);
+        return NULL;
+    }
+ 
+    r = fread(str, sizeof(char), len, fd);
+ 
+    str[r - 1] = '\0'; /* Shader sources have to term with null */
+ 
+    fclose(fd);
+ 
+    return str;
+}
+
+void PrintShaderLog(GLuint obj)
+{
+    int infologLength = 0;
+    int maxLength;
+ 
+    if(glIsShader(obj))
+        glGetShaderiv(obj,GL_INFO_LOG_LENGTH,&maxLength);
+    else
+        glGetProgramiv(obj,GL_INFO_LOG_LENGTH,&maxLength);
+ 
+    char infoLog[maxLength];
+ 
+    if (glIsShader(obj))
+        glGetShaderInfoLog(obj, maxLength, &infologLength, infoLog);
+    else
+        glGetProgramInfoLog(obj, maxLength, &infologLength, infoLog);
+ 
+    if (infologLength > 0)
+    {
+        printf("\n%s\n",infoLog);
+    }
+}
+
+void DebugPrintEvent(int ev, MIDI_MSG msg)
+{
+#if 0
+    char str[128];
+    unsigned int j;
+    if (muGetMIDIMsgName(str, ev))  printf("%s\t", str);
+
+    switch(ev)
+        {
+        case    msgNoteOff:
+                muGetNameFromNote(str, msg.MsgData.NoteOff.iNote);
+                printf("(%.2d) %s", msg.MsgData.NoteOff.iChannel, str);
+                break;
+        case    msgNoteOn:
+                muGetNameFromNote(str, msg.MsgData.NoteOn.iNote);
+                printf("\t(%.2d) %s %d", msg.MsgData.NoteOn.iChannel, str, msg.MsgData.NoteOn.iVolume);
+                break;
+        case    msgNoteKeyPressure:
+                muGetNameFromNote(str, msg.MsgData.NoteKeyPressure.iNote);
+                printf("(%.2d) %s %d", msg.MsgData.NoteKeyPressure.iChannel, 
+                        str,
+                        msg.MsgData.NoteKeyPressure.iPressure);
+                break;
+        case    msgSetParameter:
+                muGetControlName(str, msg.MsgData.NoteParameter.iControl);
+                printf("(%.2d) %s -> %d", msg.MsgData.NoteParameter.iChannel, 
+                        str, msg.MsgData.NoteParameter.iParam);
+                break;
+        case    msgSetProgram:
+                muGetInstrumentName(str, msg.MsgData.ChangeProgram.iProgram);
+                printf("(%.2d) %s", msg.MsgData.ChangeProgram.iChannel, str);
+                break;
+        case    msgChangePressure:
+                muGetControlName(str, msg.MsgData.ChangePressure.iPressure);
+                printf("(%.2d) %s", msg.MsgData.ChangePressure.iChannel, str);
+                break;
+        case    msgSetPitchWheel:
+                printf("(%.2d) %d", msg.MsgData.PitchWheel.iChannel,  
+                        msg.MsgData.PitchWheel.iPitch);
+                break;
+
+        case    msgMetaEvent:
+                printf("---- ");
+                switch(msg.MsgData.MetaEvent.iType)
+                    {
+                    case    metaMIDIPort:
+                            printf("MIDI Port = %d", msg.MsgData.MetaEvent.Data.iMIDIPort);
+                            break;
+
+                    case    metaSequenceNumber:
+                            printf("Sequence Number = %d",msg.MsgData.MetaEvent.Data.iSequenceNumber);
+                            break;
+
+                    case    metaTextEvent:
+                            printf("Text = '%s'",msg.MsgData.MetaEvent.Data.Text.pData);
+                            break;
+                    case    metaCopyright:
+                            printf("Copyright = '%s'",msg.MsgData.MetaEvent.Data.Text.pData);
+                            break;
+                    case    metaTrackName:
+                            printf("Track name = '%s'",msg.MsgData.MetaEvent.Data.Text.pData);
+                            break;
+                    case    metaInstrument:
+                            printf("Instrument = '%s'",msg.MsgData.MetaEvent.Data.Text.pData);
+                            break;
+                    case    metaLyric:
+                            printf("Lyric = '%s'",msg.MsgData.MetaEvent.Data.Text.pData);
+                            break;
+                    case    metaMarker:
+                            printf("Marker = '%s'",msg.MsgData.MetaEvent.Data.Text.pData);
+                            break;
+                    case    metaCuePoint:
+                            printf("Cue point = '%s'",msg.MsgData.MetaEvent.Data.Text.pData);
+                            break;
+                    case    metaEndSequence:
+                            printf("End Sequence");
+                            break;
+                    case    metaSetTempo:
+                            printf("Tempo = %d",msg.MsgData.MetaEvent.Data.Tempo.iBPM);
+                            break;
+                    case    metaSMPTEOffset:
+                            printf("SMPTE offset = %d:%d:%d.%d %d",
+                                    msg.MsgData.MetaEvent.Data.SMPTE.iHours,
+                                    msg.MsgData.MetaEvent.Data.SMPTE.iMins,
+                                    msg.MsgData.MetaEvent.Data.SMPTE.iSecs,
+                                    msg.MsgData.MetaEvent.Data.SMPTE.iFrames,
+                                    msg.MsgData.MetaEvent.Data.SMPTE.iFF
+                                    );
+                            break;
+                    case    metaTimeSig:
+                            printf("Time sig = %d/%d",msg.MsgData.MetaEvent.Data.TimeSig.iNom,
+                                        msg.MsgData.MetaEvent.Data.TimeSig.iDenom/MIDI_NOTE_CROCHET);
+                            break;
+                    case    metaKeySig:
+                            if (muGetKeySigName(str, msg.MsgData.MetaEvent.Data.KeySig.iKey))
+                                printf("Key sig = %s", str);
+                            break;
+
+                    case    metaSequencerSpecific:
+                            printf("Sequencer specific = ");
+                            HexList(msg.MsgData.MetaEvent.Data.Sequencer.pData, msg.MsgData.MetaEvent.Data.Sequencer.iSize);
+                            break;
+                    }
+                break;
+
+        case    msgSysEx1:
+        case    msgSysEx2:
+                printf("Sysex = ");
+                HexList(msg.MsgData.SysEx.pData, msg.MsgData.SysEx.iSize);
+                break;
+        }
+
+    if (ev == msgSysEx1 || ev == msgSysEx1 || (ev==msgMetaEvent && msg.MsgData.MetaEvent.iType==metaSequencerSpecific))
+        {
+        /* Already done a hex dump */
+        }
+    else
+        {
+        printf("\t[");
+        if (msg.bImpliedMsg) printf("%.2x!", msg.iImpliedMsg);
+        for(j=0;j<msg.iMsgSize;j++)
+            printf("%.2x ", msg.data[j]);
+        printf("]\n");
+        }
+#endif
+}
+
+void LoadMIDIEventList(const char *pFilename)
+{
+    printf("--- MIDISYS ENGINE: LoadMIDIEventList(\"%s\")\n", pFilename);
+    MIDI_FILE *mf = midiFileOpen(pFilename);
+    int ev;
+
+    int timeline_index = 0;
+    int track_index = 0;
+    MIDI_MSG msg;
+
+    if (mf)
+    {
+        int i, iNum;
+
+        midiReadInitMessage(&msg);
+        iNum = midiReadGetNumTracks(mf);
+
+        for(i=0;i<iNum;i++)
+        {
+            #ifdef SUPERVERBOSE 
+            printf("# Track %d\n", i);
+            #endif
+            while(midiReadGetNextMessage(mf, i, &msg))
+            {
+                #ifdef SUPERVERBOSE 
+                printf(" %.6ld ", msg.dwAbsPos);
+                #endif
+
+                if (msg.bImpliedMsg) { ev = msg.iImpliedMsg; }
+                else { ev = msg.iType; }
+
+                memcpy(&timeline[track_index][timeline_index], &msg, sizeof(MIDI_MSG));
+
+                if (ev == msgMetaEvent && msg.MsgData.MetaEvent.iType == metaTrackName) 
+                {
+                    strncpy(timeline_trackname[track_index], msg.MsgData.MetaEvent.Data.Text.pData, 8);
+                    timeline_trackname[track_index][8] == '\0';
+                    printf("track #%d, name = \"%s\"\n", track_index, timeline_trackname[track_index]);
+                }
+
+                #ifdef SUPERVERBOSE 
+                DebugPrintEvent(ev,msg); 
+                #endif
+
+                timeline_index++;
+
+            }
+            printf("track length: %d\n", timeline_index);
+            timeline_tracklength[track_index] = timeline_index;
+            track_index++;
+            timeline_index = 0;
+        }
+
+        timeline_trackcount = track_index;
+        midiReadFreeMessage(&msg);
+
+        midiFileClose(mf);
+    }
+
+    //timeline_length = timeline_index+1;
+    printf("--- MIDISYS ENGINE: LoadMIDIEventList() success\n");
+}
+
+void ParseMIDITimeline(const char* mappingFile)
+{
+    printf("--- MIDISYS ENGINE: ParseMIDITimeline(\"%s\")\n", mappingFile);
+    int cnt;
+    char *line;
+
+    FILE *mapfile;
+
+    if ((mapfile = fopen(mappingFile, "r"))) 
+    {
+        cnt = 0;
+        while (0 == fggets(&line, mapfile)) {
+            char* token = strtok(line, ",");
+            int paramnum = atoi(token);
+            mapping_paramnum[cnt] = paramnum;
+
+            token = strtok(NULL, ",");
+            sprintf(mapping_trackname[cnt],"%s",token);
+
+            token = strtok(NULL, ",");
+            if (strcmp("param",token) == 0) mapping_type[cnt] = 1;
+            else mapping_type[cnt] = 0;
+
+//          token = strtok(NULL, ",");
+//          sprintf(englines[cnt],"%s",token);
+
+            int i = 0;
+            int found = -1;
+            for (i = 0; i < timeline_trackcount; i++)
+            {
+                if (strcmp(timeline_trackname[i], mapping_trackname[cnt]) == 0) 
+                {
+                    printf("MIDI track #%d (\"%s\") maps to shader param %d, type: %s\n", i, mapping_trackname[cnt], mapping_paramnum[cnt], mapping_type[cnt] == 0 ? "trig" : "param");
+                    mapping_tracknum[cnt] = i;
+                    cnt++;
+                    found = 1;
+                }
+                if (found == 1) break;
+            }   
+        }
+        mapping_count = cnt;
+    }
+    else
+    {
+        printf("ParseMIDITimeline error: MIDI mapping file \"%s\" cannot be opened\n", mappingFile);
+        exit(1);
+    }
+    printf("--- MIDISYS ENGINE: ParseMIDITimeline() success\n");
+}
+
+
+void stbi_flip_y(int w, int h, int comp, stbi_uc *data)
+{
+   size_t y, i, stride = w * comp;
+   uint8 *out = data;
+
+   for (y = 0; y < (h>>1); ++y) {
+      stbi_uc *p1 = out + y * stride;
+      stbi_uc *p2 = out + (h-1-y) * stride;
+      for (i = 0; i < stride; ++i) {
+         stbi_uc t = p1[i];
+         p1[i] = p2[i];
+         p2[i] = t;       
+      } 
+   }
+}
+
+int scale_image_RGB_to_NTSC_safe(int width, int height, int channels,stbi_uc* data) {
+    const float scale_lo = 16.0f - 0.499f;
+    const float scale_hi = 235.0f + 0.499f;
+    int i, j;
+    int nc = channels;
+
+    unsigned char* orig = data;
+
+    unsigned char scale_LUT[256];
+    /*  error check */
+    if( (width < 1) || (height < 1) ||
+        (channels < 1) || (orig == NULL) )
+    {
+        /*  nothing to do   */
+        return 0;
+    }
+    /*  set up the scaling Look Up Table    */
+    for( i = 0; i < 256; ++i )
+    {
+        scale_LUT[i] = (unsigned char)((scale_hi - scale_lo) * i / 255.0f + scale_lo);
+    }
+    /*  for channels = 2 or 4, ignore the alpha component   */
+    nc -= 1 - (channels & 1);
+    /*  OK, go through the image and scale any non-alpha components */
+    for( i = 0; i < width*height*channels; i += channels )
+    {
+        for( j = 0; j < nc; ++j )
+        {
+            orig[i+j] = scale_LUT[orig[i+j]];
+        }
+    }
+    return 1;
+}
+
+
+GLuint LoadTexture(const char* pFilename, int invert)
+{
+    if(!load_textures) return;
+
+    if (strcmp(pFilename,"") == 0) return 99999;
+    printf(" - LoadTexture(\"%s\")", pFilename);
+    GLuint tex_2d;
+    unsigned char *data;
+    int x, y, comp;
+
+    FILE *file = fopen(pFilename, "rb");
+    if (!file)
+        return 0;
+
+    data = stbi_load_from_file(file, &x, &y, &comp, 0);
+    fclose(file);
+    //gluBuild2DMipmaps( GL_TEXTURE_2D, 4, x, y, GL_RGBA, GL_UNSIGNED_BYTE, data );
+
+    glGenTextures( 1, &tex_2d );
+    glBindTexture( GL_TEXTURE_2D, tex_2d );
+    glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
+    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
+    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+
+
+    if (invert == 1) 
+    {
+        stbi_flip_y(x, y, comp, data);
+    }
+    else
+    {
+    }
+
+    scale_image_RGB_to_NTSC_safe(x,y,comp,data);
+
+    if( comp == 3 ) {
+        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, x, y, 0, GL_RGB, GL_UNSIGNED_BYTE, data );
+    } else if( comp == 4 ) {
+        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data );
+    }
+
+
+    if(0 == tex_2d)
+    {
+        printf(" error loading texture from file \"%s\"\n", pFilename);
+        exit(1);
+    }
+
+    printf(" success\n");
+
+    return tex_2d;
+}
+
+GLuint LoadTexture(const char* pFilename)
+{
+    return LoadTexture(pFilename, 1);
+}
+
+int LoadGLTextures(const aiScene* scene) {
+
+    if (scene->HasTextures())
+    {
+        printf("ERROR: support for meshes with embedded textures is not implemented\n");
+        exit(1);
+    }
+
+    /* scan scene's materials for textures */
+    for (unsigned int m=0; m<scene->mNumMaterials; m++)
+    {
+        int texIndex = 0;
+        aiReturn texFound = AI_SUCCESS;
+
+        aiString path;  // filename
+
+        while (texFound == AI_SUCCESS)
+        {
+            texFound = scene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, texIndex, &path);
+            GLuint texnum = LoadTexture(path.data, 0);
+            textureIdMap[path.data] = texnum;
+            texIndex++;
+        }
+    }
+    //return success;
+    return true;
+}
+
+GLuint LoadShader(const char* pFilename)
+{
+    fprintf(stdout," - LoadShader(\"%s\")", pFilename);
+
+    #ifdef SUPERVERBOSE
+    printf("\n");
+    #endif
+
+    char vsName[256] = "";
+    strcpy(vsName, pFilename);
+    strcat(vsName, ".vs");
+
+    char fsName[256] = "";
+    strcpy(fsName, pFilename);
+    strcat(fsName, ".fs");
+
+    #ifdef SUPERVERBOSE 
+    fprintf(stdout,"\tLoadShader(\"%s\") vertex shader source file: \"%s\"\n", pFilename, vsName);
+    #endif
+
+    GLchar *vsSource = File2String(vsName);
+
+    #ifdef SUPERVERBOSE 
+    fprintf(stdout,"\tLoadShader(\"%s\") vertex shader source:\n----------------------------------------------------\n%s\n----------------------------------------------------\n", pFilename, vsSource);
+    #endif
+
+    #ifdef SUPERVERBOSE 
+    fprintf(stdout,"\tLoadShader(\"%s\") fragment shader source file: \"%s\"\n", pFilename, fsName);
+    #endif
+
+    GLchar *fsSource = File2String(fsName);
+
+    #ifdef SUPERVERBOSE 
+    fprintf(stdout,"\tLoadShader(\"%s\") fragment shader source:\n----------------------------------------------------\n%s\n----------------------------------------------------\n", pFilename, fsSource);
+    #endif
+
+    GLuint vs, fs, sp;
+
+    #ifdef SUPERVERBOSE 
+    printf("\tLoadShader(): vs glCreateShader\n");
+    #endif
+    vs = glCreateShader(GL_VERTEX_SHADER);
+    PrintShaderLog(vs);
+    #ifdef SUPERVERBOSE 
+    printf("\tLoadShader(): vs glShaderSource\n");
+    #endif
+    glShaderSource(vs, 1, (const GLchar**)&vsSource, NULL);
+    PrintShaderLog(vs);
+    #ifdef SUPERVERBOSE 
+    printf("\tLoadShader(): vs glCompileShader\n");
+    #endif
+    glCompileShader(vs);
+    PrintShaderLog(vs);
+    #ifdef SUPERVERBOSE 
+    printf("\tLoadShader(): vs compiled\n");
+    #endif
+
+    #ifdef SUPERVERBOSE 
+    printf("\tLoadShader(): fs glCreateShader\n");
+    #endif
+    fs = glCreateShader(GL_FRAGMENT_SHADER);
+    PrintShaderLog(fs);
+    #ifdef SUPERVERBOSE 
+    printf("\tLoadShader(): fs glShaderSource\n");
+    #endif
+    glShaderSource(fs, 1, (const GLchar**)&fsSource, NULL);
+    PrintShaderLog(fs);
+    #ifdef SUPERVERBOSE 
+    printf("\tLoadShader(): fs glCompileShader\n");
+    #endif
+    glCompileShader(fs);
+    PrintShaderLog(fs);
+    #ifdef SUPERVERBOSE 
+    printf("\tLoadShader(): fs compiled\n");
+    #endif
+
+    free(vsSource);
+    free(fsSource);
+
+    #ifdef SUPERVERBOSE 
+    printf("\tLoadShader(): glCreateProgram\n");
+    #endif
+    sp = glCreateProgram();
+    PrintShaderLog(sp);
+    #ifdef SUPERVERBOSE 
+    printf("\tLoadShader(): glAttachShader vs\n");
+    #endif
+    glAttachShader(sp, vs);
+    PrintShaderLog(sp);
+    #ifdef SUPERVERBOSE 
+    printf("\tLoadShader(): glAttachShader fs\n");
+    #endif
+    glAttachShader(sp, fs);
+    PrintShaderLog(sp);
+    #ifdef SUPERVERBOSE 
+    printf("\tLoadShader(): glLinkProgram\n");
+    #endif
+    glLinkProgram(sp);
+    PrintShaderLog(sp);
+
+    #ifdef SUPERVERBOSE
+    fprintf(stdout,"--- MIDISYS ENGINE: LoadShader(\"%s\") success\n", pFilename);
+    #else
+    printf(" success\n");
+    #endif
+
+    return sp;
+}
+
+///////////////////////////////////////////////////////////////// EFFECTS
+///////////////////////////////////////////////////////////////// EFFECTS
+///////////////////////////////////////////////////////////////// EFFECTS
+///////////////////////////////////////////////////////////////// EFFECTS
+///////////////////////////////////////////////////////////////// EFFECTS
+
+
+// Can't send color down as a pointer to aiColor4D because AI colors are ABGR.
+void Color4f(const aiColor4D *color)
+{
+glColor4f(color->r, color->g, color->b, color->a);
+}
+
+void set_float4(float f[4], float a, float b, float c, float d)
+{
+f[0] = a;
+f[1] = b;
+f[2] = c;
+f[3] = d;
+}
+
+void color4_to_float4(const aiColor4D *c, float f[4])
+{
+f[0] = c->r;
+f[1] = c->g;
+f[2] = c->b;
+f[3] = c->a;
+}
+
+void apply_material(const aiMaterial *mtl)
+{
+    float c[4];
+
+    GLenum fill_mode;
+    int ret1, ret2;
+    aiColor4D diffuse;
+    aiColor4D specular;
+    aiColor4D ambient;
+    aiColor4D emission;
+    float shininess, strength;
+    int two_sided;
+    int wireframe;
+    unsigned int max;   // changed: to unsigned
+
+    int texIndex = 0;
+    aiString texPath;   //contains filename of texture
+
+    if(AI_SUCCESS == mtl->GetTexture(aiTextureType_DIFFUSE, texIndex, &texPath))
+    {
+    //bind texture
+    GLuint texId = textureIdMap.at(texPath.data);
+    if (texId != 99999) glBindTexture(GL_TEXTURE_2D, texId);
+    }
+
+    set_float4(c, 0.8f, 0.8f, 0.8f, 1.0f);
+    if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_DIFFUSE, &diffuse))
+    color4_to_float4(&diffuse, c);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, c);
+
+    set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
+    if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_SPECULAR, &specular))
+    color4_to_float4(&specular, c);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, c);
+
+    set_float4(c, 0.2f, 0.2f, 0.2f, 1.0f);
+    if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_AMBIENT, &ambient))
+    color4_to_float4(&ambient, c);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, c);
+
+    set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
+    if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_EMISSIVE, &emission))
+    color4_to_float4(&emission, c);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, c);
+
+    max = 1;
+    ret1 = aiGetMaterialFloatArray(mtl, AI_MATKEY_SHININESS, &shininess, &max);
+    max = 1;
+    ret2 = aiGetMaterialFloatArray(mtl, AI_MATKEY_SHININESS_STRENGTH, &strength, &max);
+    if((ret1 == AI_SUCCESS) && (ret2 == AI_SUCCESS))
+    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess * strength);
+    else {
+    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0.0f);
+    set_float4(c, 0.0f, 0.0f, 0.0f, 0.0f);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, c);
+    }
+
+    max = 1;
+    if(AI_SUCCESS == aiGetMaterialIntegerArray(mtl, AI_MATKEY_ENABLE_WIREFRAME, &wireframe, &max))
+    fill_mode = wireframe ? GL_LINE : GL_FILL;
+    else
+    fill_mode = GL_FILL;
+    glPolygonMode(GL_FRONT_AND_BACK, fill_mode);
+
+    max = 1;
+    if((AI_SUCCESS == aiGetMaterialIntegerArray(mtl, AI_MATKEY_TWOSIDED, &two_sided, &max)) && two_sided)
+    glEnable(GL_CULL_FACE);
+    else
+    glDisable(GL_CULL_FACE);
+}
+
+
+void recursive_render (const struct aiScene *sc, const struct aiNode* nd, float scale)
+{
+
+    unsigned int i;
+    unsigned int n=0, t;
+    aiMatrix4x4 m = nd->mTransformation;
+
+    aiMatrix4x4 m2;
+    aiMatrix4x4::Scaling(aiVector3D(scale, scale, scale), m2);
+    m = m * m2;
+
+    // update transform
+    m.Transpose();
+    glPushMatrix();
+    glMultMatrixf((float*)&m);
+
+    // draw all meshes assigned to this node
+    for (; n < nd->mNumMeshes; ++n)
+    {
+    const struct aiMesh* mesh = sc->mMeshes[nd->mMeshes[n]];
+
+    apply_material(sc->mMaterials[mesh->mMaterialIndex]);
+
+
+    if(mesh->mNormals == NULL)
+    {
+    glDisable(GL_LIGHTING);
+    }
+    else
+    {
+    glEnable(GL_LIGHTING);
+    }
+
+    if(mesh->mColors[0] != NULL)
+    {
+    glEnable(GL_COLOR_MATERIAL);
+    }
+    else
+    {
+    glDisable(GL_COLOR_MATERIAL);
+    }
+
+
+
+    for (t = 0; t < mesh->mNumFaces; ++t) {
+    const struct aiFace* face = &mesh->mFaces[t];
+    GLenum face_mode;
+
+    switch(face->mNumIndices)
+    {
+    case 1: face_mode = GL_POINTS; break;
+    case 2: face_mode = GL_LINES; break;
+    case 3: face_mode = GL_TRIANGLES; break;
+    default: face_mode = GL_POLYGON; break;
+    }
+
+    glBegin(face_mode);
+
+    for(i = 0; i < face->mNumIndices; i++)  // go through all vertices in face
+    {
+    int vertexIndex = face->mIndices[i];    // get group index for current index
+    if(mesh->mColors[0] != NULL)
+    Color4f(&mesh->mColors[0][vertexIndex]);
+    if(mesh->mNormals != NULL)
+
+    if(mesh->HasTextureCoords(0))   //HasTextureCoords(texture_coordinates_set)
+    {
+    glTexCoord2f(mesh->mTextureCoords[0][vertexIndex].x, 1 - mesh->mTextureCoords[0][vertexIndex].y); //mTextureCoords[channel][vertex]
+    }
+
+    glNormal3fv(&mesh->mNormals[vertexIndex].x);
+    glVertex3fv(&mesh->mVertices[vertexIndex].x);
+    }
+
+    glEnd();
+
+    }
+
+    }
+
+
+    // draw all children
+    for (n = 0; n < nd->mNumChildren; ++n)
+    {
+    recursive_render(sc, nd->mChildren[n], scale);
+    }
+
+    glPopMatrix();
+}
+float jormymillis = 0.0;
+float startti = 0;
+float startti2 = 0;
+float pantime = 0;
+void BiloThreeScene()
+{
+    float mymillis = (millis-scene_start_millis);
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fake_framebuffer); // default
+    glUseProgram(0);
+
+    float colmy = mymillis*0.0001;
+    if (colmy > 0.5) colmy = 0.5;
+
+    if (millis > 302800) colmy-=((millis-302800)*0.00001);
+
+    glClearColor(colmy, 0.0f, 0.0f, 0.0f);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    glDisable(GL_TEXTURE_2D);
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_DST_COLOR);
+
+    glShadeModel(GL_SMOOTH);    // Enables Smooth Shading
+    glEnable(GL_DEPTH_TEST);    // Enables Depth Testing
+    glDepthFunc(GL_LEQUAL); // The Type Of Depth Test To Do
+    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);  // Really Nice Perspective Calculation
+
+    glDisable(GL_LIGHTING);
+    glEnable(GL_LIGHT0); // Uses default lighting parameters
+    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
+    glEnable(GL_NORMALIZE);
+
+    float zoom = -300.0f+(((mymillis-jormymillis)*atan(mymillis*0.005))*0.05);
+
+    if (zoom > -0.5 && startti == 0) { startti = mymillis; }
+    if (zoom >= -0.5) { zoom = -0.5; jormymillis+=290;}
+
+    glLoadIdentity();
+
+    if (jormymillis > 300*60 && startti2 == 0) 
+    {
+        startti2 = mymillis;
+    }
+
+    if (jormymillis > 300*60)
+    {
+        pantime = mymillis-startti2;
+    }
+
+    glTranslatef(0.0f, startti2 > 0 ? -7.5f-pantime*0.0008 : -7.5f, zoom+pantime*0.001*atan(pantime*0.005));
+    if (jormymillis > 0) {
+        glRotatef(jormymillis*0.0026,-1.0,0.0,0.0);
+    }
+
+    float zoomfactor = 2.0+jormymillis*0.0001;
+    if (zoomfactor > 4.0) zoomfactor = 4.0;
+
+    if (startti > 0) recursive_render(bilothree, bilothree->mRootNode, zoomfactor-0.5);
+    recursive_render(bilothree, bilothree->mRootNode, zoomfactor);
+    recursive_render(bilothree, bilothree->mRootNode, 6.0-zoomfactor);
+
+}
+
+
+
+
+void KolmeDeeLogic(float dt)
+{
+    kujalla_angle += dt*0.01;
+}
+
+void kapsule_render()
+{
+
+    glEnable(GL_BLEND);
+    glUseProgram(0);
+    glEnable(GL_TEXTURE_2D);
+    glShadeModel(GL_SMOOTH);    // Enables Smooth Shading
+    glEnable(GL_DEPTH_TEST);    // Enables Depth Testing
+    glDepthFunc(GL_LEQUAL); // The Type Of Depth Test To Do
+    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);  // Really Nice Perspective Calculation
+
+    glEnable(GL_LIGHTING);
+    glEnable(GL_LIGHT0); // Uses default lighting parameters
+    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
+    glEnable(GL_NORMALIZE);
+
+    GLfloat LightAmbient[]= { 0.5f, 0.5f, 0.5f, 1.0f };
+    GLfloat LightDiffuse[]= { 1.0f, 1.0f, 1.0f, 1.0f };
+    GLfloat LightPosition[]= { 0.0f, 0.0f, 15.0f, 1.0f };
+
+    glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);
+    glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);
+    glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);
+    glEnable(GL_LIGHT1);
+
+    glLoadIdentity();
+
+    glTranslatef(0.0f, 0.0f, -50.0f+cos(millis*0.05)*25);   // Move 40 Units And Into The Screen
+    glRotatef(millis*0.01,1.0,0,0);
+    glRotatef(millis*0.008,0.0,0.0,1.0);
+    glRotatef(millis*0.006,0.0,1.0,0.0);
+
+    recursive_render(kapsule, kapsule->mRootNode, 2.5);
+    glDisable(GL_DEPTH_TEST);
+    glDisable(GL_LIGHTING);
+    glDisable(GL_NORMALIZE);
+}
+
+float brimillis = 0.0;
+float bristart = 0.0;
+float brixrot = 0.0;
+float briyrot = 0.0;
+
+void brieflycase_render()
+{
+    if (bristart == 0) {bristart = millis;}
+    brimillis = millis-bristart;
+
+    glEnable(GL_BLEND);
+    glUseProgram(0);
+    glEnable(GL_TEXTURE_2D);
+    glShadeModel(GL_SMOOTH);    // Enables Smooth Shading
+    glEnable(GL_DEPTH_TEST);    // Enables Depth Testing
+    glDepthFunc(GL_LEQUAL); // The Type Of Depth Test To Do
+    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);  // Really Nice Perspective Calculation
+
+    glEnable(GL_LIGHTING);
+    glEnable(GL_LIGHT0); // Uses default lighting parameters
+    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
+    glEnable(GL_NORMALIZE);
+
+    float brifade = 1.0f;
+
+    if (brimillis <= 1000) {
+        brifade = brimillis*0.001;
+        if (brifade > 1.0f) brifade = 1.0f;
+    }
+    GLfloat LightAmbient[]= { 0.5f*brifade, 0.5f*brifade, 0.5f*brifade, 1.0f*brifade };
+    GLfloat LightDiffuse[]= { 1.0f*brifade, 1.0f*brifade, 1.0f*brifade, 1.0f*brifade };
+    GLfloat LightPosition[]= { 0.0f, 0.0f, 15.0f*brifade, 1.0f*brifade };
+
+    glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);
+    glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);
+    glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);
+    glEnable(GL_LIGHT1);
+
+    glLoadIdentity();
+
+    //nosto
+    if (brimillis > 1000 && brimillis < 3000)
+    {
+        briyrot=(brimillis-1000)*0.047*(1.0-(cos(brimillis*0.001)));
+        if (briyrot > 90.0f) briyrot = 90.0f;
+    }
+
+    if (brimillis > 5000)
+    {
+        brixrot=(brimillis-5000)*0.047*2* 1.0-(cos((brimillis-4000)*0.0001));
+//        if (brixrot > 180.0f) brixrot = 180.0f;
+    }
+
+    glTranslatef(0.0f, 0.0f, -50.0f+brixrot*0.2);   // Move 40 Units And Into The Screen
+    //glRotatef(millis*0.001,1.0,0,0);
+    glRotatef(-briyrot,1.0,0.0,0.0);
+    glRotatef(brixrot,0.0,0.0,1.0);
+
+    recursive_render(brieflycase, brieflycase->mRootNode, 2.5);
+    glDisable(GL_DEPTH_TEST);
+    glDisable(GL_LIGHTING);
+    glDisable(GL_NORMALIZE);
+}
+
+
+void
+on_key_press ( unsigned char key)
+{
+    if (key == 1)
+    {
+        console_process( console, "home", 0 );
+    }
+    else if (key == 4)
+    { 
+        console_process( console, "delete", 0 );
+    }
+    else if (key == 5)
+    { 
+        console_process( console, "end", 0 );
+    }
+    else if (key == 8)
+    { 
+        console_process( console, "backspace", 0 );
+    }
+    else if (key == 9)
+    {
+        console_process( console, "complete", 0 );
+    }
+    else if (key == 11)
+    {
+        console_process( console, "kill", 0 );
+    }
+    else if (key == 12)
+    {
+        console_process( console, "clear", 0 );
+    }
+    else if (key == 13)
+    {
+        console_process( console, "enter", 0 );
+    }
+    else if (key == 25)
+    {
+        console_process( console, "yank", 0 );
+    }
+    else if (key == 27)
+    {
+        console_process( console, "escape", 0 );
+    }
+    else if (key == 127)
+    {
+        console_process( console, "backspace", 0 );
+    }
+    else if( key > 31)
+    {
+        console_process( console, "type", key );
+    }
+}
+
+/*void LoaderLogic(float dt)
+{
+    printf("DEBUG: LoaderLogic(%f)\n", dt);
+}*/
+
+int keyindex = 0;
+int nextmillis = 0;
+void ConsoleLogic(float dt)
+{
+//  int kmillis = (int)(millis-15750);
+    int kmillis = (int)(millis-scene_start_millis);
+
+    //printf("kmillis:%d\n",kmillis);
+    if (kmillis >= 0 && kmillis >= keymillis[keyindex])
+    {
+        if(keyindex >= 0 && keyindex < KEYEVENTS_COUNT)
+        {
+            on_key_press(keyrec[keyindex]);
+        }
+
+        keyindex++;
+    }
+}
+
+int keyindex2 = 0;
+
+void ConsoleLogic2(float dt)
+{
+    int kmillis = -2000+(int)((millis-scene_start_millis)*1.3);
+
+    //printf("kmillis:%d\n",kmillis);
+    if (kmillis >= 0 && kmillis >= keymillis2[keyindex2])
+    {
+        if(keyindex2 >= 0 && keyindex2 < KEYEVENTS_COUNT2)
+        {
+            on_key_press(keyrec2[keyindex2]);
+        }
+
+        keyindex2++;
+    }
+}
+
+int kolmedeeindex = 0;
+
+const aiScene* Import3DFromFile(const std::string& pFile)
+{
+    fprintf(stdout," - Import3DFromFile(\"%s\")", pFile.c_str());
+
+    //check if file exists
+    std::ifstream fin(pFile.c_str());
+    if(!fin.fail())
+    {
+        fin.close();
+    }
+    else
+    {
+        printf(" could not open file %s\n", pFile.c_str());
+        exit(1);
+    }
+
+    const aiScene* scener = importer[kolmedeeindex].ReadFile( pFile, aiProcessPreset_TargetRealtime_Quality);
+
+    // If the import failed, report it
+    if( !scener)
+    {
+        printf(" import failed %s\n", pFile.c_str());
+        exit(1);
+    }
+
+    kolmedeeindex++;
+
+    fprintf(stdout," success\n");
+
+    // We're done. Everything will be cleaned up by the importer destructor
+    return scener;
+}
+
+int shader_index = 1; // 1 not 0 because console shader is loaded separately
+bool LoadShaders() 
+{
+    if(shader_index == sizeof(shaders) / sizeof(shaders[0])) return false;
+    shaders[shader_index] = LoadShader(shaderss[shader_index]);
+    shader_index++;
+
+    return true;
+}
+int texture_index = 0;
+bool LoadTextures()
+{
+    if(texture_index == sizeof(textures) / sizeof(textures[0])) return false;
+    textures[texture_index] = LoadTexture(texturess[texture_index]);
+    texture_index++;
+
+    return true;
+}
+int assets_3dmodel_total = 1;
+bool Load3DAssets()
+{
+    kapsule = Import3DFromFile("data/models/kapsule.obj");
+    LoadGLTextures(kapsule);
+
+    return true;
+}
+
+bool assets_loaded = false;
+int skip_frames = 10;
+int skip_frames_count = 0;
+clock_t t_loader_begin = 0, t_loader_d;
+int assets_index = -1, assets_total = -1;
+int loader_phase = -1;
+float loading_time = 0;
+void Loader()
+{
+    if(t_loader_begin == 0) { current_scene = 0; t_loader_begin = clock(); assets_total = ((sizeof(shaders) / sizeof(shaders[0])) + (sizeof(textures) / sizeof(textures[0])) + assets_3dmodel_total ); } else { loading_time = (float)((((float)t_loader_d - (float)t_loader_begin) / 1000000.0F ) * 1000); }
+    if(assets_total == -1) {
+        printf("ERROR: Loader(): No assets to load and/or something is just terribly wrong! Terminating...\n");
+        exit(1);
+    }
+
+    float phase = (float)((float)(assets_index) / (float)(assets_total));
+    const aiScene* loaderscene = bilothorn;
+
+    //glClearColor (phase,phase,phase,1.0);
+    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    float mymillis = phase*1750;
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb); // default
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    glUseProgram(0);
+
+    glDisable(GL_TEXTURE_2D);
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);
+
+    glShadeModel(GL_SMOOTH);    // Enables Smooth Shading
+    //glClearColor(1.0f-phase,1.0f-phase,1.0f-phase, 1.0f);
+    glClearDepth(1.0f); // Depth Buffer Setup
+    glEnable(GL_DEPTH_TEST);    // Enables Depth Testing
+    glDepthFunc(GL_LEQUAL); // The Type Of Depth Test To Do
+    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);  // Really Nice Perspective Calculation
+
+    glDisable(GL_LIGHTING);
+    glEnable(GL_LIGHT0); // Uses default lighting parameters
+    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
+    glDisable(GL_NORMALIZE);
+
+    float r, g, b, a = 1.0f;
+    if(loader_phase < 1) {
+        r = g = b = sin(loading_time*100);
+    } else {
+        r = g = b = a;
+    }
+    GLfloat LightAmbient[]= { r, g, b, a };
+    GLfloat LightDiffuse[]= { r, g, b, a };
+    GLfloat LightPosition[]= { 0.0f, 0.0f, 15.0f, 1.0f};
+    //GLfloat LightAmbient[]= { 0.0f, 1.0f-phase, 0.0f, 1.0f };
+   // GLfloat LightDiffuse[]= { 0.0f, 1.0f-phase, 0.0f, 1.0f };
+  //  GLfloat LightPosition[]= { 0.0f, 0.0f, 15.0f, 1.0f };
+
+    glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);
+    glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);
+    glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);
+    glEnable(GL_LIGHT1);
+
+    float zoom = -50.0f+((mymillis)*0.05*0.2)*((phase+0.5f)*(phase+0.5f)*(phase+0.5f));
+
+    if (zoom > -0.5 && startti == 0) { startti = mymillis; }
+    if (zoom >= -0.5) { zoom = -0.5; jormymillis=(mymillis-startti); }
+
+
+    if (jormymillis > 0) {
+        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    }
+
+    glLoadIdentity();
+
+    glRotatef(180.0f*phase*0.974,0.0,0.0,1.0);
+    glTranslatef(0.0f, 0.0f, zoom);
+    /*if (jormymillis > 0) {
+        glRotatef(jormymillis*0.0026,-1.0,0.0,0.0);
+    }*/
+
+    skip_frames_count++;
+    if(skip_frames_count == skip_frames) {
+        skip_frames_count = 0;
+        assets_index++;
+        printf("--- MIDISYS-ENGINE: Loading asset #%i", assets_index);
+
+        // begin loading animation
+
+        if(loader_phase == -1) {
+            loader_phase = 0;
+        } else if(loader_phase == 0) {
+            if(!LoadShaders()) {
+                loader_phase = 1;
+            } else {
+            }
+        } else if(loader_phase == 1) {
+            if(!LoadTextures()) {
+                loader_phase = 2;
+            } else {
+            }
+        } else if(loader_phase == 2) {
+            printf("..%i", assets_total);
+            // load all 3d models; after that all asset loading is done
+            Load3DAssets();
+            loader_phase = 3;
+        } else {
+            assets_loaded = true;
+        }
+    } else {
+        // format bilotrip terminal 1.6.2.0
+
+        /*if(phase > 0.975f) {
+            phase = 1.0f-phase;
+        }*/
+        glClearColor ((1.0f-phase)*0.86,(1.0f-phase)*0.86,(1.0f-phase)*0.86,1.0);
+        glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+        glFlush();
+        glutSwapBuffers();
+    }
+
+    //printf("n:%i\n",(int)((float)((float)(assets_index) / (float)(assets_total)) * 3.0f));
+    /*glBegin(GL_TRIANGLES);
+        glVertex3f(-25.0f,-25.0f,-50.0f);
+        glVertex3f(-25.0f,25.0f,-50.0f);
+        glVertex3f(25.0f,25.0f,-50.0f);
+    glEnd();*/
+    //glTranslatef(0.0,floor(phase+0.25f),0.0);
+    for(int n = 0; n < (int)(phase*6.9f); n++) {
+        if(n<3) {
+            int t = floor(n/3);
+            float tf = (float)(t)*120.0f;
+            if(n!=0&&n%3==0) glTranslatef(0.0,0.0,tf);
+            glRotatef(120.0f,0.0,0.0,((float)(n)*phase));
+            recursive_render(loaderscene, loaderscene->mRootNode, 2.0+jormymillis*0.001);
+        }
+        //if (jormymillis > 0)recursive_render(loaderscene, loaderscene->mRootNode, 4.0-jormymillis*0.001);     
+    }
+
+
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fake_framebuffer); // default
+    glDisable(GL_BLEND);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+
+    glUseProgram(shaders[hex]);
+    float widthLoc5 = glGetUniformLocation(shaders[hex], "width");
+    float heightLoc5 = glGetUniformLocation(shaders[hex], "height");
+    float timeLoc5 = glGetUniformLocation(shaders[hex], "time");
+    float effuLoc5 = glGetUniformLocation(shaders[hex], "effu");
+
+    glUniform1f(widthLoc5, g_Width);
+    glUniform1f(heightLoc5, g_Height);
+    glUniform1f(timeLoc5, mymillis/100);
+    glUniform1f(effuLoc5, jormymillis > 0 ? 1.0 : 0.0);
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, fb_tex);
+
+    float location5 = glGetUniformLocation(shaders[hex], "texture0");
+    glUniform1i(location5, 0);
+
+    glLoadIdentity();
+
+    glTranslatef(-1.2, -1.0, -1.0);
+    //glTranslatef(0.0, 0.0, -1.0);
+
+    int i=0;
+    int j=0;
+    glBegin(GL_QUADS);
+    glVertex2f(i, j);
+    glVertex2f(i + 100, j);
+    glVertex2f(i + 100, j + 100);
+    glVertex2f(i, j + 100);
+    glEnd();
+
+    glFlush();
+    glutSwapBuffers();
+
+    glDisable(GL_DEPTH_TEST);    // Enables Depth Testing
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb); // default
+    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+    glDepthFunc(GL_LEQUAL); // The Type Of Depth Test To Do
+    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);  // Really Nice Perspective Calculation
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // default
+    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+
+    if (quitflag == 0) glutPostRedisplay();
+}
+
+int vieterframe = 0;
+float vieterstart = 0;
+
+void LeadMaskScene()
+{
+glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb); // fbo
+glClear(GL_DEPTH_BUFFER_BIT);
+float mymillis = millis;
+glUseProgram(shaders[projector]);
+
+int kuvaflag = 0;
+
+if (millis >= 0 && millis < 25000) {
+kuvaflag = 0;
+}
+else if (millis >= 25000 && millis < 37000) {
+kuvaflag = 1;
+}
+else if (millis >= 37000 && millis < 48500) {
+kuvaflag = 2;
+}
+else if (millis >= 48500 && millis < 64000) {
+kuvaflag = 3;
+}
+else if (millis >= 64000 && millis < 76100) {
+kuvaflag = 4;
+}
+else if (millis >= 76100 && millis < 84000) {
+kuvaflag = 5;
+}
+else if (millis >= 84000) {
+kuvaflag = 6;
+if (vieterstart == 0) vieterstart = mymillis;
+vieterframe = (int)((mymillis-vieterstart)*0.004);
+if (vieterframe > 12) { vieterframe = 0; vieterstart = 0; }
+}
+
+glEnable(GL_BLEND);
+glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA);
+
+GLint widthLoc5 = glGetUniformLocation(shaders[projector], "width");
+GLint heightLoc5 = glGetUniformLocation(shaders[projector], "height");
+GLint timeLoc5 = glGetUniformLocation(shaders[projector], "time");
+GLint alphaLoc5 = glGetUniformLocation(shaders[projector], "alpha");
+
+glUniform1f(widthLoc5, g_Width);
+glUniform1f(heightLoc5, g_Height);
+glUniform1f(timeLoc5, mymillis - (millis < 64000 ? 0 : 50000));
+glUniform1f(alphaLoc5, mymillis*0.0001+0.2-cos(mymillis*0.0005)*0.15);
+
+glActiveTexture(GL_TEXTURE0);
+if (kuvaflag == 0)
+glBindTexture(GL_TEXTURE_2D, textures[tex_scene]);
+else if (kuvaflag == 1)
+glBindTexture(GL_TEXTURE_2D, textures[tex_dude]);
+else if (kuvaflag == 2)
+glBindTexture(GL_TEXTURE_2D, textures[tex_dude2]);
+else if (kuvaflag == 3)
+glBindTexture(GL_TEXTURE_2D, textures[tex_mask]);
+else if (kuvaflag == 4)
+glBindTexture(GL_TEXTURE_2D, textures[tex_note]);
+else if (kuvaflag == 5)
+glBindTexture(GL_TEXTURE_2D, textures[tex_exit]);
+else if (kuvaflag == 6)
+glBindTexture(GL_TEXTURE_2D, textures[tex_v0+vieterframe]);
+
+GLint location5 = glGetUniformLocation(shaders[projector], "texture0");
+glUniform1i(location5, 0);
+
+glLoadIdentity();
+glTranslatef(-1.2, -1.0, -1.0);
+
+glBegin(GL_QUADS);
+
+int i,j;
+
+for (i = -50; i < 50; i+=10)
+for (j = -50; j < 50; j+=10)
+{
+glVertex2f(i, j);
+glVertex2f(i + 1, j);
+glVertex2f(i + 1, j + 1);
+glVertex2f(i, j + 1);
+}
+glEnd();
+
+glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb2); // default
+
+glUseProgram(shaders[fsquad]);
+
+glEnable(GL_BLEND);
+glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+glBlendFunc(GL_ONE_MINUS_SRC_COLOR, GL_ONE_MINUS_DST_COLOR);
+
+GLint widthLoc6 = glGetUniformLocation(shaders[fsquad], "width");
+GLint heightLoc6 = glGetUniformLocation(shaders[fsquad], "height");
+GLint timeLoc6 = glGetUniformLocation(shaders[fsquad], "time");
+GLint alphaLoc6 = glGetUniformLocation(shaders[fsquad], "alpha");
+GLint gammaLoc = glGetUniformLocation(shaders[fsquad], "gamma");
+GLint gridLoc6 = glGetUniformLocation(shaders[fsquad], "grid");
+GLint alphamodeLoc5 = glGetUniformLocation(shaders[fsquad], "alphamode");
+glUniform1f(alphamodeLoc5, 0.0f);
+
+glUniform1f(gridLoc6, 0.001+cos(mymillis)*0.0005);
+
+
+glUniform1f(widthLoc6, g_Width);
+glUniform1f(heightLoc6, g_Height);
+glUniform1f(timeLoc6, mymillis/100);
+glUniform1f(alphaLoc6, 0.1+abs(cos(mymillis*0.08)*0.05));
+glUniform1f(gammaLoc, 0.0f);
+
+glActiveTexture(GL_TEXTURE0);
+glBindTexture(GL_TEXTURE_2D, fb_tex);
+
+GLint location6 = glGetUniformLocation(shaders[fsquad], "texture0");
+glUniform1i(location6, 0);
+
+glLoadIdentity();
+
+glTranslatef(-1.2, -1.0, -1.0);
+
+i=0;
+j=0;
+glBegin(GL_QUADS);
+glVertex2f(i, j);
+glVertex2f(i + 100, j);
+glVertex2f(i + 100, j + 100);
+glVertex2f(i, j + 100);
+glEnd();
+
+glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fake_framebuffer); // default
+
+glUseProgram(shaders[fsquad]);
+
+glDisable(GL_BLEND);
+
+glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+GLint widthLoc7 = glGetUniformLocation(shaders[fsquad], "width");
+GLint heightLoc7 = glGetUniformLocation(shaders[fsquad], "height");
+GLint timeLoc7 = glGetUniformLocation(shaders[fsquad], "time");
+GLint alphaLoc7 = glGetUniformLocation(shaders[fsquad], "alpha");
+GLint gammaLoc2 = glGetUniformLocation(shaders[fsquad], "gamma");
+GLint gridLoc = glGetUniformLocation(shaders[fsquad], "grid");
+GLint alphamodeLoc7 = glGetUniformLocation(shaders[fsquad], "alphamode");
+glUniform1f(alphamodeLoc7, 0.0f);
+
+glUniform1f(widthLoc7, g_Width);
+glUniform1f(heightLoc7, g_Height);
+glUniform1f(timeLoc7, mymillis/100);
+glUniform1f(alphaLoc7, 1.0);
+glUniform1f(gammaLoc2, 4.0f);
+glUniform1f(gridLoc, 1.0f+tan(mymillis*10)*0.3);
+
+glActiveTexture(GL_TEXTURE0);
+glBindTexture(GL_TEXTURE_2D, fb_tex2);
+
+GLint location7 = glGetUniformLocation(shaders[fsquad], "texture0");
+glUniform1i(location7, 0);
+
+glLoadIdentity();
+
+glTranslatef(-1.2, -1.0, -1.0);
+
+i=0;
+j=0;
+glBegin(GL_QUADS);
+glVertex2f(i, j);
+glVertex2f(i + 100, j);
+glVertex2f(i + 100, j + 100);
+glVertex2f(i, j + 100);
+glEnd();
+
+glUseProgram(shaders[projector]);
+
+glEnable(GL_BLEND);
+glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
+glBlendFunc(GL_SRC_COLOR, GL_DST_ALPHA);
+
+widthLoc5 = glGetUniformLocation(shaders[projector], "width");
+heightLoc5 = glGetUniformLocation(shaders[projector], "height");
+timeLoc5 = glGetUniformLocation(shaders[projector], "time");
+alphaLoc5 = glGetUniformLocation(shaders[projector], "alpha");
+
+glUniform1f(widthLoc5, g_Width);
+glUniform1f(heightLoc5, g_Height);
+glUniform1f(timeLoc5, mymillis);
+glUniform1f(alphaLoc5, 1.0);
+
+glActiveTexture(GL_TEXTURE0);
+glBindTexture(GL_TEXTURE_2D, textures[tex_scene]);
+
+location5 = glGetUniformLocation(shaders[projector], "texture0");
+glUniform1i(location5, 0);
+
+glLoadIdentity();
+glTranslatef(-1.2, -1.0, -1.0);
+
+glBegin(GL_QUADS);
+
+
+for (i = -50; i < 50; i+=10)
+for (j = -50; j < 50; j+=10)
+{
+glVertex2f(i, j);
+glVertex2f(i + 1, j);
+glVertex2f(i + 1, j + 1);
+glVertex2f(i, j + 1);
+}
+glEnd();
+
+
+if (millis > 37400 && millis < 37600) kapsule_render();
+
+
+}
+
+int copbeatcounter = -1;
+int coptexid = 0;
+void CopScene()
+{
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fake_framebuffer); // default
+
+    float mymillis = (millis-scene_start_millis);
+    glUseProgram(shaders[copquad]);
+
+    glEnable(GL_BLEND);
+
+
+    float joo = cos(mymillis);
+    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, joo < 0.5 ? GL_MODULATE : GL_SUBTRACT );
+    glBlendFunc(GL_SRC_COLOR, joo < 0.5 ? GL_DST_ALPHA : GL_ONE_MINUS_DST_COLOR);
+
+    GLint widthLoc5 = glGetUniformLocation(shaders[copquad], "width");
+    GLint heightLoc5 = glGetUniformLocation(shaders[copquad], "height");
+    GLint timeLoc5 = glGetUniformLocation(shaders[copquad], "time");
+    GLint alphaLoc5 = glGetUniformLocation(shaders[copquad], "alpha");
+    GLint gridLoc6 = glGetUniformLocation(shaders[copquad], "grid");
+    glUniform1f(gridLoc6, tan(mymillis));
+
+    glUniform1f(widthLoc5, g_Width);
+    glUniform1f(heightLoc5, g_Height);
+    glUniform1f(timeLoc5, mymillis/100);
+    glUniform1f(alphaLoc5, cos(mymillis*0.1)*0.01);
+
+    if (scene_shader_params[2] == 36) { copbeatcounter++; }
+    if (copbeatcounter > 0) { coptexid++; copbeatcounter = 0;}
+
+    //int texind = (int)(mymillis*(0.001/2));
+    int texind = coptexid;
+    if (texind > 30) texind = 30;
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, textures[tex_copkiller + texind]);
+
+    glActiveTexture(GL_TEXTURE1);
+    glBindTexture(GL_TEXTURE_2D, textures[texind == tex_copkiller ? tex_copkiller+17 :
+              tex_copkiller + (abs(texind-1))]);
+
+    GLint location5 = glGetUniformLocation(shaders[copquad], "texture0");
+    glUniform1i(location5, 0);
+
+    GLint location6 = glGetUniformLocation(shaders[copquad], "texture1");
+    glUniform1i(location6, 1);
+
+    glLoadIdentity();
+
+    glTranslatef(-1.2, -1.0, -1.0);
+
+    int i=0;
+    int j=0;
+    glBegin(GL_QUADS);
+    glVertex2f(i, j);
+    glVertex2f(i + 100, j);
+    glVertex2f(i + 100, j + 100);
+    glVertex2f(i, j + 100);
+    glEnd();
+
+}
+
+static int video_started = 0;
+
+void MarssiScene()
+{
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fake_framebuffer); // default
+    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+    glDisable(GL_BLEND);
+
+    if (video_started == 0)
+    {
+        myVideoFrame->play();
+        video_started = 1;
+    }
+
+    myVideoFrame->render();
+}
+
+void LongScene()
+{
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fake_framebuffer); // default
+    glDisable(GL_BLEND);
+    glClearColor(0,0,0,0);
+    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+
+    glUseProgram(shaders[fsquad]);
+    float mymillis = (((millis)-scene_start_millis));
+
+    GLint widthLoc5 = glGetUniformLocation(shaders[fsquad], "width");
+    GLint heightLoc5 = glGetUniformLocation(shaders[fsquad], "height");
+    GLint timeLoc5 = glGetUniformLocation(shaders[fsquad], "time");
+    GLint alphaLoc5 = glGetUniformLocation(shaders[fsquad], "alpha");
+    GLint gammaLoc = glGetUniformLocation(shaders[fsquad], "gamma");
+    GLint gridLoc6 = glGetUniformLocation(shaders[fsquad], "grid");
+    glUniform1f(gridLoc6, 0.0f);
+    glUniform1f(gammaLoc, 0.0f);
+    GLint alphamodeLoc5 = glGetUniformLocation(shaders[fsquad], "alphamode");
+    glUniform1f(alphamodeLoc5, 0.0f);
+
+    glUniform1f(widthLoc5, g_Width);
+    glUniform1f(heightLoc5, g_Height);
+    glUniform1f(timeLoc5, mymillis/100);
+    glUniform1f(alphaLoc5, 0.0);
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, textures[tex_aegis]);
+
+    GLint location5 = glGetUniformLocation(shaders[fsquad], "texture0");
+    glUniform1i(location5, 0);
+
+    glLoadIdentity();
+
+    glTranslatef(-1.2, -1.0, -1.0);
+
+    int i=0;
+    int j=0;
+    glBegin(GL_QUADS);
+    glVertex2f(i, j);
+    glVertex2f(i + 100, j);
+    glVertex2f(i + 100, j + 100);
+    glVertex2f(i, j + 100);
+    glEnd();
+
+//
+    if (mymillis > 2500) {
+        int plussati = 0;
+        if (mymillis > 2800)plussati=400;
+        glEnable(GL_BLEND);
+        glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+        int lloff = (int)(((plussati+mymillis)-2500)*0.0025);
+        if (lloff >= 4) lloff = 4;
+        widthLoc5 = glGetUniformLocation(shaders[fsquad], "width");
+        heightLoc5 = glGetUniformLocation(shaders[fsquad], "height");
+        timeLoc5 = glGetUniformLocation(shaders[fsquad], "time");
+        alphaLoc5 = glGetUniformLocation(shaders[fsquad], "alpha");
+        float alphamodeLoc = glGetUniformLocation(shaders[fsquad], "alphamode");
+
+        glUniform1f(widthLoc5, g_Width);
+        glUniform1f(heightLoc5, g_Height);
+        glUniform1f(timeLoc5, mymillis/100);
+        glUniform1f(alphaLoc5, 1.0);
+        glUniform1f(alphamodeLoc, 1.0);
+
+        glActiveTexture(GL_TEXTURE0);
+        glBindTexture(GL_TEXTURE_2D, textures[tex_ll1+lloff]);
+
+        GLint location5 = glGetUniformLocation(shaders[fsquad], "texture0");
+        glUniform1i(location5, 0);
+
+        glLoadIdentity();
+
+        glTranslatef(-1.2, -1.0, -1.0);
+
+        int i=0;
+        int j=0;
+        glBegin(GL_QUADS);
+        glVertex2f(i, j);
+        glVertex2f(i + 100, j);
+        glVertex2f(i + 100, j + 100);
+        glVertex2f(i, j + 100);
+        glEnd();
+
+    }
+
+}
+
+int majic_texnum = 0;
+int noclearframes = 0;
+void EyeScene()
+{
+    int i, j;
+
+    int lisuri = 0;
+
+    //printf("millis:%f\n",millis);
+    if (millis > 225200) lisuri = 50000+(millis-225200)*0.5;
+    if (lisuri > 150000) lisuri = 150000;
+
+    float mymillis = (((millis)-scene_start_millis)*100);
+    float mymillis2 = (((millis+lisuri)-scene_start_millis)*100);
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb); // fbo
+
+    glClearColor((float)(scene_shader_params[1]/127)*0.7,(float)(scene_shader_params[1]/127)*0.4,(float)(scene_shader_params[1]/127)*0.8,0.9-0.005*(float)(scene_shader_params[1]/127));
+
+    if ((scene_shader_params[0] == 65 && scene_shader_param_type[0] == 0) || noclearframes > 200)
+    {
+        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+        beatmode = -beatmode;
+        noclearframes = 0;
+    }
+    else
+    {
+        noclearframes++;
+    }
+
+    // render fbo copy to fbo
+    glEnable(GL_BLEND);
+    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+    glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_DST_COLOR);
+
+    glUseProgram(shaders[eye_post]);
+
+//  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    GLint widthLoc3 = glGetUniformLocation(shaders[eye_post], "width");
+    GLint heightLoc3 = glGetUniformLocation(shaders[eye_post], "height");
+    GLint timeLoc3 = glGetUniformLocation(shaders[eye_post], "time");
+
+    glUniform1f(widthLoc3, g_Width);
+    glUniform1f(heightLoc3, g_Height);
+    glUniform1f(timeLoc3, mymillis/100);
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, fb_tex);
+
+    GLint location3 = glGetUniformLocation(shaders[eye_post], "texture0");
+    glUniform1i(location3, 0);
+
+    glLoadIdentity();
+
+    glRotatef(45,0.0,0.0,1.0f);
+    glTranslatef(-1.2, -1.0, 0.0+(tan(mymillis*0.00001)*0.1)*(millis-155520)*0.0001);
+
+    i=0;
+    j=0;
+    glBegin(GL_QUADS);
+    glVertex2f(i, j);
+    glVertex2f(i + 100, j);
+    glVertex2f(i + 100, j + 100);
+    glVertex2f(i, j + 100);
+    glEnd();
+
+    // render eye
+
+    glEnable(GL_BLEND);
+    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA);
+
+    glUseProgram(shaders[eye]);
+
+    GLfloat waveTime = 1+atan(mymillis*0.0001)*0.1,
+            waveWidth = cos(mymillis*0.000001)*1000+atan(mymillis*0.00001)*2.0,
+            waveHeight = sin(mymillis*0.0001)*100*atan(mymillis*0.00001)*2.0;
+
+    GLint waveTimeLoc = glGetUniformLocation(shaders[eye], "waveTime");
+    GLint waveWidthLoc = glGetUniformLocation(shaders[eye], "waveWidth");
+    GLint waveHeightLoc = glGetUniformLocation(shaders[eye], "waveHeight");
+
+    GLint widthLoc = glGetUniformLocation(shaders[eye], "width");
+    GLint heightLoc = glGetUniformLocation(shaders[eye], "height");
+
+    GLint timeLoc = glGetUniformLocation(shaders[eye], "time");
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, textures[tex_grayeye]);
+
+    GLuint location;
+    location = glGetUniformLocation(shaders[eye], "texture0");
+    glUniform1i(location, 0);
+
+    glLoadIdentity();
+
+    glTranslatef(0.0, 0.0, -150.0+sin(mymillis*0.0000004)*120);
+    glRotatef(-75.0, 1.0, 0.0, 0.0);
+    glRotatef(mymillis*0.01, 0.0, 0.0, 1.0);
+
+    glUniform1f(waveTimeLoc, waveTime);
+    glUniform1f(waveWidthLoc, waveWidth);
+    glUniform1f(waveHeightLoc, waveHeight);
+
+    glUniform1f(widthLoc, g_Width);
+    glUniform1f(heightLoc, g_Height);
+
+    glUniform1f(timeLoc, mymillis2/100);
+
+
+    int zoom = 0;
+
+    for (zoom = 0; zoom < 8; zoom++)
+    {
+
+    glTranslatef(-0.01, -0.01, -0.1);
+    glRotatef((90*zoom+mymillis*0.01)*0.1, 1.0, 0.0, 0.0);
+    glRotatef((45*zoom+mymillis*0.01)*0.1, 0.0, 0.0, 1.0);
+    glRotatef((25*zoom+mymillis*0.01)*0.1, 0.0, 1.0, 0.0);
+    glBegin(GL_QUADS);
+
+    for (i = -50; i < 50; i+=10)
+        for (j = -50; j < 50; j+=10)
+        {
+            glVertex2f(i, j);
+            glVertex2f(i + 1, j);
+            glVertex2f(i + 1, j + 1);
+            glVertex2f(i, j + 1);
+        }
+    glEnd();
+    }
+
+    // eye postprocess to screen
+
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fake_framebuffer); // default
+
+    glUseProgram(shaders[eye_post]);
+
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    glDisable(GL_BLEND);
+
+    GLint widthLoc2 = glGetUniformLocation(shaders[eye_post], "width");
+    GLint heightLoc2 = glGetUniformLocation(shaders[eye_post], "height");
+    GLint timeLoc2 = glGetUniformLocation(shaders[eye_post], "time");
+
+    glUniform1f(widthLoc2, g_Width);
+    glUniform1f(heightLoc2, g_Height);
+    glUniform1f(timeLoc2, mymillis/100);
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, fb_tex);
+
+    if (scene_shader_params[0] != -1 && scene_shader_param_type[0] == 0)
+    {
+        room_texnum = (int)(rand() % 3);
+    }
+
+    glActiveTexture(GL_TEXTURE1);
+    glBindTexture(GL_TEXTURE_2D, textures[tex_room + room_texnum]);
+
+    GLint location2 = glGetUniformLocation(shaders[eye_post], "texture0");
+    glUniform1i(location2, 0);
+
+    GLint location4 = glGetUniformLocation(shaders[eye_post], "texture1");
+    glUniform1i(location4, 1);
+
+    glLoadIdentity();
+
+    glTranslatef(-1.2, -1.0, -1.0);
+
+    i=0;
+    j=0;
+    glBegin(GL_QUADS);
+    glVertex2f(i, j);
+    glVertex2f(i + 100, j);
+    glVertex2f(i + 100, j + 100);
+    glVertex2f(i, j + 100);
+    glEnd();
+
+    // text overlay
+
+    if (beatmode == 1)
+    {
+        glUseProgram(shaders[fsquad]);
+
+        glEnable(GL_BLEND);
+        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_SUBTRACT);
+        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_DST_COLOR);
+
+        GLint widthLoc5 = glGetUniformLocation(shaders[fsquad], "width");
+        GLint heightLoc5 = glGetUniformLocation(shaders[fsquad], "height");
+        GLint timeLoc5 = glGetUniformLocation(shaders[fsquad], "time");
+        GLint alphaLoc5 = glGetUniformLocation(shaders[fsquad], "alpha");
+        GLint alphamodeLoc5 = glGetUniformLocation(shaders[fsquad], "alphamode");
+        glUniform1f(alphamodeLoc5, 0.0f);
+
+        glUniform1f(widthLoc5, g_Width);
+        glUniform1f(heightLoc5, g_Height);
+        glUniform1f(timeLoc5, mymillis/100);
+        glUniform1f(alphaLoc5, cos(mymillis*0.1)*0.1);
+
+        glActiveTexture(GL_TEXTURE0);
+        
+        majic_texnum = (int)(rand() % 4);
+
+        glBindTexture(GL_TEXTURE_2D, textures[tex_majestic1+majic_texnum]);
+
+        GLint location5 = glGetUniformLocation(shaders[fsquad], "texture0");
+        glUniform1i(location5, 0);
+
+        glLoadIdentity();
+
+        glTranslatef(-1.2, -1.0, -1.0);
+
+        i=0;
+        j=0;
+        glBegin(GL_QUADS);
+        glVertex2f(i, j);
+        glVertex2f(i + 100, j);
+        glVertex2f(i + 100, j + 100);
+        glVertex2f(i, j + 100);
+        glEnd();
+    }
+
+}
+
+int redcounter = 1;
+
+void RedCircleScene()
+{
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fake_framebuffer); // fbo
+
+    glUseProgram(shaders[redcircle]);
+    float mymillis = (millis-scene_start_millis)*160;
+
+    glClearColor(0,0,0,0);
+    if (scene_shader_params[2] == 36) { redcounter++; }
+    if (redcounter > 4) { redcounter = 0; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); }
+
+    glEnable(GL_BLEND);
+    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+    GLint widthLoc2 = glGetUniformLocation(shaders[redcircle], "width");
+    GLint heightLoc2 = glGetUniformLocation(shaders[redcircle], "height");
+
+    glUniform1f(widthLoc2, g_Width);
+    glUniform1f(heightLoc2, g_Height);
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, textures[tex_grayeye]);
+
+    GLint location2 = glGetUniformLocation(shaders[redcircle], "texture0");
+    glUniform1i(location2, 0);
+
+    glLoadIdentity();
+
+    glTranslatef(0.0, 0.0, -90.0-cos(mymillis*0.0000004)*120);
+    glRotatef(-75.0, 1.0, 0.0, 0.0);
+    glRotatef(mymillis*0.01, 0.0, 0.0, 1.0);
+
+    glBegin(GL_QUADS);
+
+    int i,j;
+
+    for (i = -50; i < 50; i+=10)
+        for (j = -50; j < 50; j+=10)
+        {
+
+            glVertex2f(i, j);
+            glVertex2f(i + 1, j);
+            glVertex2f(i + 1, j + 1);
+            glVertex2f(i, j + 1);
+        }
+    glEnd();
+
+}
+
+void VHSPost(float effuon)
+{
+    if (current_scene == 6) effuon = 2.0f;
+    if (current_scene == 7) effuon = 3.0f;
+    float mymillis = (millis-scene_start_millis);
+
+    if (current_scene == 1 || current_scene == 2)
+    {
+        if (millis > 105000 && millis < 112000) brieflycase_render();
+    }
+
+    if (current_scene == 1 || current_scene == 3) 
+    {
+// console crap
+        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fake_framebuffer); // default
+
+        glLoadIdentity();
+
+        mat4_set_identity( &projection );
+        mat4_set_identity( &model );
+        mat4_set_identity( &view );
+
+        mat4_set_orthographic( &projection, 0, g_Width, 0, g_Height, -1, 1);
+
+        console_render( console );
+    }   
+
+
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // default
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+    glClearDepth(1.0f); // Depth Buffer Setup
+
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    glUseProgram(shaders[vhs]);
+
+    glDisable(GL_BLEND);
+    float widthLoc5 = glGetUniformLocation(shaders[vhs], "width");
+    float heightLoc5 = glGetUniformLocation(shaders[vhs], "height");
+    float timeLoc5 = glGetUniformLocation(shaders[vhs], "time");
+    float effuLoc5 = glGetUniformLocation(shaders[vhs], "effu");
+
+    float beatLoc = glGetUniformLocation(shaders[vhs], "beat");
+    float noiseLoc = glGetUniformLocation(shaders[vhs], "noisetin");
+
+    if ((current_scene == 2 || current_scene == 1 || current_scene == 4) && (millis > 55000 && millis < 186000))
+    {
+        if (scene_shader_params[2] == 36) { vhsbeat = 1.0f; vhsbeat_start = mymillis; }
+        vhsbeat-=((mymillis-vhsbeat_start)*0.00005);
+        if (vhsbeat <= (current_scene == 2 ? 0.2 : 0.1)) vhsbeat = (current_scene == 2 ? 0.2 : 0.1);
+    }
+    else if (current_scene == 3) {
+        if (millis >= 181500 && vhsbeat_start < 181500) { 
+            vhsbeat_start = millis;
+            //printf("START!\n");
+        }
+
+        if (vhsbeat_start >= 181500)
+        {
+            vhsbeat = (millis-vhsbeat_start)*(0.02/30);
+            //printf("vhsbeat:%f\n", vhsbeat);
+        }
+    }
+    else if (current_scene == 6)
+    {
+       if (scene_shader_params[2] == 36) { vhsbeat = 1.0f; vhsbeat_start = mymillis; }
+        vhsbeat-=((mymillis-vhsbeat_start)*0.0005);
+        if (vhsbeat < 0.0) vhsbeat = 0.0;
+
+    }
+    else{
+        vhsbeat = 0.2;
+    }
+
+    float vhsnoise = ((float)(scene_shader_params[5]))/255.0f;
+    float adder = (((float)(scene_shader_params[4]))/255.0f)-(current_scene == 2 ? 0.2 : 0.5f);
+
+    if (current_scene == 4) adder = 0.0;
+
+    if (current_scene == 7) vhsnoise = 1.0+cos(millis*0.001);
+
+    if (!assets_loaded) vhsnoise=sin(loading_time*0.1);
+
+    glUniform1f(widthLoc5, g_Width);
+    glUniform1f(heightLoc5, g_Height);
+    glUniform1f(timeLoc5, mymillis/100);
+    glUniform1f(effuLoc5, effuon);
+    glUniform1f(beatLoc, vhsbeat+adder);
+    glUniform1f(noiseLoc, vhsnoise);
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, fake_framebuffer_tex);
+
+    float location5 = glGetUniformLocation(shaders[vhs], "texture0");
+    glUniform1i(location5, 0);
+
+    glLoadIdentity();
+
+    glTranslatef(-1.2, -1.0, -1.0);
+
+    int i=0;
+    int j=0;
+    glBegin(GL_QUADS);
+    glVertex2f(i, j);
+    glVertex2f(i + 100, j);
+    glVertex2f(i + 100, j + 100);
+    glVertex2f(i, j + 100);
+    glEnd();
+
+
+}
+
+
+///////////////////////////////////////////////////////////////// END EFFECTS
+///////////////////////////////////////////////////////////////// END EFFECTS
+///////////////////////////////////////////////////////////////// END EFFECTS
+///////////////////////////////////////////////////////////////// END EFFECTS
+///////////////////////////////////////////////////////////////// END EFFECTS
+
+// update sync from midi + mapping data
+
+void UpdateShaderParams()
+{
+    int intmillis = (int)millis;
+    int i;
+
+
+    for (i=0; i < mapping_count; i++)
+    {
+        int tracknum = mapping_tracknum[i];
+        int trackidx = timeline_trackindex[tracknum];
+        if (timeline_trackindex[tracknum] >= timeline_tracklength[tracknum]) continue;
+
+        MIDI_MSG currentMsg = timeline[tracknum][trackidx];
+
+
+        int dw = (int)currentMsg.dwAbsPos;
+        int tarkistus = (int)(dw)*1.212;
+
+        // flush midi to correct position if debugging
+        if (debugmode == 1)
+        {
+            while (tarkistus < intmillis)
+            {
+                timeline_trackindex[tracknum]++;
+                trackidx = timeline_trackindex[tracknum];
+                MIDI_MSG currentMsg2 = timeline[tracknum][trackidx];
+                dw = (int)currentMsg2.dwAbsPos;
+                tarkistus = (int)(dw)*1.212;
+            }
+            printf("DEBUG: midi track %d flushed to position: %d\n", tracknum, tarkistus);
+        }
+
+        // reset trigs
+        if (scene_shader_param_type[mapping_paramnum[i]] == 0) scene_shader_params[mapping_paramnum[i]] = -1;
+
+        //if (intmillis+155520 < tarkistus*1.212) break;
+        if (intmillis < tarkistus) continue;
+
+        timeline_trackindex[tracknum]++;
+
+        int ev = 0;
+
+        if (currentMsg.bImpliedMsg) { ev = currentMsg.iImpliedMsg; }
+        else { ev = currentMsg.iType; }
+
+//        DebugPrintEvent(ev, currentMsg);
+
+        int trigVal = -1;
+        int paramVal = -1;
+
+        switch(mapping_type[i])
+        {
+            // trig
+            case 0:
+            {
+
+                if (ev == msgNoteOn)
+                {
+                    trigVal = currentMsg.MsgData.NoteOn.iNote;
+//                  printf("track #%d (%s), len %d, pos %d: dwAbsPos: %d (millis: %d) -> noteon: %d (shadermap num %d)\n", tracknum, timeline_trackname[tracknum], timeline_tracklength[tracknum], timeline_trackindex[tracknum], currentMsg.dwAbsPos, intmillis, trigVal, mapping_paramnum[i]);
+//                    printf("shader param %d trig: %d\n", mapping_paramnum[i], trigVal);
+                }
+                else if (ev == msgNoteOff)
+                {
+                    trigVal = -1;
+                }
+
+                scene_shader_params[mapping_paramnum[i]] = trigVal;
+                scene_shader_param_type[mapping_paramnum[i]] = 0;
+                //if (ev == msgNoteOn) printf("sync (%s): %d: trig %d to: %f\n", timeline_trackname[tracknum], intmillis, mapping_paramnum[i], scene_shader_params[mapping_paramnum[i]]);
+                break;
+            }
+
+            // param:
+            case 1:
+            {
+                if (ev == msgSetParameter)
+                {
+                    paramVal = currentMsg.MsgData.NoteParameter.iParam;
+                    scene_shader_params[mapping_paramnum[i]] = paramVal;
+                    scene_shader_param_type[mapping_paramnum[i]] = 1;
+                    //printf("sync (%s): %d: param %d to: %d\n", timeline_trackname[tracknum], intmillis, mapping_paramnum[i], scene_shader_params[mapping_paramnum[i]]);
+                }
+
+                break;
+            }
+        }
+    }
+    debugmode = 0;
+
+}
+///////////////////////////////////////////////////////////////// MAIN LOGIC
+
+void quit()
+{
+    quitflag = 1;
+    printf("--- MIDISYS ENGINE: time to quit()\n");
+    if(!window) {
+        glutLeaveGameMode();
+        glutLeaveMainLoop();
+    } else {
+        glutDestroyWindow(window);
+        glutLeaveMainLoop();
+    }
+}
+
+double min(double a, double b)
+{
+    if (a<=b) return a;
+    else return b;
+}
+
+GLint gFramesPerSecond = 0;
+ 
+void FPS(void) {
+  static GLint Frames = 0;         // frames averaged over 1000mS
+  static GLuint Clock;             // [milliSeconds]
+  static GLuint PreviousClock = 0; // [milliSeconds]
+  static GLuint NextClock = 0;     // [milliSeconds]
+ 
+  ++Frames;
+  Clock = glutGet(GLUT_ELAPSED_TIME); //has limited resolution, so average over 1000mS
+  if ( Clock < NextClock ) return;
+ 
+  gFramesPerSecond = Frames/1; // store the averaged number of frames per second
+ 
+  PreviousClock = Clock;
+  NextClock = Clock+1000; // 1000mS=1S in the future
+  Frames=0;
+}
+
+void logic()
+{   
+    if (assets_loaded) {
+        if (music_started == -1) {
+            printf("--- MIDISYS-ENGINE: total loading time: %f\n", loading_time);
+            printf("--- MIDISYS-ENGINE: demo startup\n");
+            BASS_ChannelPlay(music_channel,FALSE); music_started = 1;
+            if(jump_to) { BASS_ChannelSetPosition(music_channel, jump_to, BASS_POS_BYTE); }
+        } 
+
+        QWORD bytepos = BASS_ChannelGetPosition(music_channel, BASS_POS_BYTE);
+        double pos = BASS_ChannelBytes2Seconds(music_channel, bytepos);
+        millis = (float)pos*1000*demo_speed_x;
+
+        if (millis > 367000) quit();
+
+        demo_playlist();
+        scene_logic[current_scene](0.0f);
+    } else {
+        t_loader_d = clock();
+        //scene_logic[current_scene]((float)((float)(assets_index) / (float)(assets_total)));
+    }
+
+//  glutPostRedisplay();
+}
+ 
+void timer(int value)
+{
+  const int desiredFPS=60;
+  glutTimerFunc(1000/desiredFPS, timer, ++value);
+ 
+  logic();
+
+  FPS(); //only call once per frame loop to measure FPS 
+  if (quitflag == 0) glutPostRedisplay();
+}
+
+
+///////////////////////////////////////////////////////////// RENDER FUNCTION
+
+void display(void)
+{
+    UpdateShaderParams();
+    scene_render[current_scene]();
+    VHSPost(assets_loaded && current_scene <= 4 ? 1.0 : 0.0);
+
+    glFlush();
+    glutSwapBuffers();
+    frame++;
+    logic();
+}
+
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+
+void keyPress(unsigned char key, int x, int y)
+{
+    if (key == 27) quit();
+}
+
+void mouseMotion(int button, int state, int x, int y)
+{
+    mouseX = x;
+    mouseY = y;
+}
+
+void InitFBO()
+{
+    glGenTextures(1, &fb_tex);
+    glBindTexture(GL_TEXTURE_2D, fb_tex);
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+
+    printf("\tframebuffer size: %dx%d\n", g_Width, g_Height);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, g_Width, g_Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+    glGenFramebuffersEXT(1, &fb);
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);
+
+    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fb_tex, 0);
+
+    glGenRenderbuffersEXT(1, &depth_rb);
+    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_rb);
+    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, g_Width, g_Height);
+
+    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_rb);
+
+    GLenum status;
+    status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+    switch(status)
+    {
+        case GL_FRAMEBUFFER_COMPLETE_EXT:
+            printf("\tInitFBO() status: GL_FRAMEBUFFER_COMPLETE\n");
+            break;
+        default:
+            printf("\tInitFBO() error: status != GL_FRAMEBUFFER_COMPLETE\n");
+            exit(1);
+            break;
+    }
+
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
+
+
+
+    glGenTextures(1, &fb_tex2);
+    glBindTexture(GL_TEXTURE_2D, fb_tex2);
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, g_Width, g_Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+    glGenFramebuffersEXT(1, &fb2);
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb2);
+
+    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fb_tex2, 0);
+
+    glGenRenderbuffersEXT(1, &depth_rb2);
+    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_rb2);
+    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, g_Width, g_Height);
+
+    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_rb2);
+
+    status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+    switch(status)
+    {
+        case GL_FRAMEBUFFER_COMPLETE_EXT:
+            printf("\tInitFBO() status: GL_FRAMEBUFFER_COMPLETE\n");
+            break;
+        default:
+            printf("\tInitFBO() error: status != GL_FRAMEBUFFER_COMPLETE\n");
+            exit(1);
+            break;
+    }
+
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
+
+//----------
+
+    glGenTextures(1, &fake_framebuffer_tex);
+    glBindTexture(GL_TEXTURE_2D, fake_framebuffer_tex);
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, g_Width, g_Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+    glGenFramebuffersEXT(1, &fake_framebuffer);
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fake_framebuffer);
+
+    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fake_framebuffer_tex, 0);
+
+    glGenRenderbuffersEXT(1, &depth_rb3);
+    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_rb3);
+    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, g_Width, g_Height);
+
+    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_rb3);
+
+    glClearColor(0.0,0.0,0.0,1.0);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+    switch(status)
+    {
+        case GL_FRAMEBUFFER_COMPLETE_EXT:
+            printf("\tInitFBO() status: GL_FRAMEBUFFER_COMPLETE\n");
+            break;
+        default:
+            printf("\tInitFBO() error: status != GL_FRAMEBUFFER_COMPLETE\n");
+            exit(1);
+            break;
+    }
+
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
+
+
+
+}
+
+void InitGraphics(int argc, char* argv[])
+{
+    fprintf(stdout, "--- MIDISYS ENGINE: InitGraphics()\n");
+    glutInit(&argc, argv);
+
+
+    if(!window) {
+        glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
+        // 1280x720, 32bit pixel depth, 60Hz refresh rate
+        glutGameModeString( "1280x720:32@60" );
+
+        // start fullscreen game mode
+        glutEnterGameMode();
+    } else {
+        window = glutCreateWindow("majestic twelve by bilotrip");   
+        glutReshapeWindow(c_Width, c_Height);
+    }
+
+    glutSetCursor(GLUT_CURSOR_NONE);
+
+    GLenum err = glewInit();
+    if (GLEW_OK != err)
+    {
+        /* Problem: glewInit failed, something is seriously wrong. */
+        fprintf(stderr, "\tInitGraphics() error: %s\n", glewGetErrorString(err));
+        exit(1);
+    }
+    fprintf(stdout, "\tInitGraphics() status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
+
+    glEnable(GL_TEXTURE_2D);
+
+    glShadeModel(GL_SMOOTH);
+
+#ifdef  __APPLE__
+    int swap_interval = 1;
+    CGLContextObj cgl_context = CGLGetCurrentContext();
+    CGLSetParameter(cgl_context, kCGLCPSwapInterval, &swap_interval);
+#endif
+
+    glutDisplayFunc(display);
+    glutReshapeFunc(reshape);
+    glutIdleFunc(logic);
+    glutKeyboardFunc(keyPress);
+    glutMouseFunc(mouseMotion);
+    glutTimerFunc(0,timer,0);
+
+    fprintf(stdout, "--- MIDISYS ENGINE: InitGraphics() success\n");
+}
+
+void StartMainLoop()
+{
+    printf("--- MIDISYS ENGINE: StartMainLoop()\n");
+    glutMainLoop();
+}
+
+int main(int argc, char* argv[])
+{
+    printf("--- MIDISYS ENGINE: bilotrip foundation MIDISYS ENGINE 4.20 - dosing, please wait\n");
+    
+    // init graphics
+
+    InitGraphics(argc, argv);
+
+    // init console
+
+    console = console_new();
+    shader = shader_load("data/shaders/v3f-t2f-c4f.vert",
+                         shaderss[0]);
+
+    // load & init video
+
+    printf("--- nu laddar vi en videofilmen, det aer jaetteroligt att fuska poe Assembly\n");   
+
+    OggPlayer ogg("data/video/video.ogg",AF_S16,2,44100,VF_BGRA);
+    if(ogg.fail()) {
+       printf("could not open video file \"%s\"\n", "data/video/video.ogg\n");
+       return -2;
+    }
+    YUVFrame yuv_frame(ogg);
+    myVideoFrame = &yuv_frame;
+
+    // init MIDI sync and audio
+
+    LoadMIDIEventList("data/music/midicontrols_final.mid");
+    ParseMIDITimeline("data/music/mapping.txt");
+    InitAudio("data/music/UusiArtistiNimi_-_The_March_compoVersion.mp3");
+
+    // Loader assets
+
+    bilothorn = Import3DFromFile("data/models/bilotrip_logo_thorn.3ds");
+    LoadGLTextures(bilothorn);
+    biloflat = Import3DFromFile("data/models/bilotrip_logo_flat.obj");
+    LoadGLTextures(biloflat);
+    bilothree = Import3DFromFile("data/models/bilotrip.3ds");
+    LoadGLTextures(bilothree);
+    bilotetra = Import3DFromFile("data/models/bilotrip_logo_tetra.obj");
+    LoadGLTextures(bilotetra);
+    brieflycase = Import3DFromFile("data/models/brieflycase.obj");
+    LoadGLTextures(brieflycase);
+
+    // start mainloop
+
+    StartMainLoop();
+
+    return 0;
+}
\ No newline at end of file