Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <canvas id="c" style="width:100%;height:100%;"></canvas>
<div id="clock"></div>

<script id="2d-vertex-shader" type="x-shader/x-vertex">
//proc 3d DOC {violet bold}
//
// 3d coordinate system used on sebastian.it
//
//
//              +Y
//               |
//          e    |
//         n     |
//        a      |     XY - plane
//       l       |
//      p        |
//     /         |
//    ZY         /------------------+X
//              /
//             /       ZX - plane
//            /
//          +Z

// In our world all graphic objects are represented by a quadrilateral polygon.
// Each polygon is composed of two triangles and therefore has 6 vertices (a, b, c; d, e, f).
// Each vertex is composed of three X,Y,Z coordinates (aX,aY,aZ, bX,bY,bZ ... fX,fY,fZ).
//
//   a           d    e
//   |\          ------
//   | \          \   |
//   |  \          \  |
//   |   \          \ |
//   ------          \|
//   b    c           f
//
// Each polygon has a texture assigned to the vertices of the triangles that compose it.
// Since the texture has no depth it has only two coordinates per vertex.
//
// In our simplified 3d world there are two types of geometric objects:
// - the tiles "T", which make up the terrain of the world and are considered fixed.
// - the entities "E", which represent everything else and which are generally dynamic objects.
//

// NOTE
// A graphical feature that differentiates between tiles and entities
// is the method by which they are drawn. The tiles do not have any kind of
// transparency while the entities do.
// This distinction has a weight in webgl since it forces us to
// manually manage the z-order when we draw objects that need alpha-blending
// as it is necessary to disable the depth buffer by using the following statement
// gl.depthMask (false)

// The tiles make up the ground and are drawn parallel to the ZX plane,
// entities are generally drawn parallel to the XY plane except in some cases.
// For example, the following entities are drawn parallel to the ZX plane:
// - water
// - nymphaea
//
// Here is an "excellent" representation of what has been said:
//               ._____.
//               |     |
//      ------   |  E  |
//     /  T  /   |_____|
//    /_____/
//
//
// TEXTURES
// ref: https://open.gl/textures
//
// The coordinates of the textures follow the opengl logic
// in which the coordinates are normalized in the range 0.0-1.0
//
// The pixels in the texture are addressed using texture coordinates
// during drawing operations.
// 0,0 is conventionally the bottom-left corner
// 1,1 is the top-right corner of the texture image
//
//
// 0.0 1.0        1.0 1.0
//    .______________.
//    |              |
//    |              |
//    |              |
//    |              |
//    |              |
//    |              |
//    |______________|
// 0.0 0.0        1.0 0.0
//
//
// To avoid confusion with the X,Y coordinates of the vertices,
// by convention the coordinates of the textures are referenced
// with the parameters S and T.
//
// This is how the polygons are represented in our code:
// Tile "T" - ZX plane
// Since the texture follows the order of the vertices of the triangles
// we arrange the coordinates S and T according to the current vertex:
//
// vertices        name  buffer_index  texture_coords  name
// X    Y    Z                         S    T
// 0.0  0.0  0.0   a     0, 1, 2       0.0  1.0        A
// 0.0  0.0  1.0   b     3, 4, 5       0.0  0.0        B
// 1.0  0.0  1.0   c     6, 7, 8       1.0  0.0        C
//
// 0.0  0.0  0.0   d     9,10,11       0.0  1.0        D
// 1.0  0.0  0.0   e     12,13,14      1.0  1.0        E
// 1.0  0.0  1.0   f     15,16,17      1.0  0.0        F
//
//
//              +Y
//               |
//          e    |
//         n     |
//        a      |
//       l       |
//      p        |
//     /         |
//    ZY       aA,dD---------eE----+X
//              /\          /
//             /  \        /
//            /    \      /
//           /      \    /
//          /        \  /
//         /__________\/
//        /bB        cC,fF
//       /
//     +Z
//
//
//entity "E" - XY plane
//
// vertices        name  buffer_index  texture_coords  name
// X    Y    Z                         S    T
// 0.0  1.0  0.0   a     0, 1, 2       0.0  1.0        A
// 0.0  0.0  0.0   b     3, 4, 5       0.0  0.0        B
// 1.0  0.0  0.0   c     6, 7, 8       1.0  0.0        C
//
// 0.0  1.0  0.0   d     9,10,11       0.0  1.0        D
// 1.0  1.0  0.0   e     12,13,14      1.0  1.0        E
// 1.0  0.0  0.0   f     15,16,17      1.0  0.0        F
//
//
//              +Y
//               |
//              aA,dD--------eE
//               |\          |
//               | \         |
//               |  \        |
//               |   \       |
//               |    \      |
//          e    |     \     |
//         n     |      \    |
//        a      |       \   |
//       l       |        \  |
//      p        |         \ |
//     /         |          \|
//    ZY       bB,dD--------cC,fF-----+X
//              /
//             /
//            /
//           /
//          /
//        +Z
//
//
// and I would say that for today I have done enough ASCII art.
//
//
//
//--------------------------------------------------------------------------

//For our simple world lowp precision is enough
precision lowp float;
precision lowp int;

// vertex shader inputs:
attribute vec3 aVertexPosition;    //these are the coordinates of the vertices -> x,y,z
attribute vec2 aTextureCoord;      //s,t textures coords for each vertext
attribute vec3 aEntityTranslation; //x,y,z translations are applied to each vertex to move entities around the world (Daft Punk)
attribute vec4 aEntityColor;       //r,g,b,a colors for every vertex

attribute vec4 aEntityProperties;
// [0]=entity current texture normalized x0 coordinate (used for x fliping)
// [1]=texture_atlas_layer_index
// [2]=entity current texture normalized x1 coordinate (used for x fliping)
// since apparently openl gl 3.0 is required for bitwise operations ....
// [3]=poor man's fast bitwise -> 'packed' combined mutual exclusive properties
//     bool hasShadowMitigation (generally clouds and other types of objects in the sky)
//     bool isLightEmitter (sun, moon)
//     bool isFlippedX  (flip entity around y axis)

uniform mat4  uMVMatrix; //model view matrix        -> rotations
uniform mat4  uPMatrix;  //camera projection matrix -> perspective

uniform vec3 uCameraTranslation; //the x, y, z coordinates of the camera are added to each vertex

uniform vec3 uPointLighting1Location; //the x,y,z position of our first light point
uniform vec3 uPointLighting2Location; //the x,y,z position of our second light point

// global ambient light
// setting it to 1.0.1.0.1.0 we have full lighting
// setting it to 0.0.0.0.0.0 will have light only from the point light
uniform lowp vec3 uAmbientLightColorIntensity;
uniform lowp vec3 uAmbientLightColor; //the color of the ambient light, controls the color of all sprites globally

varying vec2  vTextureCoord;
varying vec3  vLightWeighting;
varying vec3  vLightWeightingWhiteLight;
varying float vTextureAlpha;
varying float vTextureLayerIndex;

void main(void){

  vec3 pointLighting1ColorIntensity=vec3(1.0,1.0,1.0);//light color intensity of point light 1 (sun)
  vec3 pointLighting2ColorIntensity=vec3(0.5,0.5,0.6);//light color intensity of point light 2 (moon) a little bluish

  gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition+aEntityTranslation-uCameraTranslation,1.0);
  vTextureCoord = aTextureCoord;

  vTextureAlpha       =aEntityColor.a;
  vTextureLayerIndex  =aEntityProperties[1];
  float combinedProperties=aEntityProperties[3];
                           //1
  bool hasShadowMitigation=((combinedProperties>0.9 && combinedProperties<1.9)); //clouds
                           //10
  bool isLightEmitter     =(combinedProperties>9.9 && combinedProperties<10.9);  //stars and satellites
                           //20
  bool isFlippedX         =(combinedProperties>19.9);                            //entities

  if (isFlippedX){
    //aEntityProperties receives at aEntityProperties[0]
    //the normalized x coordinate of the texture from the texture atlas
    //and aEntityProperties[0] the normalized x1 coordinates (x + width) of the texture
    vTextureCoord.x = aEntityProperties[2]-aTextureCoord.x+aEntityProperties[0];
  }

  vec3 pointLighting1Location=uPointLighting1Location;
  vec3 pointLighting2Location=uPointLighting2Location;
  if (hasShadowMitigation){
    //Let's raise the light for the current entity more than the rest,
    //this allows us to have illuminated objects on the sky while the others are not
    pointLighting1Location[1]+=100.0;  //sun light
    pointLighting2Location[1]+=20.0;   //moon light
  }

  if (!isLightEmitter){
    //the entity does not emit its own light

    //we must determine the direction of the light:
    vec3 vectorNormal=vec3(0.0,1.0,0.0); //in our simple geometry just consider a fixed normal
    vec3 light1Direction = normalize(pointLighting1Location - aEntityTranslation - aVertexPosition);
    vec3 light2Direction = normalize(pointLighting2Location - aEntityTranslation - aVertexPosition);

    float directionalLight1Weighting = max(dot(vectorNormal, light1Direction), 0.0);
    float directionalLight2Weighting = max(dot(vectorNormal, light2Direction), 0.0);

    vec3 sunWhiteLight  = pointLighting1ColorIntensity
                          * directionalLight1Weighting * (uAmbientLightColor+0.1);
    //+0.1 explained ->
    //give a little shove to the color to have white <-> #fffff during zenith

    vec3 moonWhiteLight = pointLighting2ColorIntensity
                          * directionalLight2Weighting * uAmbientLightColor;

    //our world has two mobile points of light in total
    vLightWeighting = uAmbientLightColorIntensity + sunWhiteLight  * aEntityColor.xyz; //sunlight
    vLightWeighting+= uAmbientLightColorIntensity + moonWhiteLight * aEntityColor.xyz; //moonlight

    //----
    //we repeat the above calculations but without taking into
    //account the color of the entity. This will give us a white
    //light that we can use to manage the 'neutral' pixels of
    //the entity in the fragment shader that will not have
    //to undergo color alterations (such as the eyes).

    vLightWeightingWhiteLight = uAmbientLightColorIntensity + sunWhiteLight;
    vLightWeightingWhiteLight+= uAmbientLightColorIntensity + moonWhiteLight;

  }else{
    //if the entity emits light do not apply color variation
    vLightWeighting=vec3(1.0,1.0,1.0);
  }

}
</script>

<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision lowp float;
precision lowp int;

//our textures
//instead of using a texture array that requires an extension
//we select the texture atlas layer by using a variable.
//It is not the most efficient thing in the world but it works
//and for our little world is enough.
//about tiling+texture atlas
//----
//the texture bleeding phenomenon is 'solved'
//by an appropriate drawing method during the texture atlas generation

uniform sampler2D uTextureSampler0; //ref: https://www.khronos.org/opengl/wiki/Sampler_(GLSL)
uniform sampler2D uTextureSampler1;
uniform sampler2D uTextureSampler2;
uniform sampler2D uTextureSampler3;
uniform sampler2D uTextureSampler4;
uniform sampler2D uTextureSampler5;
uniform sampler2D uTextureSampler6;
uniform sampler2D uTextureSampler7;
//2018-04-02 15:54:41 - in the tests I did on mobile it seems that there are problems beyond the 8 textures

//the texCoords passed in from the vertex shader.
//gluniform doc -> ref: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glUniform.xhtml
varying vec2  vTextureCoord;
varying vec3  vLightWeighting;
varying vec3  vLightWeightingWhiteLight;
varying float vTextureAlpha;
varying float vTextureLayerIndex;

void main(void){

  vec4 color;

  // We use a float range to determine the layer
  // and not a specific number to allow correct management on mobile browsers

  if (vTextureLayerIndex==0.0)                               //select atlas 0
    color=texture2D(uTextureSampler0,vec2(vTextureCoord.s,vTextureCoord.t));
  else if (vTextureLayerIndex>0.9 && vTextureLayerIndex<1.9) //atlas 1
    color=texture2D(uTextureSampler1,vec2(vTextureCoord.s,vTextureCoord.t));
  else if (vTextureLayerIndex>1.9 && vTextureLayerIndex<2.9) //atlas 2
    color=texture2D(uTextureSampler2,vec2(vTextureCoord.s,vTextureCoord.t));
  else if (vTextureLayerIndex>2.9 && vTextureLayerIndex<3.9) //atlas 3
    color=texture2D(uTextureSampler3,vec2(vTextureCoord.s,vTextureCoord.t));
  else if (vTextureLayerIndex>3.9 && vTextureLayerIndex<4.9) //atlas 4
    color=texture2D(uTextureSampler4,vec2(vTextureCoord.s,vTextureCoord.t));
  else if (vTextureLayerIndex>4.9 && vTextureLayerIndex<5.9) //atlas 5
    color=texture2D(uTextureSampler5,vec2(vTextureCoord.s,vTextureCoord.t));
  else if (vTextureLayerIndex>5.9 && vTextureLayerIndex<6.9) //atlas 6
    color=texture2D(uTextureSampler6,vec2(vTextureCoord.s,vTextureCoord.t));
  else if (vTextureLayerIndex>6.9 && vTextureLayerIndex<7.9) //atlas 7
    color=texture2D(uTextureSampler7,vec2(vTextureCoord.s,vTextureCoord.t));

  //2018-05-04 15:39:04
  //with the following IF we choose the RED color (used as a mask) in a fairly aprox way,
  // We use a color mask to be able to color the eyes of the entities with white light
  // and the rest of the body with the color of the sprite
  if (color.r>.5 && color.g<.5 && color.b<.5){
    float gray=color.r+color.g+color.b/3.0;
    vec3  colorGray=vec3(gray,gray,gray);
    gl_FragColor = vec4(colorGray.rgb*vLightWeightingWhiteLight,vTextureAlpha*color.a);
  }else{
    gl_FragColor = vec4(color.rgb*vLightWeighting,vTextureAlpha*color.a);
  }

}
</script>

              
            
!

CSS

              
                html, body{
margin:0;
padding:0;
width:100%;
height:100%;
overflow:hidden;
}
#clock{
font-family:"Lucida Sans Unicode", "Lucida Grande", sans-serif;
position:absolute;
font-size:40px;
color:white;
bottom:10px;
right:25px;
}

              
            
!

JS

              
                  const SEBASTIAN={ //proc SEBASTIAN
    GLOBALS:{ //proc GLOBALS {orange indent_1}
      fps:30, //orginal value: 1000 - number of frames rendered per second, the higher the number the greater the cpu usage
      maxHandledTextures:8, //8 seems the max number suitable for mobile. if increased, fragment shader code modifications are required iON2D2Js0y
      maxTextureAtlasWidth:4096, //4096 tested on on iPhone 6 and Android
      maxTextureAtlasHeight:4096,
      m3d:null, //mouse 3d coordinates from 2d position
      mx:0, //actual mouse coord and prev mouse coords
      my:0,
      mb:null, //mouse button pressed or released
      oMx:null, //detect mouse movement
      oMy:null,

      //time
      hours:null,
      minutes:null,

      //entities idx counter
      idx:0,

      loadedAssets:0, //texture loader index
      //pressed keys
      keys:[],
      keysUp:[],
      //frame control
      lastTime:window.performance.now(),
      //color mapping and time travel
      lastTimeKey:null,
      debugLightColors:1,
      timeTravel:0,
      //map Editor
      tilesColorPool:['C5','C1','C3','C2','C4','B1','B2','B3'],
      tilesColorPoolIdx:0,
      tilesBusyList:{},
      //tile blob editor
      mapEntityGroups:[
                        'nymphaea','pavementAngleTopLeft','pavementTop','pavementAngleTopRight',
                        'pavementLeft','pavementRight',
                        'pavementAngleBottomLeft','pavementBottom','pavementAngleBottomRight',
                        'water','water_tiled','grass','taraxacum','bullrushes','frog',
                      ],
      mapEntityGroupsIdx:0,
      //--
      editorMode:'terrain', //auto select editor mode
      currentInput:null,
      currentEntityId:0,    //current selected entity
      currentEntityMOId:0,  //current entity under mouse
      entitySelectionColor:[1.0,0.0,1.0], //the color used to highlight a selected entity
      entitySelectedColor:[1.0,0.0,1.0], //the color used to highlight an entity when the mouse is on it
      tilesTexturePool:['pavement_02','pavement_01'],
      tilesTexturePoolIdx:0,
      tilesOpacityPool:[1.0,.7,.9,.5,.3],
      tilesOpacityPoolIdx:0,
      currentTileId:null, //current selected tile
      currentTileMOId:null, //current tile under mouse
      dynamicEntitiesGeometry:1,
      //flags/counters
      isWorldTilesVerticesPositionDirty:0,
      isWorldTilesColorsDirty:0,
      isWorldTilesTextureCoordsDirty:0,
      isWorldTilesPropertiesDirty:0, //if a tile alpha or sprite is changed we set this flag to 1
      autoBuildTextureAtlases:0, //if true, load all the separate texture images and create a texture atlas, otherwise load the atlas (manually saved)
      isEditorEnabled:0,

      loadSavedScene:1,

      isMouseDirty:0,    //used to perform analysis only during mouse movements
      sceneVersion:0,    //used to visually increase the version number of the scene during save, it has no other uses.
      useUTCTime:1,
    },
    init:function(){ //proc init {green indent_1}
      SEBASTIAN.GLOBALS.canvas=document.getElementById('c');
      SEBASTIAN.GLOBALS.clock=document.getElementById('clock');
      SEBASTIAN.WEBGL.initGL();
      SEBASTIAN.UTILS.init();
      SEBASTIAN.WEBGL.initShaders();
      SEBASTIAN.WEBGL.WORLD.ENTITIES.init();

      //init time
      // we use getUTCHours so that there are no 'jumps'
      // in the hour due to the introduction of daylight savings time
      // if we used eg getHours
      // there would be a jump from 01:59 to 03:00 on 24/03/2018
      // and this would cause the moon to jump forward
      var tDate=new Date();
      SEBASTIAN.GLOBALS.fpsInterval=1000/SEBASTIAN.GLOBALS.fps;
      if (SEBASTIAN.GLOBALS.useUTCTime){
        SEBASTIAN.GLOBALS.hours=tDate.getUTCHours();
        SEBASTIAN.GLOBALS.minutes=tDate.getUTCMinutes();
      }else{
        SEBASTIAN.GLOBALS.hours=tDate.getHours();
        SEBASTIAN.GLOBALS.minutes=tDate.getMinutes();
      }

      //proc assets loading CODEPEN {violet indent_2}
      //load assets with a callback, then load world with a callback
      //then init gameloop.

      SEBASTIAN.WEBGL.GLOBALS.textures=textureData_js;
      SEBASTIAN.GLOBALS.lightColorTimeMap=colormap_js;
      var o=SEBASTIAN.GLOBALS.lightColorTimeMap;
      var counter=0;
      // assign an index to every hour of the day,
      // we will use this to rotate the moon and the sun
      // based on current hour
      for (var z in o){
        o[z].idx=counter;
        //console.log(z);
        counter++;
      }

      var assetsListAtlases=[
        {'atlas1.glTexture':atlas_js}, //here we can for example choose textures with different quality/resolution to improve performance on mobile browsers
      ]

      var assetsList=[];
      assetsList=assetsListAtlases;
      SEBASTIAN.UTILS.initAssets(assetsList,function(){
        SEBASTIAN.WEBGL.loadTextureAtlases(function(){
          SEBASTIAN.WEBGL.WORLD.init();
          //SEBASTIAN.WEBGL.WORLD.load();
          SEBASTIAN.WEBGL.WORLD.onloadHandler(
            scene_js,
            function(){SEBASTIAN.gameLoop(window.performance.now())}
          )

        });
      });

    },
    gameLoop:function(newtime){ //proc gameLoop {green indent_1}
      const _seba=SEBASTIAN;
      //proc draw scene call {violet indent_2}
      _seba.WEBGL.WORLD.draw(newtime);

      requestAnimationFrame(_seba.gameLoop); //loop forever
    },
    UTILS:{ //proc UTILS {orange indent_1}
      init:function(){ //proc init {green indent_2}
        const _seba=SEBASTIAN;

        //proc window.onresize {violet indent_3}
        window.onresize=function(){
          //console.log('resize',document.documentElement.clientWidth);
          //canvas resizing
          var docWidth=document.documentElement.clientWidth;
          var docHeight=document.documentElement.clientHeight;
          var canvasDom=document.getElementById('c');
          c.width=docWidth;
          c.setAttribute('width',docWidth);
          c.style.width=docWidth+'px';
          c.height=docHeight;
          c.style.height=docHeight+'px';
          c.setAttribute('height',docHeight);

          //resize webgl render window
          var gl=_seba.GLOBALS.webgl_ctx; //lookup var
          gl.viewportWidth=c.width;
          gl.viewportHeight=c.height;
          _seba.WEBGL.WORLD.GLOBALS.isViewportDirty=1;
        };
        window.onresize();

      },
      onVisibilityChangeHandler:function(hiddenPropertyName){ //proc editorInputKeyUp
        if (document[hiddenPropertyName]){
          SEBASTIAN.GLOBALS.fpsInterval=10000;
          console.log('HIDDEN',new Date());
        }else{
          SEBASTIAN.GLOBALS.fpsInterval=1000/SEBASTIAN.GLOBALS.fps;
          console.log('VISIBLE',new Date());
        }
      },

      applyTimeCalc(cfg){  //proc applyTimeCalc {green indent_2}
        var config={
          //sunLightVerticalPosition:cfg.sunLightVerticalPosition, //_seba.WEBGL.WORLD.GLOBALS.sunLightVerticalDistance
          parameter:cfg.parameter,
          currentHour:cfg.currentHour,
          currentMinute:cfg.currentMinute,
          fromHours:cfg.fromHours,
          toHours:cfg.toHours,
          fromMinutes:cfg.fromMinutes,
          toMinutes:cfg.toMinutes,
          fromValue:cfg.fromValue,
          toValue:cfg.toValue,
          logInfo:cfg.logInfo || 0,
          applyTimeBouncing:cfg.applyTimeBouncing,
        }
        var _seba=SEBASTIAN;
        var parameter=config.parameter;
        var currentHour=config.currentHour;
        var currentMinute=config.currentMinute;
        var fromHours=config.fromHours;
        var toHours=config.toHours;
        var fromMinutes=config.fromMinutes;
        var toMinutes=config.toMinutes;
        var fromValue=config.fromValue;
        var toValue=config.toValue;
        var logInfo=config.logInfo;
        var applyTimeBouncing=config.applyTimeBouncing;
        if (typeof applyTimeBouncing==='undefined')
          applyTimeBouncing=1;

        //build Time index
        var h_key, m_key, colorKey;
        //current index
        //to index
        h_key=currentHour;
        m_key=currentMinute;
        colorKey=h_key+'_'+m_key;
        var currentTimeIdx=SEBASTIAN.GLOBALS.lightColorTimeMap[colorKey].idx;
        //from index
        h_key=fromHours;
        m_key=fromMinutes;
        colorKey=h_key+'_'+m_key;
        var initTimeIdx=SEBASTIAN.GLOBALS.lightColorTimeMap[colorKey].idx;
        //to index
        h_key=toHours;
        m_key=toMinutes;
        colorKey=h_key+'_'+m_key;
        var endTimeIdx=SEBASTIAN.GLOBALS.lightColorTimeMap[colorKey].idx;
        var distanceIdx=endTimeIdx-initTimeIdx;
        var halfTimeIdx=initTimeIdx+(distanceIdx)/2;

        if (applyTimeBouncing){
          //if (currentHour===fromHours && currentMinute>=fromMinutes){
          if (currentTimeIdx>=initTimeIdx && currentTimeIdx<halfTimeIdx){
            //console.log('+delta',(currentTimeIdx-initTimeIdx)*1/halfTimeIdx);
            //var delta=(currentTimeIdx-initTimeIdx)/initTimeIdx; //normalize
            var delta=(currentTimeIdx-initTimeIdx)*1/distanceIdx*2; //normalize to 0..1
            parameter=fromValue*(1-delta)+toValue*delta; //lerp
            //console.log('>>',parameter);
          }
          if (currentTimeIdx>=halfTimeIdx && currentTimeIdx<=endTimeIdx){
            //var delta=1-((toMinutes-currentMinute)/toMinutes); //normalize
            //console.log('-delta',1-((endTimeIdx-currentTimeIdx)*1/halfTimeIdx));
            //var delta=1-((endTimeIdx-currentTimeIdx)/endTimeIdx); //normalize
            var delta=1-((endTimeIdx-currentTimeIdx)*1/distanceIdx*2);
            parameter=toValue*(1-delta)+fromValue*delta; //lerp
            //console.log('<<',parameter);
          }
        }else{
          if (currentTimeIdx>=initTimeIdx && currentTimeIdx<=endTimeIdx){
            //console.log('+DELTA',(currentTimeIdx-initTimeIdx)*1/halfTimeIdx);
            //var delta=(currentTimeIdx-initTimeIdx)/initTimeIdx; //normalize
            var delta=(currentTimeIdx-initTimeIdx)*1/distanceIdx; //normalize to 0..1
            parameter=fromValue*(1-delta)+toValue*delta; //lerp
            //console.log('>>',parameter);
          }
        }
        return parameter;

      },
      degToRad(degrees){ //proc deg2rad {green indent_2}
        return degrees*Math.PI/180;
      },
      initAssets:function(tList,callback){ //proc initAssets {green indent_2}
        var gl=SEBASTIAN.GLOBALS.webgl_ctx; //lookup var
        for (var z=0,zEnd=tList.length;z<zEnd;z++){

          //a bit hacky
          //for (var assetId in tList[z]){}
          //clearer ->
          var assetId=Object.keys(tList[z])[0];

          if (tList[z][assetId].match(/\.json$/)){
            var assetUrl=tList[z][assetId];
            var xhr=new XMLHttpRequest();
            xhr.onload=function(z,assetId){
              return function(){ //func factory {indent_4}
                if (xhr.status===200){
                  var responseText=this.responseText;
                  if (typeof tList[z].onLoad!=='undefined')
                    responseText=tList[z].onLoad(responseText);
                  SEBASTIAN.GLOBALS[assetId]=JSON.parse(responseText); //save the object in SEBASTIAN.GLOBALS with the specified key
                  if (typeof tList[z].onLoadDone!=='undefined')
                    responseText=tList[z].onLoadDone(SEBASTIAN.GLOBALS[assetId]);
                }else{
                  //request failed
                }
                SEBASTIAN.GLOBALS.loadedAssets++;
                if (SEBASTIAN.GLOBALS.loadedAssets===tList.length){ //all loaded
                  callback();
                }
              }
            }(z,assetId);
            xhr.open('GET',assetUrl);
            xhr.send();
          }else if(assetId.match(/\.glTexture$/)){
            var imgUrl=tList[z][assetId];
            var tempImage=gl.createTexture();
            tempImage.image=new Image();
            SEBASTIAN.WEBGL.GLOBALS.tempTextures[assetId]=tempImage;
            tempImage._data=tList[z].data; //set data
            tempImage.image.onload=function(assetId){
              return function(){
                SEBASTIAN.GLOBALS.loadedAssets++;
                //console.log(SEBASTIAN.GLOBALS.loadedAssets,tList.length);
                if (SEBASTIAN.GLOBALS.loadedAssets===tList.length){ //all loaded
                  callback();
                }
              }
            }(assetId);
            tempImage.image.src=imgUrl;
          }else{
            var imgUrl=tList[z][assetId];
            var tempImage=new Image();
            SEBASTIAN.WEBGL.GLOBALS.tempTextures[assetId]=tempImage;
            tempImage._data=tList[z].data; //set data
            tempImage.onload=function(assetId){
              return function(){
                SEBASTIAN.GLOBALS.loadedAssets++;
                if (SEBASTIAN.GLOBALS.loadedAssets===tList.length){ //all loaded
                  callback();
                }
              }
            }(assetId);
            tempImage.src=imgUrl;
          }
        }
      },
    },
    WEBGL:{ //proc WEBGL {orange indent_1}
      GLOBALS:{ //proc WEBGL.GLOBALS {orange indent_3}
        buffers:{}, //proc buffers {blue indent_3}
        textures:{}, //proc textures {blue indent_3} texture database in relation to the atlas texture
        tempTextures:{}, //proc tempTextures {blue indent_3} textures loaded individually before creating the atlas texture
        shaderProgram:{}, //proc shaderProgram {blue indent_3}
        anisotropicFilter:null,
      },
      //--
      initGL:function(){ //proc initGL {green indent_2}
        var canvas=SEBASTIAN.GLOBALS.canvas;
        //preserveDrawingBuffer    -> determines whether or not to clear the buffer at each frame
        //alpha:false              -> disabled alpha blending with html background
        //premultipliedAlpha:false -> blend alpha from gl with html background
        SEBASTIAN.GLOBALS.webgl_ctx=canvas.getContext('webgl',{preserveDrawingBuffer:false,premultipliedAlpha:true});
        if (!SEBASTIAN.GLOBALS.webgl_ctx) //no luck? ok, let's try something different....
          SEBASTIAN.GLOBALS.webgl_ctx=canvas.getContext('experimental-webgl',{preserveDrawingBuffer:false,premultipliedAlpha:true});
        //--
        var gl=SEBASTIAN.GLOBALS.webgl_ctx; //lookup var
        gl.viewportWidth=canvas.width;
        gl.viewportHeight=canvas.height;

        //enable ALPHA blending for png transparent textures
        //gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA); //premultipliedAlpha should be set to true and -> gl.pixelStorei   (gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); should be used
        //enable ALPHA blending for gif transparent textures (deal with 1 bit transparency)
        gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
        gl.enable(gl.BLEND);

        //2018-03-12 22:58:42
        //test if we can enable anisotropic filtering
        //https://developer.mozilla.org/en-US/docs/Web/API/EXT_texture_filter_anisotropic
        SEBASTIAN.WEBGL.GLOBALS.anisotropicFilter=gl.getExtension('EXT_texture_filter_anisotropic');

        // if we want alpha blending for our .PNGs file we can't use DEPTH_TEST
        // we manage the depth test in a distinct way during drawing.
        // use depthMask(true) for objects without alpha
        // use depthMask(false) for transparent objects ->
        // in this case the z-ordering must be done by our routine
        // see drawloop

        gl.enable(gl.DEPTH_TEST);
      },

      initShaders:function(){ //proc initShaders {green indent_2}
        var gl=SEBASTIAN.GLOBALS.webgl_ctx; //lookup var
        //Init shaders
        //setup a GLSL program
        var vertexShader  =SEBASTIAN.WEBGL.createShaderFromScript(gl,'2d-vertex-shader');
        var fragmentShader=SEBASTIAN.WEBGL.createShaderFromScript(gl,'2d-fragment-shader');
        var shaderProgram =SEBASTIAN.WEBGL.createProgram(gl,[vertexShader,fragmentShader]);
        SEBASTIAN.WEBGL.GLOBALS.shaderProgram=shaderProgram;
        gl.useProgram(shaderProgram);

        //proc shaderProgram set vars {violet indent_3}
        //set attributes and uniforms parameters
        shaderProgram.aVertexPosition_var=gl.getAttribLocation(shaderProgram,'aVertexPosition');
        gl.enableVertexAttribArray(shaderProgram.aVertexPosition_var);
        //--
        shaderProgram.aTextureCoord_var=gl.getAttribLocation(shaderProgram,'aTextureCoord');
        gl.enableVertexAttribArray(shaderProgram.aTextureCoord_var);
        //--
        shaderProgram.aEntityTranslation_var=gl.getAttribLocation(shaderProgram,'aEntityTranslation');
        gl.enableVertexAttribArray(shaderProgram.aEntityTranslation_var);
        //--
        shaderProgram.aEntityColor_var=gl.getAttribLocation(shaderProgram,'aEntityColor');
        gl.enableVertexAttribArray(shaderProgram.aEntityColor_var);
        //--
        shaderProgram.aEntityProperties_var=gl.getAttribLocation(shaderProgram,'aEntityProperties');
        gl.enableVertexAttribArray(shaderProgram.aEntityProperties_var);
        //--

        //--
        shaderProgram.uPMatrix_var        = gl.getUniformLocation(shaderProgram,'uPMatrix');
        shaderProgram.uMVMatrix_var       = gl.getUniformLocation(shaderProgram,'uMVMatrix');

        //do a configurable loop
        for (var z=0;z<SEBASTIAN.GLOBALS.maxHandledTextures;z++){
          shaderProgram['uTextureSampler'+z+'_var']=gl.getUniformLocation(shaderProgram,'uTextureSampler'+z);
        }

        shaderProgram.uAmbientLightColor_var          = gl.getUniformLocation(shaderProgram,'uAmbientLightColor');
        shaderProgram.uAmbientLightColorIntensity_var = gl.getUniformLocation(shaderProgram,'uAmbientLightColorIntensity');

        shaderProgram.uCameraTranslation_var = gl.getUniformLocation(shaderProgram,'uCameraTranslation');

        shaderProgram.uPointLighting1Location_var = gl.getUniformLocation(shaderProgram, "uPointLighting1Location");
        shaderProgram.uPointLighting2Location_var = gl.getUniformLocation(shaderProgram, "uPointLighting2Location");
      },

      loadTextureAtlases:function(callback){ //proc loadTextureAtlases {green indent_2}
        const _seba=SEBASTIAN;
        var gl=SEBASTIAN.GLOBALS.webgl_ctx; //lookup var
        var atlasLayerIndex=0;
        for (var textureSid in _seba.WEBGL.GLOBALS.tempTextures){
          var currentTexture=_seba.WEBGL.GLOBALS.tempTextures[textureSid];
          SEBASTIAN.WEBGL.handleLoadedTextureAtlasImage(currentTexture,atlasLayerIndex);
          //console.log(_seba.WEBGL.GLOBALS.tempTextures,atlasLayerIndex);
          atlasLayerIndex++;
        }
        delete SEBASTIAN.WEBGL.GLOBALS.tempTextures;
        callback();
      },

      handleLoadedTextureAtlasImage:function(texture,layerIdx){ //proc handleLoadedTextureAtlasImage {green indent_2}
        //multitextures -> ref: https://webglfundamentals.org/webgl/webgl-2-textures.html
        var gl=SEBASTIAN.GLOBALS.webgl_ctx; //lookup var
        var shaderProgram=SEBASTIAN.WEBGL.GLOBALS.shaderProgram;
        var USE_MIPMAPS=1;

        gl.uniform1i     (shaderProgram['uTextureSampler'+layerIdx+'_var'],layerIdx);
        gl.activeTexture (gl.TEXTURE0+layerIdx);
        gl.bindTexture   (gl.TEXTURE_2D,texture); //bind once - the atlas texture
        gl.texImage2D    (gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,texture.image);
        gl.texParameteri (gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        if (USE_MIPMAPS){
          //gl.texParameteri (gl.TEXTURE_2D,gl.TEXTURE_BASE_LEVEL,0);
          gl.generateMipmap(gl.TEXTURE_2D);
          gl.texParameteri (gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
        }else{
          gl.texParameteri (gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        }

        //2018-03-12 23:04:23
        //https://developer.mozilla.org/en-US/docs/Web/API/EXT_texture_filter_anisotropic
        //MAX_TEXTURE_MAX_ANISOTROPY_EXT is 16 on my card
        if (SEBASTIAN.WEBGL.GLOBALS.anisotropicFilter)
          gl.texParameteri(
            gl.TEXTURE_2D,
            SEBASTIAN.WEBGL.GLOBALS.anisotropicFilter.TEXTURE_MAX_ANISOTROPY_EXT,
            gl.getParameter(SEBASTIAN.WEBGL.GLOBALS.anisotropicFilter.MAX_TEXTURE_MAX_ANISOTROPY_EXT)
          );

        //2018-02-11 16:26:59
        //using the 'CLAMP_TO_EDGE' setting we
        //avoid the texture bleeding phenomenon visible
        //in the edges of the textures that extend throughout
        //the width or height of the tile (tileable-type textures)
        //Note: 2018-03-11 23:03:15
        //since CLAMP_TO_EDGE only applies to the edges of the texture,
        //this setting can not solve the texture bleeding problem if we are using
        //a texture atlas

        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );

      },
      createShaderFromScript:function(gl,scriptId){ //proc createShaderFromScript {green indent_2}
        var shaderType;
        var shaderScript = document.getElementById(scriptId);
        if (!shaderScript)
          alert("*** Error: script element not found: " + scriptId);

        switch(shaderScript.type){
          case 'x-shader/x-vertex':
            shaderType = gl.VERTEX_SHADER;
          break;
          case 'x-shader/x-fragment':
            shaderType = gl.FRAGMENT_SHADER;
          break;
          default:
            alert('webgl_createShaderFromScript error unkown type')
          break;
        }

        // Create the shader object
        var shader = gl.createShader(shaderType);
        // Load the shader source
        gl.shaderSource(shader,shaderScript.text);
        // Compile the shader
        gl.compileShader(shader);
        // Check the compile status
        var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
        if (!compiled) {
          // Something went wrong during compilation; get the error
          var lastError = gl.getShaderInfoLog(shader);
          console.error("*** Error compiling shader '" + shader + "':" + lastError);
          gl.deleteShader(shader);
          return null;
        }

        return shader;
      },
      createProgram:function(gl,shaders){ //proc createProgram {green indent_2}
        var program = gl.createProgram();
        for (var ii = 0; ii < shaders.length; ++ii) {
          gl.attachShader(program,shaders[ii]);
        }
        gl.linkProgram(program);

        // Check the link status
        var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
        if (!linked) {
          // something went wrong with the link
          var lastError = gl.getProgramInfoLog (program);
          console.error("Error in program linking:" + lastError);
          gl.deleteProgram(program);
          return null;
        }
        return program;
      },

      project:function(x,y,z,xPos,yPos,zPos,eX,eY,eZ,pMatrix,mvMatrix){ //proc project {green indent_2}
        //gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition+aEntityTranslation-uCameraTranslation,1.0);
        //gl.viewport(0,0,gl.viewportWidth,gl.viewportHeight);
        //gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition-uCameraTranslation+uEntityTranslation,1.0);
        //https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glViewport.xml
        //https://github.com/hughsk/from-3d-to-2d/blob/master/index.js
        var gl=SEBASTIAN.GLOBALS.webgl_ctx; //lookup var

        var out=vec4.fromValues(x-xPos+eX,y-yPos+eY,z-zPos+eZ,1.0);

        var pMatrixClone=mat4.clone(pMatrix);

        mat4.multiply(pMatrixClone,pMatrixClone,mvMatrix);

        //transform world to clipping coordinates
        vec4.transformMat4(out,out,pMatrixClone);

        var screenX=Math.round(((out[0]/out[2]+1)/2.0)*gl.viewportWidth);
        var screenY=Math.round(((1-out[1]/out[2])/2.0)*gl.viewportHeight);
        //or
        //var screenX=Math.round((out[0]/out[2]+1)*(gl.viewportWidth/2)+x);
        //var screenY=Math.round((1-out[1]/out[2])*(gl.viewportHeight/2)+y);
        return [screenX,screenY];//pMatrix;//cameraTranslation;//[screenX,screenY];
      },

      WORLD:{ //proc WEBGL.WORLD {orange indent_2}
        GLOBALS:{ //proc WEBGL.WORLD.GLOBALS {orange indent_3}
          //vertexPositions:null, //store dynamic terrain vertex positions
          //worldTilesData:[], //store info for every world tile (e.g textureindex)
          isCameraPositionDirty:1, //flag used to decide whether or not it is necessary to update the translation uniforms of the shader

          //tile world columns
          //these numbers determine the size of the world
          columns:45,
          rows:25,

          //world colors and flags
          sunLightVerticalDistance:300, //height of sunlight (point light) relative to the y position of the sun, the greater the distance the greater the amount of radiated light
          isPointLightPositionDirty:1,
          isAmbientLightDirty:1,

          //def 0.0,0.0,0.0 -> ambient light, the scene has a point light (the sun),
          //this parameter indicates how bright the scene is in case the point-light does
          //not affect what we are watching
          //by setting it to 0,0,0 we get colors with greater contrast
          ambientLightColorIntensity:[0.0,0.0,0.0], //black

          //this color changes according to the time regardless
          //of the light intensity. This parameter allows us to specify
          //which color is the global light
          //(the point light light componente is fixed into the fragment shader for simplicity)
          ambientLightColor:[1.0,1.0,1.0], //1,1,1 -> white

          //ambientLightColor:[22/255,4/255,2/255], //1,1,1 -> white
          //skyLightColor:[254/255,196/255,1/255], //2018-03-17 11:06:14 discontinued
          //ambientLightColor:[171/255,60/255,45/255], //1,1,1 -> white
          //ambientLightColor:[0.01,0.01,0.01], //1,1,1 -> white

          pMatrix:mat4.create(),
          mvMatrix:mat4.create(),

        },
        //dynamic buffers (ummovable objects)
        //static world buffers (ummovable objects)
        vertexPositionBuffer:{}, //proc vertexPositionBuffer {blue indent_3}
        vertexTextureCoordBuffer:{}, //proc vertexTextureCoordBuffer {blue indent_3}
        //---
        init:function(){ //proc init {green indent_3} WEBGL.WORLD.init

        },
        setCameraTo:function(cfg){
          var config={
            xPos:cfg.xPos,
            yPos:cfg.yPos,
            zPos:cfg.zPos,
            pitch:cfg.pitch,
            yaw:cfg.yaw,
          }
          SEBASTIAN.WEBGL.WORLD.GLOBALS.xPos=config.xPos;
          SEBASTIAN.WEBGL.WORLD.GLOBALS.yPos=config.yPos;
          SEBASTIAN.WEBGL.WORLD.GLOBALS.zPos=config.zPos;
          SEBASTIAN.WEBGL.WORLD.GLOBALS.pitch=config.pitch;
          SEBASTIAN.WEBGL.WORLD.GLOBALS.yaw=config.yaw;
          SEBASTIAN.WEBGL.WORLD.GLOBALS.isCameraPositionDirty=1
        },

        syncAllTileBuffersData:function(){ //proc syncAllTileBuffersData {green indent_4}
          //console.log('synca');
          //update the dynamic buffers of all the world tiles
          var _seba=SEBASTIAN;
          var worldData             =_seba.GLOBALS.worldData;
          var dWorldTileSpriteColors=_seba.WEBGL.GLOBALS.buffers.worldTileSpriteColors.data;

          for (var z=0,zEnd=worldData.world.length;z<zEnd;z++){
            var currentWorldTile=worldData.world[z];
            var idx4=currentWorldTile.id*6*4; //added alpha component
            //console.log(z,idx4);
            //vertex colors (4 points per vertex)
            dWorldTileSpriteColors[idx4+0]=currentWorldTile.color1[0];
            dWorldTileSpriteColors[idx4+1]=currentWorldTile.color1[1];
            dWorldTileSpriteColors[idx4+2]=currentWorldTile.color1[2];
            dWorldTileSpriteColors[idx4+3]=currentWorldTile.opacity;

            dWorldTileSpriteColors[idx4+4]=currentWorldTile.color1[0];
            dWorldTileSpriteColors[idx4+5]=currentWorldTile.color1[1];
            dWorldTileSpriteColors[idx4+6]=currentWorldTile.color1[2];
            dWorldTileSpriteColors[idx4+7]=currentWorldTile.opacity;

            dWorldTileSpriteColors[idx4+8]=currentWorldTile.color1[0];
            dWorldTileSpriteColors[idx4+9]=currentWorldTile.color1[1];
            dWorldTileSpriteColors[idx4+10]=currentWorldTile.color1[2];
            dWorldTileSpriteColors[idx4+11]=currentWorldTile.opacity;

            dWorldTileSpriteColors[idx4+12]=currentWorldTile.color1[0];
            dWorldTileSpriteColors[idx4+13]=currentWorldTile.color1[1];
            dWorldTileSpriteColors[idx4+14]=currentWorldTile.color1[2];
            dWorldTileSpriteColors[idx4+15]=currentWorldTile.opacity;

            dWorldTileSpriteColors[idx4+16]=currentWorldTile.color1[0];
            dWorldTileSpriteColors[idx4+17]=currentWorldTile.color1[1];
            dWorldTileSpriteColors[idx4+18]=currentWorldTile.color1[2];
            dWorldTileSpriteColors[idx4+19]=currentWorldTile.opacity;

            dWorldTileSpriteColors[idx4+20]=currentWorldTile.color1[0];
            dWorldTileSpriteColors[idx4+21]=currentWorldTile.color1[1];
            dWorldTileSpriteColors[idx4+22]=currentWorldTile.color1[2];
            dWorldTileSpriteColors[idx4+23]=currentWorldTile.opacity;
          }

          //update the buffer only if necessary
          _seba.GLOBALS.isWorldTilesColorsDirty=1;

        },
        syncTileBuffersData:function(currentWorldTile){ //proc syncTileBuffersData {green indent_4}
          //console.log('syncb');
          //update the dynamic buffers of the world tiles
          //console.log('xxx',currentWorldTile.id);
          var _seba=SEBASTIAN;
          var dWorldTileSpriteColors    =_seba.WEBGL.GLOBALS.buffers.worldTileSpriteColors.data;
          var dWorldTileEntityProperties=_seba.WEBGL.GLOBALS.buffers.worldTileEntityProperties.data;
          var dWorldTileTextureCoords   =_seba.WEBGL.GLOBALS.buffers.worldTileTextureCoords.data;
          var currentTexture=_seba.WEBGL.GLOBALS.textures[currentWorldTile.textureId];

          var idx4=currentWorldTile.id*6*4; //2018-05-03 17:19:15 added alpha component
          var dWorldTileEntityPropertiesIdx=currentWorldTile.id*6*_seba.WEBGL.WORLD.ENTITIES.GLOBALS.totalProperties;
          var dWorldTileTextureCoordsIdx=currentWorldTile.id*6*2;
          //vertex colors (3 points per vertex)
          dWorldTileSpriteColors[idx4+0]=currentWorldTile.color1[0];
          dWorldTileSpriteColors[idx4+1]=currentWorldTile.color1[1];
          dWorldTileSpriteColors[idx4+2]=currentWorldTile.color1[2];
          dWorldTileSpriteColors[idx4+3]=currentWorldTile.opacity;

          dWorldTileSpriteColors[idx4+4]=currentWorldTile.color1[0];
          dWorldTileSpriteColors[idx4+5]=currentWorldTile.color1[1];
          dWorldTileSpriteColors[idx4+6]=currentWorldTile.color1[2];
          dWorldTileSpriteColors[idx4+7]=currentWorldTile.opacity;

          dWorldTileSpriteColors[idx4+8]=currentWorldTile.color1[0];
          dWorldTileSpriteColors[idx4+9]=currentWorldTile.color1[1];
          dWorldTileSpriteColors[idx4+10]=currentWorldTile.color1[2];
          dWorldTileSpriteColors[idx4+11]=currentWorldTile.opacity;

          dWorldTileSpriteColors[idx4+12]=currentWorldTile.color1[0];
          dWorldTileSpriteColors[idx4+13]=currentWorldTile.color1[1];
          dWorldTileSpriteColors[idx4+14]=currentWorldTile.color1[2];
          dWorldTileSpriteColors[idx4+15]=currentWorldTile.opacity;

          dWorldTileSpriteColors[idx4+16]=currentWorldTile.color1[0];
          dWorldTileSpriteColors[idx4+17]=currentWorldTile.color1[1];
          dWorldTileSpriteColors[idx4+18]=currentWorldTile.color1[2];
          dWorldTileSpriteColors[idx4+19]=currentWorldTile.opacity;

          dWorldTileSpriteColors[idx4+20]=currentWorldTile.color1[0];
          dWorldTileSpriteColors[idx4+21]=currentWorldTile.color1[1];
          dWorldTileSpriteColors[idx4+22]=currentWorldTile.color1[2];
          dWorldTileSpriteColors[idx4+23]=currentWorldTile.opacity;

          // simply copy the the selected texture coordinates
          // into the 'slot' of the current world tile within the buffer of texture coordinates

          dWorldTileTextureCoords[dWorldTileTextureCoordsIdx+ 0]=currentTexture.textureCoordinates[0];
          dWorldTileTextureCoords[dWorldTileTextureCoordsIdx+ 1]=currentTexture.textureCoordinates[1];
          dWorldTileTextureCoords[dWorldTileTextureCoordsIdx+ 2]=currentTexture.textureCoordinates[2];
          dWorldTileTextureCoords[dWorldTileTextureCoordsIdx+ 3]=currentTexture.textureCoordinates[3];
          dWorldTileTextureCoords[dWorldTileTextureCoordsIdx+ 4]=currentTexture.textureCoordinates[4];
          dWorldTileTextureCoords[dWorldTileTextureCoordsIdx+ 5]=currentTexture.textureCoordinates[5];
          dWorldTileTextureCoords[dWorldTileTextureCoordsIdx+ 6]=currentTexture.textureCoordinates[6];
          dWorldTileTextureCoords[dWorldTileTextureCoordsIdx+ 7]=currentTexture.textureCoordinates[7];
          dWorldTileTextureCoords[dWorldTileTextureCoordsIdx+ 8]=currentTexture.textureCoordinates[8];
          dWorldTileTextureCoords[dWorldTileTextureCoordsIdx+ 9]=currentTexture.textureCoordinates[9];
          dWorldTileTextureCoords[dWorldTileTextureCoordsIdx+10]=currentTexture.textureCoordinates[10];
          dWorldTileTextureCoords[dWorldTileTextureCoordsIdx+11]=currentTexture.textureCoordinates[11];

          dWorldTileEntityProperties[  dWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[0]; //texture X coord
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=currentTexture.layerId; //sprite layer id
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[4]; //texture X1 coord
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=0.0; //is light emitter,apply shadow attenuation,isFlippedX
          //--
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[0];
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=currentTexture.layerId;
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[4];
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=0.0;
          //--
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[0];
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=currentTexture.layerId;
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[4];
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=0.0;
          //--
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[0];
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=currentTexture.layerId;
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[4];
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=0.0;
          //--
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[0];
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=currentTexture.layerId;
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[4];
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=0.0;
          //--
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[0];
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=currentTexture.layerId;
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[4];
          dWorldTileEntityProperties[++dWorldTileEntityPropertiesIdx]=0.0;

          _seba.GLOBALS.isWorldTilesColorsDirty=1;
          _seba.GLOBALS.isWorldTilesTextureCoordsDirty=1;
          _seba.GLOBALS.isWorldTilesPropertiesDirty=1;

        },
        draw:function(now){ //proc DRAW! {green bold indent_3}

          const _seba=SEBASTIAN; //performance lookup
          //var lastTime=;
          //console.log(now,lastTime);
          var elapsed=now-_seba.GLOBALS.lastTime;
          if (elapsed<_seba.GLOBALS.fpsInterval)
            return;

          //BEGIN RENDER

          //console.log('frame',new Date());

          var delta=elapsed;
          if (delta>_seba.GLOBALS.fpsInterval) //do not allow overflow
            delta=_seba.GLOBALS.fpsInterval;

          _seba.GLOBALS.lastTime=now;

//          return;

          var oDate=new Date();

          //color timekey
          var h_key,m_key;
          var hours,minutes;
          if (_seba.GLOBALS.debugLightColors===1){ //allows setting the time manually
            var tDate=new Date();
            var unixtime=tDate.getTime();
            unixtime+=_seba.GLOBALS.timeTravel*60*1000;
            tDate.setTime(unixtime);
            if (SEBASTIAN.GLOBALS.useUTCTime){
              h_key=tDate.getUTCHours();
              m_key=tDate.getUTCMinutes();
            }else{
              h_key=tDate.getHours();
              m_key=tDate.getMinutes();
            }
            hours=h_key;
            minutes=m_key;
          }else{
            if (SEBASTIAN.GLOBALS.useUTCTime){
              h_key=oDate.getUTCHours();
              m_key=oDate.getUTCMinutes();
            }else{
              h_key=oDate.getHours();
              m_key=oDate.getMinutes();
            }
            hours=h_key;
            minutes=m_key;
          }
          //if (h_key<10)
          //  h_key='0'+h_key;
          //if (m_key<10)
          //  m_key='0'+m_key;
          var colorKey=h_key+'_'+m_key;
          var timeIdx=hours*60+minutes; //this allows us to index the day in minutes

          //lookups
          var shaderProgram                =_seba.WEBGL.GLOBALS.shaderProgram;
          var worldVertexPositionBuffer    =_seba.WEBGL.WORLD.vertexPositionBuffer;
          var worldVertexTextureCoordBuffer=_seba.WEBGL.WORLD.vertexTextureCoordBuffer;
          var gl                           =_seba.GLOBALS.webgl_ctx; //lookup var
          var pitch                        =_seba.WEBGL.WORLD.GLOBALS.pitch;
          var yaw                          =_seba.WEBGL.WORLD.GLOBALS.yaw;
          var pMatrix                      =_seba.WEBGL.WORLD.GLOBALS.pMatrix;
          var mvMatrix                     =_seba.WEBGL.WORLD.GLOBALS.mvMatrix;
          var xPos                         =_seba.WEBGL.WORLD.GLOBALS.xPos;
          var yPos                         =_seba.WEBGL.WORLD.GLOBALS.yPos;
          var zPos                         =_seba.WEBGL.WORLD.GLOBALS.zPos;
          var degToRad                     =_seba.UTILS.degToRad;
          var textures                     =_seba.WEBGL.textures;
          var worldData                    =_seba.GLOBALS.worldData;
          var entitiesTotalProperties      =_seba.WEBGL.WORLD.ENTITIES.GLOBALS.totalProperties;

          if (_seba.GLOBALS.lastTimeKey===null || _seba.GLOBALS.lastTimeKey!==colorKey){
            //console.log(tDate);
            //update time globals
            _seba.GLOBALS.hours=h_key;//tDate.getHours();
            _seba.GLOBALS.minutes=m_key;//tDate.getMinutes();
            //refresh clock
            var c_h_key=tDate.getHours();
            var c_m_key=tDate.getMinutes();
            if (c_h_key<10)
              c_h_key='0'+c_h_key;
            if (c_m_key<10)
              c_m_key='0'+c_m_key;
            SEBASTIAN.GLOBALS.clock.textContent=c_h_key+':'+c_m_key;
            //map new colors
            //console.log('map color',colorKey,SEBASTIAN.GLOBALS.lightColorTimeMap[colorKey].idx);

            var brotherSun=worldData.entitiesNamePointer['sun'];
            var sisterMoon=worldData.entitiesNamePointer['moon'];
            var centerX=0;
            var centerY=-25; //let's move the moon and the sun down a little bit so that they are more visible in our horizon
            var circleRadius=120;
            var distance=1440; //number of minutes in a day, corresponds to our time keys.
                               //We want our stars (star and satellite to be precise)
                               //to describe a circle based on the current minute.
                               //I want the sun to be high at midday and low at midnight.
                               //for the moon I want the exact opposite, ergo set a distance of 720 minutes between them
                               //720 minutes = half of the minutes in a day
            //console.log(Math.acos(1-(Math.pow(distance/circleRadiusX,2)/2)));
            //arccos( 1-(d/r)^2/2 )
            //console.log(distance/circleRadiusX,2);
            //https://stackoverflow.com/questions/17384663/canvas-move-object-in-circle
            //https://www.safaribooksonline.com/library/view/html5-canvas/9781449308032/ch05s03.html
            //console.log(Math.pow(distance/circleRadiusX,2)/2);
            //console.log(Math.acos(1-Math.pow(distance/circleRadiusX,2)/2));
            //console.log(colorKey);
            var angle1=((SEBASTIAN.GLOBALS.lightColorTimeMap[colorKey].idx+720)*Math.PI/(360*2));
            var angle2=((SEBASTIAN.GLOBALS.lightColorTimeMap[colorKey].idx)*Math.PI/(360*2));
            //console.log();
            brotherSun.x=centerX+Math.sin(angle1)*circleRadius;
            brotherSun.y=centerY+Math.cos(angle1)*circleRadius;
            brotherSun.angle=angle1; //used to simplify light position calculations
            //brotherSun.z=-(centerY+Math.cos(angle)*circleRadius);
            //I would say to put an opacity to the moon in order to make it disappear when it's sunny...
            sisterMoon.x=centerX+Math.sin(angle2)*circleRadius;
            sisterMoon.y=centerY+Math.cos(angle2)*circleRadius;
            _seba.WEBGL.WORLD.ENTITIES.syncBuffersData(brotherSun);
            _seba.WEBGL.WORLD.ENTITIES.syncBuffersData(sisterMoon);

            //console.log((2 * Math.PI/60)*oDate.getSeconds());
            //ctx.rotate(((2 * Math.PI) / 60) * oDate.getSeconds() + ((2 * Math.PI) / 60000) * oDate.getMilliseconds());

            _seba.WEBGL.WORLD.GLOBALS.ambientLightColor=SEBASTIAN.GLOBALS.lightColorTimeMap[colorKey].a;
            //_seba.WEBGL.WORLD.GLOBALS.skyLightColor=SEBASTIAN.GLOBALS.lightColorTimeMap[colorKey].a;
            document.body.style.background=SEBASTIAN.GLOBALS.lightColorTimeMap[colorKey].g;
            _seba.GLOBALS.lastTimeKey=colorKey;
            //the position of the light has changed
            _seba.WEBGL.WORLD.GLOBALS.isPointLightPositionDirty=1;
            //the color of the light has changed
            _seba.WEBGL.WORLD.GLOBALS.isAmbientLightDirty=1;
          }

          //read the mouse coordinates from the html
          var mx=_seba.GLOBALS.mx;
          var my=_seba.GLOBALS.my;
          var oMx=_seba.GLOBALS.oMx;
          var oMy=_seba.GLOBALS.oMy;

          if (worldVertexTextureCoordBuffer===null || worldVertexPositionBuffer===null)
            return;

          if (_seba.WEBGL.WORLD.GLOBALS.isViewportDirty){ //browser window resized
            // we need to calculate the perspective
            // only if the viewport is resized
            gl.viewport(0,0,gl.viewportWidth,gl.viewportHeight);
            mat4.perspective(pMatrix,45,gl.viewportWidth/gl.viewportHeight,0.1,200.0);
            gl.uniformMatrix4fv(shaderProgram.uPMatrix_var,false,pMatrix); //apply global projection matrix
            _seba.WEBGL.WORLD.GLOBALS.isViewportDirty=0;
          }

          if (_seba.WEBGL.WORLD.GLOBALS.isCameraPositionDirty){ // modified camera position
            gl.uniform3f(shaderProgram.uCameraTranslation_var,xPos,yPos,zPos); // pass the position of the camera to the vertex shader
            _seba.WEBGL.WORLD.GLOBALS.isCameraPositionDirty=0;

            // I have to perform entity position analysis and determine who is visible
            // 0 = I do not care to reorder on z
            // 1= I'm interested in determining visibility
            // 0= I do not care to know who is under the mouse

            _seba.WEBGL.WORLD.ENTITIES.checkEntitiesDepthVisibilityMousePos(0,1,0);
          }

          //clear the buffers
          //ref: https://stackoverflow.com/questions/19469194/why-do-we-have-to-clear-depth-buffer-in-opengl-during-rendering/19469291#19469291
          gl.clear(gl.COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT);

           // if a different rotation is applied than the one we have stored
           // Let's do some nice calculations.
          if (_seba.WEBGL.WORLD.GLOBALS.lPitch!==pitch || _seba.WEBGL.WORLD.GLOBALS.lYaw!==yaw){
            mat4.identity(mvMatrix);
            if (pitch!==0)
              mat4.rotate(mvMatrix,mvMatrix,degToRad(-pitch), [1, 0, 0]);
            if (yaw!==0)
              mat4.rotate(mvMatrix,mvMatrix,degToRad(-yaw)  , [0, 1, 0]);
            _seba.WEBGL.WORLD.GLOBALS.lPitch=pitch;
            _seba.WEBGL.WORLD.GLOBALS.lYaw=yaw;
            //apply global viematrix only if a rotation is applied
            gl.uniformMatrix4fv(shaderProgram.uMVMatrix_var,false,mvMatrix);
          }

          var foundTileUnderMouse=0;
          var foundEntityUnderMouse=0;
          var searchTile=0;
          var searchEntity=0;

          if (_seba.WEBGL.WORLD.GLOBALS.isPointLightPositionDirty){
            // apply light-based calculations
            // only if the position of the light has changed
            var brotherSun=worldData.entitiesNamePointer['sun'];
            var sisterMoon=worldData.entitiesNamePointer['moon'];

            // change the vertical position of the light based on the time of day,
            // this allows us, for example, to create the silhouette effect during sunset or sunrise
            var sunLightVerticalDistance=_seba.WEBGL.WORLD.GLOBALS.sunLightVerticalDistance;

            //sunset
            sunLightVerticalDistance=SEBASTIAN.UTILS.applyTimeCalc({
              parameter:sunLightVerticalDistance,
              currentHour:hours,currentMinute:minutes,
              fromHours :17,fromMinutes:45,
              toHours   :18,toMinutes  :15,
              fromValue:SEBASTIAN.WEBGL.WORLD.GLOBALS.sunLightVerticalDistance,
              toValue  :0,
            });
            //from after sunset to midnight
            sunLightVerticalDistance=SEBASTIAN.UTILS.applyTimeCalc({
              //logInfo:1,
              applyTimeBouncing:0,
              parameter:sunLightVerticalDistance,
              currentHour:hours,currentMinute:minutes,
              fromHours :18,fromMinutes:16,
              toHours   :23 ,toMinutes :59,
              fromValue:SEBASTIAN.WEBGL.WORLD.GLOBALS.sunLightVerticalDistance,
              toValue  :-1600,
            });
            // from midnight to dawn
            sunLightVerticalDistance=SEBASTIAN.UTILS.applyTimeCalc({
              //logInfo:1,
              applyTimeBouncing:0,
              parameter:sunLightVerticalDistance,
              currentHour:hours,currentMinute:minutes,
              fromHours :0,fromMinutes:0,
              toHours   :5 ,toMinutes :44,
              fromValue:-1600,
              toValue  :-400,
            });
            //Sunrise
            sunLightVerticalDistance=SEBASTIAN.UTILS.applyTimeCalc({
              applyTimeBouncing:0,
              parameter:sunLightVerticalDistance,
              currentHour:hours,currentMinute:minutes,
              fromHours :5,fromMinutes:45,
              toHours   :6,toMinutes  :15,
              fromValue:-400,
              toValue  :SEBASTIAN.WEBGL.WORLD.GLOBALS.sunLightVerticalDistance,
            });

            //MOON

            var moonLightVerticalDistance=-300;
            moonLightVerticalDistance=SEBASTIAN.UTILS.applyTimeCalc({
              //logInfo:1,
              applyTimeBouncing:0,
              parameter:moonLightVerticalDistance,
              currentHour:hours,currentMinute:minutes,
              fromHours :17,fromMinutes:16,
              toHours   :23 ,toMinutes :59,
              fromValue:30,
              toValue  :150,
            });
            //from midnight to before dawn
            moonLightVerticalDistance=SEBASTIAN.UTILS.applyTimeCalc({
              //logInfo:1,
              applyTimeBouncing:0,
              parameter:moonLightVerticalDistance,
              currentHour:hours,currentMinute:minutes,
              fromHours :0,fromMinutes:0,
              toHours   :5 ,toMinutes :44,
              fromValue:150,
              toValue  :50,
            });
            moonLightVerticalDistance=SEBASTIAN.UTILS.applyTimeCalc({
              //logInfo:1,
              applyTimeBouncing:0,
              parameter:moonLightVerticalDistance,
              currentHour:hours,currentMinute:minutes,
              fromHours :5,fromMinutes:45,
              toHours   :6 ,toMinutes :0,
              fromValue:50,
              toValue  :-300,
            });
            //the moon 'disappears' in the light of day
            var moonOpacity=0;
            moonOpacity=SEBASTIAN.UTILS.applyTimeCalc({
              //logInfo:1,
              applyTimeBouncing:0,
              parameter:moonOpacity,
              currentHour:hours,currentMinute:minutes,
              fromHours :18,fromMinutes:0,
              toHours   :21 ,toMinutes :0,
              fromValue:0,
              toValue  :1,
            });
            moonOpacity=SEBASTIAN.UTILS.applyTimeCalc({
              //logInfo:1,
              applyTimeBouncing:0,
              parameter:moonOpacity,
              currentHour:hours,currentMinute:minutes,
              fromHours :21,fromMinutes:1,
              toHours   :23,toMinutes  :59,
              fromValue:1,
              toValue  :1,
            });
            moonOpacity=SEBASTIAN.UTILS.applyTimeCalc({
              //logInfo:1,
              applyTimeBouncing:0,
              parameter:moonOpacity,
              currentHour:hours,currentMinute:minutes,
              fromHours :0,fromMinutes:0,
              toHours   :5,toMinutes  :45,
              fromValue:1,
              toValue  :0,
            });

            sisterMoon.opacity=moonOpacity;
            _seba.WEBGL.WORLD.ENTITIES.syncBuffersData(sisterMoon); //update alpha

            //console.log(moonLightVerticalDistance);
            //console.log(parseFloat(t).toFixed(2),parseFloat(brotherSun.angle).toFixed(2),sunLightVerticalPosition);
            gl.uniform3f(shaderProgram.uPointLighting1Location_var,brotherSun.x,brotherSun.y+sunLightVerticalDistance,brotherSun.z);

             // instead of lowering the intensity of the moonlight in the vertex shader
             // make sure that the light is less high than the satellite in order to illuminate less
            gl.uniform3f(shaderProgram.uPointLighting2Location_var,sisterMoon.x,sisterMoon.y+moonLightVerticalDistance,sisterMoon.z);
            _seba.WEBGL.WORLD.GLOBALS.isPointLightPositionDirty=0;
          }

          if (_seba.WEBGL.WORLD.GLOBALS.isAmbientLightDirty){
            gl.uniform3fv(shaderProgram.uAmbientLightColorIntensity_var,_seba.WEBGL.WORLD.GLOBALS.ambientLightColorIntensity); //set ambient light intensity
            gl.uniform3fv(shaderProgram.uAmbientLightColor_var,_seba.WEBGL.WORLD.GLOBALS.ambientLightColor); //set ambient light color
          }

          var drawCalls=0;

          //proc TILES {indent_4}
          //first we draw the static world, it has no transparencies
          gl.depthMask(true); //this disables alpha blending, if we were to render a texture with alphait would have a solid background

          var skippedTiles=0;

          var vertexPositions=_seba.WEBGL.GLOBALS.buffers.worldTileVertexPositions.data;
          for (var z=0,zEnd=worldData.world.length;z<zEnd;z++){
            var currentWorldTile=worldData.world[z];

            //if (z===20){
            //  console.log(vertexPositions[z*6*3+2]);
            //}

            if (_seba.WEBGL.WORLD.GLOBALS.isAmbientLightDirty){ // This must be done regardless of the visibility of the tile
              var clockKey=_seba.GLOBALS.lastTimeKey;
              var colorsPoolIdx=currentWorldTile.colorsPoolIdx;
              var chosenColor=SEBASTIAN.GLOBALS.lightColorTimeMap[clockKey][colorsPoolIdx];
              currentWorldTile.color1=chosenColor;
              currentWorldTile.oColor1=chosenColor;
              // it makes no sense to sync one tile at a time.
              // run a single call at the end
              //_seba.WEBGL.WORLD.syncTileBuffersData(currentWorldTile);
            }

            //drawCalls++;
          }

          //update the color of the tiles if necessary
          if (_seba.WEBGL.WORLD.GLOBALS.isAmbientLightDirty){
            _seba.WEBGL.WORLD.syncAllTileBuffersData();
          }

          // worldtile buffers data do not change with each drawcall
          // only in the in case something changes during editing -> update them

          gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.worldTileTextureCoords.buffer);
          if (_seba.GLOBALS.isWorldTilesTextureCoordsDirty){
            gl.bufferData(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.worldTileTextureCoords.data,gl.DYNAMIC_DRAW);
            _seba.GLOBALS.isWorldTilesTextureCoordsDirty=0;
          }
          gl.vertexAttribPointer(shaderProgram.aTextureCoord_var,2,gl.FLOAT,false,0,0);

          gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.worldTileTranslations.buffer);
          gl.vertexAttribPointer(shaderProgram.aEntityTranslation_var,3,gl.FLOAT,false,0,0);

          //replace the color buffer data once a minute (see WORLD.syncTileBuffersData)
          gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.worldTileSpriteColors.buffer);
          if (_seba.GLOBALS.isWorldTilesColorsDirty){
            gl.bufferSubData(gl.ARRAY_BUFFER,0,_seba.WEBGL.GLOBALS.buffers.worldTileSpriteColors.data);
            _seba.GLOBALS.isWorldTilesColorsDirty=0;
          }
          gl.vertexAttribPointer(shaderProgram.aEntityColor_var,4,gl.FLOAT,false,0,0);

          gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.worldTileEntityProperties.buffer);
          if (_seba.GLOBALS.isWorldTilesPropertiesDirty){
            gl.bufferSubData(gl.ARRAY_BUFFER,0,_seba.WEBGL.GLOBALS.buffers.worldTileEntityProperties.data);
            _seba.GLOBALS.isWorldTilesPropertiesDirty=0;
          }
          gl.vertexAttribPointer(shaderProgram.aEntityProperties_var,entitiesTotalProperties,gl.FLOAT,false,0,0);

          gl.bindBuffer   (gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.worldTileVertexPositions.buffer);
          if (_seba.GLOBALS.isWorldTilesVerticesPositionDirty){
            //since we allow editing of the vertices, apply them for the current drawcall if they have been modified
            gl.bufferSubData(gl.ARRAY_BUFFER,0,_seba.WEBGL.GLOBALS.buffers.worldTileVertexPositions.data); // this makes the position of the vertices dynamic, in case we use static terrain we can remove this line
            _seba.GLOBALS.isWorldTilesVerticesPositionDirty=0;
          }
          gl.vertexAttribPointer(shaderProgram.aVertexPosition_var,3,gl.FLOAT,false,0,0);

          gl.drawArrays(gl.TRIANGLES,0,_seba.WEBGL.GLOBALS.buffers.worldTileVertexPositions.count);

          //----------------------

          //proc ENTITIES {indent_4}
          //now we draw entities that have transparent PNGs as textures

          var depths=_seba.WEBGL.WORLD.ENTITIES.GLOBALS.depthSorted;

          //writing into the depth buffer is now disabled.
          gl.depthMask(false); //disabling depth mask we can have alpha objects but we have to sort them

          for (var z=0,zEnd=worldData.entities.length;z<zEnd;z++){
            var currentEntity=worldData.entities[depths[z].entityId]; // obtain the ordered entities based on depth, and we use the painter algorithm to draw them

            if (currentEntity.isHidden===1) // entities such as clusters are hidden. (in fact they are always drawn because the data is in the buffer array, but with negative x)
              continue;

            //todo: this is perhaps less expensive to do once and for all entities
            if (_seba.WEBGL.WORLD.GLOBALS.isAmbientLightDirty){
              // update color pool if necessary
              // this same routine manages the dynamic color of the single entities dDMKCWhHp4
              if (currentEntity.hasPoolColor===1){
                var clockKey=_seba.GLOBALS.lastTimeKey;
                var colorsPoolIdx=currentEntity.colorsPoolIdx;
                var chosenColor=SEBASTIAN.GLOBALS.lightColorTimeMap[clockKey][colorsPoolIdx];
                currentEntity.color1=chosenColor;
                currentEntity.oColor1=chosenColor;
                SEBASTIAN.WEBGL.WORLD.ENTITIES.syncBuffersData(currentEntity);
              }
            }

            _seba.WEBGL.WORLD.ENTITIES.animate(currentEntity.id,delta,timeIdx);

            if (currentEntity.isVisible===0) // non-visible entities are still animated at position level, but not geometry
              continue;

            _seba.WEBGL.WORLD.ENTITIES.animateGeometry(currentEntity.id,delta);
          }

          //proc reset isAmbientLightDirty flag {indent_4}
          if (_seba.WEBGL.WORLD.GLOBALS.isAmbientLightDirty){
            _seba.WEBGL.WORLD.GLOBALS.isAmbientLightDirty=0;
          }

          gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.entitiesTextureCoordsZ.buffer);
          gl.bufferSubData(gl.ARRAY_BUFFER,0,_seba.WEBGL.GLOBALS.buffers.entitiesTextureCoordsZ.data);
          gl.vertexAttribPointer(shaderProgram.aTextureCoord_var,2,gl.FLOAT,false,0,0);

          gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ.buffer);
          gl.bufferSubData(gl.ARRAY_BUFFER,0,_seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ.data);
          gl.vertexAttribPointer(shaderProgram.aEntityTranslation_var,3,gl.FLOAT,false,0,0);

          gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.entitiesSpriteColorsZ.buffer);
          gl.bufferSubData(gl.ARRAY_BUFFER,0,_seba.WEBGL.GLOBALS.buffers.entitiesSpriteColorsZ.data);
          gl.vertexAttribPointer(shaderProgram.aEntityColor_var,4,gl.FLOAT,false,0,0);

          gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.entitiesPropertiesZ.buffer);
          gl.bufferSubData(gl.ARRAY_BUFFER,0,_seba.WEBGL.GLOBALS.buffers.entitiesPropertiesZ.data);
          gl.vertexAttribPointer(shaderProgram.aEntityProperties_var,entitiesTotalProperties,gl.FLOAT,false,0,0);

          gl.bindBuffer   (gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.entitiesVertexPositionsZ.buffer);
          gl.bufferSubData(gl.ARRAY_BUFFER,0,_seba.WEBGL.GLOBALS.buffers.entitiesVertexPositionsZ.data);
          gl.vertexAttribPointer(shaderProgram.aVertexPosition_var,3,gl.FLOAT,false,0,0);

          gl.drawArrays(gl.TRIANGLES,0,_seba.WEBGL.GLOBALS.buffers.entitiesVertexPositionsZ.count);

          //It was fun. ALL DONE.
        },

        onloadHandler:function(data,callback){ //proc onloadHandler WORLD/SCENE {green indent_3}
          var gl=SEBASTIAN.GLOBALS.webgl_ctx; //lookup var
          var worldData=JSON.parse(data);
          var _seba=SEBASTIAN;
          _seba.GLOBALS.worldData=worldData;

          //console.log(worldData.entities.length);

          //proc WORLD ENTITIES {violet bold indent_4}
          for (var z=0,zEnd=worldData.entities.length;z<zEnd;z++){
            worldData.entities[z].id=z;
            worldData.entities[z].x=parseFloat(worldData.entities[z].x);
            worldData.entities[z].y=parseFloat(worldData.entities[z].y);
            worldData.entities[z].z=parseFloat(worldData.entities[z].z);
            //handle undef neeeded props
            if (typeof worldData.entities[z].isLivingBeing==='undefined')
              worldData.entities[z].isLivingBeing=0;
            if (typeof worldData.entities[z].color1==='undefined')
              worldData.entities[z].color1=[1.0,1.0,1.0];
            worldData.entities[z].oColor1=worldData.entities[z].color1; //original color1
            if (typeof worldData.entities[z].speed==='undefined'){
              worldData.entities[z].speed=0;
            }

            worldData.entities[z].oTextureId=worldData.entities[z].textureId; // save the original texture without animations, we need it to save the scene
            // init of the entities read from file
            SEBASTIAN.WEBGL.WORLD.ENTITIES.define(z);
            //SEBASTIAN.WEBGL.WORLD.dynamicVertexTextureCoordBuffers[z]=worldVertexTextureCoordBuffer;
          }

          // after the define cycle we have the actual number of existing entities
          //console.log(worldData.entities.length);

          //init buffers
          _seba.WEBGL.GLOBALS.buffers.entitiesVertexPositions={};

          // create buffers for Z sorting
          // the vertex coordinate buffer exists in two forms:
          // not ordered and ordered in z
          // for the representation of the entities we use the z-sorted buffer
          _seba.WEBGL.GLOBALS.buffers.entitiesTextureCoordsZ={
            buffer:gl.createBuffer(),
            data:new Float32Array(worldData.entities.length * 6 * 2)
          };
          //--
          _seba.WEBGL.GLOBALS.buffers.entitiesVertexPositionsZ={ // identical to entitiesVertexPositions but sorted into z, we keep the unordered buffer to access the original coordinates of the entities
            buffer:gl.createBuffer(),
            data:new Float32Array(worldData.entities.length * 6 * 3),
            count:6*worldData.entities.length
          };
          //--
          _seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ={
            buffer:gl.createBuffer(),
            data:new Float32Array(worldData.entities.length * 6 * 3)
          };
          //--
          _seba.WEBGL.GLOBALS.buffers.entitiesSpriteColorsZ={
            buffer:gl.createBuffer(),
            data:new Float32Array(worldData.entities.length * 6 * 4)
          };
          //--
          _seba.WEBGL.GLOBALS.buffers.entitiesPropertiesZ={
            buffer:gl.createBuffer(),
            data:new Float32Array(worldData.entities.length * 6 * _seba.WEBGL.WORLD.ENTITIES.GLOBALS.totalProperties) //3 proprietà per ogni entity
          }

          //
          var bEntitiesVertexPositions=_seba.WEBGL.GLOBALS.buffers.entitiesVertexPositions;
          bEntitiesVertexPositions.data=new Float32Array(worldData.entities.length * 6 * 3);

          //fill entities buffer data
          var bEntitiesVertexPositionsIdx=0;
          for (var z=0,zEnd=worldData.entities.length;z<zEnd;z++){
            var currentEntity=worldData.entities[z];

            var currentTexture=_seba.WEBGL.GLOBALS.textures[currentEntity.oTextureId];
            if (typeof currentTexture==='undefined'){
              currentTexture=_seba.WEBGL.GLOBALS.textures['pavement_01'];
              console.log(currentEntity.oTextureId,'->','pavement_01');
            }

            if (currentEntity.isStanding===1){
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+0]=0.0*currentEntity.scale*currentTexture.scaleX;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+1]=1.0*currentEntity.scale*currentTexture.scaleY;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+2]=0.0;

              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+3]=0.0*currentEntity.scale*currentTexture.scaleX;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+4]=0.0*currentEntity.scale*currentTexture.scaleY;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+5]=0.0;

              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+6]=1.0*currentEntity.scale*currentTexture.scaleX;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+7]=0.0*currentEntity.scale*currentTexture.scaleY;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+8]=0.0;

              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+9] =0.0*currentEntity.scale*currentTexture.scaleX;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+10]=1.0*currentEntity.scale*currentTexture.scaleY;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+11]=0.0;

              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+12]=1.0*currentEntity.scale*currentTexture.scaleX;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+13]=1.0*currentEntity.scale*currentTexture.scaleY;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+14]=0.0;

              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+15]=1.0*currentEntity.scale*currentTexture.scaleX;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+16]=0.0*currentEntity.scale*currentTexture.scaleY;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+17]=0.0;

              //if (currentEntity.oTextureId.indexOf('grass')!==-1){
              //  console.log(currentEntity.oTextureId,currentEntity.scale,currentTexture.scaleX);
              //}

            }else{ //entities such as water

              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+0]=0.0;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+1]=0.0;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+2]=0.0;

              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+3]=0.0;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+4]=0.0;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+5]=1.0*currentEntity.scale*currentTexture.scaleY;

              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+6]=1.0*currentEntity.scale*currentTexture.scaleX;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+7]=0.0;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+8]=1.0*currentEntity.scale*currentTexture.scaleY;;

              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+9] =0.0;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+10]=0.0;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+11]=0.0;

              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+12]=1.0*currentEntity.scale*currentTexture.scaleX;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+13]=0.0;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+14]=0.0;

              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+15]=1.0*currentEntity.scale*currentTexture.scaleX;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+16]=0.0;
              bEntitiesVertexPositions.data[bEntitiesVertexPositionsIdx+17]=1.0*currentEntity.scale*currentTexture.scaleY;
            }

            bEntitiesVertexPositionsIdx+=18;

          }

          //Once the buffers are created, sort them by the z by position
          //of the entities
          SEBASTIAN.WEBGL.WORLD.ENTITIES.sortZBuffersByEntityPosition();

          //proc WORLD TILES {violet bold indent_4}

          //proc PARSE WORLD FILE > STATIC VERTEX DATA {yellow indent_4}
          //---------
          //STATIC WORLD DATA
          //vertices that can not move
          //--

          var wordlTileColumns=SEBASTIAN.WEBGL.WORLD.GLOBALS.columns;
          var wordlTileTotalTiles=wordlTileColumns*SEBASTIAN.WEBGL.WORLD.GLOBALS.rows;
          var offsetX=0;
          var offsetZ=0;
          var tileScale=1;
          var currentTexture; //pointer to the texture applied to the tile

          //2018-03-22 13:13:51 add dynamic colors to world tiles
          var tileDefaultPoolColor='C1'; //proc default tile color {violet indent_5}
          //--

          var h_key=SEBASTIAN.GLOBALS.hours;//=tDate.getUTCHours();
          var m_key=SEBASTIAN.GLOBALS.minutes;//=tDate.getUTCMinutes();
          var colorKey=h_key+'_'+m_key;
          var colorsPoolIdx='p'+tileDefaultPoolColor;
          var chosenTileColor=SEBASTIAN.GLOBALS.lightColorTimeMap[colorKey][colorsPoolIdx];

          var counter=0;
          var initOffsetX=offsetX;
          if (typeof worldData.world==='undefined')
            worldData.world=[];

          //init buffers
          _seba.WEBGL.GLOBALS.buffers.worldTileTextureCoords={};
          _seba.WEBGL.GLOBALS.buffers.worldTileVertexPositions={};
          _seba.WEBGL.GLOBALS.buffers.worldTileTranslations={};
          _seba.WEBGL.GLOBALS.buffers.worldTileSpriteColors={};
          _seba.WEBGL.GLOBALS.buffers.worldTileEntityProperties={};
          var bWorldTileTextureCoords=_seba.WEBGL.GLOBALS.buffers.worldTileTextureCoords;
          var bWorldTileVertexPositions=_seba.WEBGL.GLOBALS.buffers.worldTileVertexPositions;
          var bWorldTileTranslations=_seba.WEBGL.GLOBALS.buffers.worldTileTranslations;
          var bWorldTileSpriteColors=_seba.WEBGL.GLOBALS.buffers.worldTileSpriteColors;
          var bWorldTileEntityProperties=_seba.WEBGL.GLOBALS.buffers.worldTileEntityProperties;
          //--
          var bWorldTileTextureCoordsIdx=0;
          var bWorldTileVertexPositionsIdx=0;
          var bWorldTileTranslationsIdx=0;
          var bWorldTileSpriteColorsIdx=0;
          var bWorldTileEntityPropertiesIdx=0;

          // for textures we need two coordinates for each vertex of the two triangles
          // the two triangles make 6 vertices in total
          bWorldTileTextureCoords.data=new Float32Array(wordlTileTotalTiles * 6 * 2);
          //for the buffer of the vertices we need 3 coordinates for each vertex of the two triangles
          //then 3 coordinates (x, y, z) for each of the 6 vertices = 6 * 3
          bWorldTileVertexPositions.data=new Float32Array(wordlTileTotalTiles * 6 * 3);
          bWorldTileTranslations.data=new Float32Array(wordlTileTotalTiles * 6 * 3);
          bWorldTileSpriteColors.data=new Float32Array(wordlTileTotalTiles * 6 * 4); //2018-05-03 16:48:33 added alpha r,g,b,a
          bWorldTileEntityProperties.data=new Float32Array(wordlTileTotalTiles * 6 * _seba.WEBGL.WORLD.ENTITIES.GLOBALS.totalProperties);

          if (SEBASTIAN.GLOBALS.loadSavedScene){
            //load the scene.json file

            for (var z=0,zEnd=worldData.world.length;z<zEnd;z++){
              var currentWorldTile=worldData.world[z];
              //handle undef neeeded props
              if (typeof currentWorldTile.color1==='undefined')
                currentWorldTile.color1=[1.0,1.0,1.0];
              currentWorldTile.oColor1=currentWorldTile.color1; //original color
              currentWorldTile.oTextureId=currentWorldTile.textureId; //original texture so we are able able to change it during selection and saving
            }

            //set camera from scene
            if (typeof worldData.camera!=='undefined'){
              SEBASTIAN.WEBGL.WORLD.GLOBALS.yPos=worldData.camera.yPos;
              SEBASTIAN.WEBGL.WORLD.GLOBALS.xPos=worldData.camera.xPos;
              SEBASTIAN.WEBGL.WORLD.GLOBALS.zPos=worldData.camera.zPos;
              SEBASTIAN.WEBGL.WORLD.GLOBALS.pitch=worldData.camera.pitch;
              SEBASTIAN.WEBGL.WORLD.GLOBALS.yaw=worldData.camera.yaw;
            }

            //restore vertices position into the typed Array
            bWorldTileVertexPositions.data.set(worldData.worldTilesVertices);
          }

          for (var z=0;z<wordlTileTotalTiles;z++){

            //set default tile data
            if (typeof worldData.world[z]==='undefined')
              worldData.world[z]={};

            var currentWorldTile=worldData.world[z];

            currentWorldTile.id=z;

            if (!SEBASTIAN.GLOBALS.loadSavedScene){

              currentWorldTile.color1=[1.0,1.0,1.0];
              currentWorldTile.opacity=1.0;
              currentWorldTile.oColor1=worldData.world[z].color1;
              currentWorldTile.textureId='pavement_01';
              currentWorldTile.oTextureId=worldData.world[z].textureId; //saved so we can 'select' a tile by switching textures
              currentWorldTile.colorsPoolIdx=colorsPoolIdx; //set colorPool index
              currentTexture=_seba.WEBGL.GLOBALS.textures[currentWorldTile.textureId];

              //vertices         texture coordinates
              // 0.0  0.0  0.0   0.0  1.0   //a  0, 1, 2
              // 0.0  0.0  1.0   0.0  0.0   //b  3, 4, 5
              // 1.0  0.0  1.0   1.0  0.0   //c  6, 7, 8
              //
              // 0.0  0.0  0.0   0.0  1.0   //d  9,10,11
              // 1.0  0.0  0.0   1.0  1.0   //e 12,13,14
              // 1.0  0.0  1.0   1.0  0.0   //f 15,16,17

              //vertexPositions
              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+0]=0.0*tileScale*currentTexture.scaleX+offsetX;
              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+1]=0.0;
              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+2]=0.0*tileScale*currentTexture.scaleY+offsetZ;

              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+3]=0.0*tileScale*currentTexture.scaleX+offsetX;
              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+4]=0.0;
              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+5]=1.0*tileScale*currentTexture.scaleY+offsetZ;

              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+6]=1.0*tileScale*currentTexture.scaleX+offsetX;
              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+7]=0.0;
              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+8]=1.0*tileScale*currentTexture.scaleY+offsetZ;

              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+9]=0.0*tileScale*currentTexture.scaleX+offsetX;
              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+10]=0.0;
              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+11]=0.0*tileScale*currentTexture.scaleY+offsetZ;

              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+12]=1.0*tileScale*currentTexture.scaleX+offsetX;
              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+13]=0.0;
              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+14]=0.0*tileScale*currentTexture.scaleY+offsetZ;

              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+15]=1.0*tileScale*currentTexture.scaleX+offsetX;
              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+16]=0.0;
              bWorldTileVertexPositions.data[bWorldTileVertexPositionsIdx+17]=1.0*tileScale*currentTexture.scaleY+offsetZ;
              bWorldTileVertexPositionsIdx+=18;
            }else{
              //if we do not load a scene, we just need to set the current texture
              currentTexture=_seba.WEBGL.GLOBALS.textures[currentWorldTile.textureId];
            }

            //copy the coordinates of the selected texture into
            //the current world tile slot in the texture coordinate buffer
            bWorldTileTextureCoords.data[bWorldTileTextureCoordsIdx+ 0]=currentTexture.textureCoordinates[0];
            bWorldTileTextureCoords.data[bWorldTileTextureCoordsIdx+ 1]=currentTexture.textureCoordinates[1];
            bWorldTileTextureCoords.data[bWorldTileTextureCoordsIdx+ 2]=currentTexture.textureCoordinates[2];
            bWorldTileTextureCoords.data[bWorldTileTextureCoordsIdx+ 3]=currentTexture.textureCoordinates[3];
            bWorldTileTextureCoords.data[bWorldTileTextureCoordsIdx+ 4]=currentTexture.textureCoordinates[4];
            bWorldTileTextureCoords.data[bWorldTileTextureCoordsIdx+ 5]=currentTexture.textureCoordinates[5];
            bWorldTileTextureCoords.data[bWorldTileTextureCoordsIdx+ 6]=currentTexture.textureCoordinates[6];
            bWorldTileTextureCoords.data[bWorldTileTextureCoordsIdx+ 7]=currentTexture.textureCoordinates[7];
            bWorldTileTextureCoords.data[bWorldTileTextureCoordsIdx+ 8]=currentTexture.textureCoordinates[8];
            bWorldTileTextureCoords.data[bWorldTileTextureCoordsIdx+ 9]=currentTexture.textureCoordinates[9];
            bWorldTileTextureCoords.data[bWorldTileTextureCoordsIdx+10]=currentTexture.textureCoordinates[10];
            bWorldTileTextureCoords.data[bWorldTileTextureCoordsIdx+11]=currentTexture.textureCoordinates[11];
            bWorldTileTextureCoordsIdx+=12;

            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 0]=0.0;
            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 1]=0.0;
            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 2]=0.0;
            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 3]=0.0;
            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 4]=0.0;
            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 5]=0.0;
            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 6]=0.0;
            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 7]=0.0;
            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 8]=0.0;
            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 9]=0.0;
            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 10]=0.0;
            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 11]=0.0;
            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 12]=0.0;
            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 13]=0.0;
            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 14]=0.0;
            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 15]=0.0;
            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 16]=0.0;
            bWorldTileTranslations.data[bWorldTileTranslationsIdx+ 17]=0.0;
            bWorldTileTranslationsIdx+=18;

            //colors

            //we do not need to specify an initial color because
            //the first draw will automatically determine the color of the tiles based
            //on their colorDynamic property
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+0 ]=1.0;//r
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+1 ]=1.0;//g
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+2 ]=1.0;//b
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+3 ]=currentWorldTile.opacity;//a

            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+4 ]=1.0;
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+5 ]=1.0;
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+6 ]=1.0;
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+7 ]=currentWorldTile.opacity;

            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+8 ]=1.0;
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+9 ]=1.0;
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+10 ]=1.0;
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+11 ]=currentWorldTile.opacity;

            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+12]=1.0;
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+13]=1.0;
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+14]=1.0;
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+15]=currentWorldTile.opacity;

            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+16]=1.0;
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+17]=1.0;
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+18]=1.0;
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+19]=currentWorldTile.opacity;

            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+20]=1.0;
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+21]=1.0;
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+22]=1.0;
            bWorldTileSpriteColors.data[bWorldTileSpriteColorsIdx+23]=currentWorldTile.opacity;
            bWorldTileSpriteColorsIdx+=24;

            //proc TILES.PROPERTIES.FLAGS {violet bold indent_4}
            bWorldTileEntityProperties.data[  bWorldTileEntityPropertiesIdx]=currentTexture.oX;  //texture coord x
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=currentTexture.layerId; //sprite layer id
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[4]; //texture coord x1
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=0.0; //is light emitter, apply shadow attenuation, is x flipped
            //--
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=currentTexture.oX;
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=currentTexture.layerId;
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[4];
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=0.0;
            //--
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=currentTexture.oX;
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=currentTexture.layerId;
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[4];
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=0.0;
            //--
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=currentTexture.oX;
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=currentTexture.layerId;
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[4];
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=0.0;
            //--
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=currentTexture.oX;
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=currentTexture.layerId;
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[4];
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=0.0;
            //--
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=currentTexture.oX;
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=currentTexture.layerId;
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=currentTexture.textureCoordinates[4];
            bWorldTileEntityProperties.data[++bWorldTileEntityPropertiesIdx]=0.0;
            bWorldTileEntityPropertiesIdx++;

            counter++;
            offsetX+=tileScale*1;
            if (counter%wordlTileColumns===0){
              offsetX=initOffsetX;
              offsetZ+=tileScale;
            }

          }

          //create the buffers for the worldtiles

          _seba.WEBGL.GLOBALS.buffers.worldTileTextureCoords.buffer=gl.createBuffer();
          gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.worldTileTextureCoords.buffer);
          gl.bufferData(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.worldTileTextureCoords.data,gl.DYNAMIC_DRAW);

          _seba.WEBGL.GLOBALS.buffers.worldTileVertexPositions.buffer=gl.createBuffer();
          gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.worldTileVertexPositions.buffer);
          gl.bufferData(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.worldTileVertexPositions.data,gl.DYNAMIC_DRAW);
          _seba.WEBGL.GLOBALS.buffers.worldTileVertexPositions.count=wordlTileTotalTiles*6;
          //console.log(_seba.WEBGL.GLOBALS.buffers.worldTileVertexPositions.data,wordlTileTotalTiles);

          _seba.WEBGL.GLOBALS.buffers.worldTileSpriteColors.buffer=gl.createBuffer();
          gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.worldTileSpriteColors.buffer);
          gl.bufferData(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.worldTileSpriteColors.data,gl.DYNAMIC_DRAW);

          _seba.WEBGL.GLOBALS.buffers.worldTileEntityProperties.buffer=gl.createBuffer();
          gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.worldTileEntityProperties.buffer);
          gl.bufferData(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.worldTileEntityProperties.data,gl.DYNAMIC_DRAW);

          //the position of the world tiles never change
          //so set the buffer data just once
          _seba.WEBGL.GLOBALS.buffers.worldTileTranslations.buffer=gl.createBuffer();
          gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.worldTileTranslations.buffer);
          gl.bufferData(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.worldTileTranslations.data,gl.STATIC_DRAW); //2018-02-22 08:58:06 opz. avevo messo worldTileVertexPositions al posto di worldTileTranslations e tutti i tile venivano ovviamente cannati....

          callback();
        },

        ENTITIES:{ //proc ENTITIES {orange indent_3}
          GLOBALS:{ //proc WEBGL.WORLD.ENTITIES.GLOBALS {orange indent_4}
            depthSorted:[], //array with list of entity ids sorted by camera to be drawn with the painter's algorithm
            //NOTE: it is not possible to have a vertextattibute with dim greather than 4 {indent_4}
            //      ref: http://docs.gl/gl3/glVertexAttribPointer -> errors

            totalProperties:4,
          },
          init:function(){ //proc init {green indent_4}
            var gl=SEBASTIAN.GLOBALS.webgl_ctx; //lookup var
            //define a polygon -> vertical 1x1 square at offset 0,0
            var vertexPositions=[
               0.0,  1.0,  0.0 ,
               0.0,  0.0,  0.0 ,
               1.0,  0.0,  0.0 ,

               0.0,  1.0,  0.0 ,
               1.0,  1.0,  0.0 ,
               1.0,  0.0,  0.0
            ];
            var vertexTextureCoords=[
               0.0, 1.0,
               0.0, 0.0,
               1.0, 0.0,

               0.0, 1.0,
               1.0, 1.0,
               1.0, 0.0
            ];
            //all entities share the same texture geometry
            SEBASTIAN.WEBGL.WORLD.ENTITIES.GLOBALS.vertexPositions=vertexPositions;
            SEBASTIAN.WEBGL.WORLD.ENTITIES.GLOBALS.vertexTextureCoords=vertexTextureCoords;
            var aVertexTextureCoords=new Float32Array(vertexTextureCoords);
            SEBASTIAN.WEBGL.WORLD.ENTITIES.GLOBALS.aVertexTextureCoords=aVertexTextureCoords;
          },

          add:function(cfg){ //proc add {green indent_4}
            cfg=cfg?cfg:{};

            var config={
              isBot:cfg.isBot || 0,
              x:cfg.x || 0,
              y:cfg.y || 0,
              z:cfg.z || 0,
              rx:cfg.rx || 0,
              ry:cfg.ry || 0,
              rz:cfg.rz || 0,
              scale:cfg.scale,
              color1:cfg.color1 || [1.0,1.0,1.0],
              colorDynamic:cfg.colorDynamic || '', //2018-03-22 12:11:51 allows to color the entity with a color that varies depending on the time
              speed:cfg.speed || 0,
              textureId:cfg.textureId || 'sel_02',
              opacity:cfg.opacity || 1,
              isLivingBeing:cfg.isLivingBeing || 0,
              isLightEmitter:cfg.isLightEmitter || 0,
              hasShadowMitigation:cfg.hasShadowMitigation || 0,
              attachToCurrentTile:cfg.attachToCurrentTile || 0,
              isFlippedX:cfg.isFlippedX || 0,
            };
            var _seba=SEBASTIAN;
            var worldData=_seba.GLOBALS.worldData;
            var newEntityId=worldData.entities.length;

            worldData.entities.push(config);
            worldData.entities[newEntityId].id=newEntityId;

            return newEntityId;
          },
          bootBot:function(idx,cloneIdx){ //proc bootBot {green indent_4}
            var worldData=SEBASTIAN.GLOBALS.worldData;
            var currentEntity=worldData.entities[idx];
            var resetSingleEntity=false;
            if (typeof cloneIdx!=='undefined')
              resetSingleEntity=true;

            if (typeof currentEntity.isCluster!=='undefined' && currentEntity.isCluster || resetSingleEntity){
              if (typeof currentEntity.cloneOf!=='undefined')
                currentEntity=worldData.entities[currentEntity.cloneOf]; // take the parent entity of the current clone
              var nClones=currentEntity.clusterInfo.clones;
              var ooX=currentEntity.clusterInfo.xRange[0];
              var ooY=currentEntity.clusterInfo.yRange[0];
              var ooZ=currentEntity.clusterInfo.zRange[0];
              var yStep=currentEntity.clusterInfo.yStep;
              var stepX=(currentEntity.clusterInfo.xRange[1]-currentEntity.clusterInfo.xRange[0])/nClones;
              var stepY=(currentEntity.clusterInfo.yRange[1]-currentEntity.clusterInfo.yRange[0])/nClones;
              var stepZ=(currentEntity.clusterInfo.zRange[1]-currentEntity.clusterInfo.zRange[0])/nClones;
              var xLimit=currentEntity.clusterInfo.xRange[1];
              var minSpeed=currentEntity.clusterInfo.speedRange[0];
              var maxSpeed=currentEntity.clusterInfo.speedRange[1];
              var minY=currentEntity.clusterInfo.yRange[0];
              var maxY=currentEntity.clusterInfo.yRange[1];
              var minZ=currentEntity.clusterInfo.zRange[0];
              var maxZ=currentEntity.clusterInfo.zRange[1];
              var minOpacity=currentEntity.clusterInfo.opacityRange[0];
              var maxOpacity=currentEntity.clusterInfo.opacityRange[1];
              var minScale=currentEntity.clusterInfo.scaleRange[0];
              var maxScale=currentEntity.clusterInfo.scaleRange[1];
              var behaviorId=currentEntity.clusterInfo.behaviorId;
              var clusterScatter=currentEntity.clusterInfo.scatter;
              var colors=currentEntity.clusterInfo.colors;
              var colorDynamic=currentEntity.colorDynamic;
              var isStanding=currentEntity.isStanding;

              var colorsPool=currentEntity.clusterInfo.colorsPool;
              var isLightEmitter=currentEntity.isLightEmitter;
              var isFlippedX=currentEntity.isFlippedX;
              var hasShadowMitigation=currentEntity.hasShadowMitigation;

              if (typeof clusterScatter==='undefined')
                clusterScatter=1;
              if (resetSingleEntity)
                nClones=1;
              for (var z=0,zEnd=nClones;z<zEnd;z++){
                //var textureId=currentEntity.clusterInfo.textures[Math.floor(Math.random()*currentEntity.clusterInfo.textures.length)];
                var textureId=currentEntity.clusterInfo.textures[Math.floor(Math.random()*currentEntity.clusterInfo.textures.length)];
                var speed=(Math.random() * (maxSpeed-minSpeed)) + minSpeed;
                var opacity=(Math.random() * (maxOpacity-minOpacity)) + minOpacity;
                var scale=(Math.random() * (maxScale-minScale)) + minScale;
                //console.log(speed);
                ooZ=(Math.random() * (maxZ-minZ)) + minZ;
                ooY=(Math.random() * (maxY-minY)) + minY;
                if (typeof yStep!=='undefined'){ //per il tronco dell'albero
                  if (typeof currentEntity.currentAutoY==='undefined')
                    currentEntity.currentAutoY=currentEntity.clusterInfo.yRange[0];
                  ooY=currentEntity.currentAutoY;
                  currentEntity.currentAutoY+=yStep;
                }
                var newEntityId;
                if (!resetSingleEntity){
                  newEntityId=SEBASTIAN.WEBGL.WORLD.ENTITIES.add({});
                  SEBASTIAN.GLOBALS.worldData.entities[newEntityId].cloneOf=idx;
                }else{
                  newEntityId=idx;
                }
                var theEntity=SEBASTIAN.GLOBALS.worldData.entities[newEntityId];

                theEntity.behaviorId=behaviorId;
                theEntity.isBot=1;
                theEntity.x=ooX;
                theEntity.y=ooY;
                theEntity.z=ooZ;
                theEntity.textureId=textureId;
                theEntity.oTextureId=theEntity.textureId;
                theEntity.speed=speed;
                theEntity.opacity=opacity;
                theEntity.scale=scale;
                theEntity.isIdle=0;
                theEntity.isLightEmitter=isLightEmitter;
                theEntity.hasShadowMitigation=hasShadowMitigation;
                theEntity.isFlippedX=isFlippedX;
                if (theEntity.speed!==0)
                  theEntity.xLimit=xLimit;
                if (typeof colors!=='undefined'){
                  var chosenColor=currentEntity.clusterInfo.colors[Math.floor(Math.random()*currentEntity.clusterInfo.colors.length)];
                  theEntity.color1=[chosenColor[0]/255,chosenColor[1]/255,chosenColor[2]/255,];
                }
                if (typeof colorDynamic!=='undefined'){ // 2018-03-22 15:05:03 dynamic color applied to an entire group of clones clusters

                  var h_key=SEBASTIAN.GLOBALS.hours;
                  var m_key=SEBASTIAN.GLOBALS.minutes;
                  var colorKey=h_key+'_'+m_key;
                  var colorsPoolIdx='p'+colorDynamic;
                  var chosenColor=SEBASTIAN.GLOBALS.lightColorTimeMap[colorKey][colorsPoolIdx];
                  theEntity.hasPoolColor=1;
                  theEntity.colorsPoolIdx=colorsPoolIdx;
                  theEntity.color1=chosenColor;
                }
                if (typeof colorsPool!=='undefined'){ // 2018-03-21 01:21:41 select a random color from the corresponding pool

                  var h_key=SEBASTIAN.GLOBALS.hours;
                  var m_key=SEBASTIAN.GLOBALS.minutes;
                  var colorKey=h_key+'_'+m_key;
                  //get a random value from the color pool
                  var max=5;
                  var min=1;
                  var rnd=Math.floor(Math.random() * (max - min + 1)) + min; //ref: https://stackoverflow.com/questions/1527803/generating-random-whole-numbers-in-javascript-in-a-specific-range
                  //console.log('p'+colorsPool+rnd);
                  var colorsPoolIdx='p'+colorsPool+rnd;
                  var chosenColor=SEBASTIAN.GLOBALS.lightColorTimeMap[colorKey][colorsPoolIdx];
                  theEntity.hasPoolColor=1;
                  theEntity.colorsPoolIdx=colorsPoolIdx;
                  theEntity.color1=chosenColor;
                  //console.log(theEntity.color1);
                }
                theEntity.oColor1=theEntity.color1;
                //define geometry and stuff
                SEBASTIAN.WEBGL.WORLD.ENTITIES.define(newEntityId);
                if (clusterScatter){ // if 1 spread the clones around (eg clouds)
                  ooX+=stepX;
                  ooY+=stepY;
                }
              }
            }
          },
          setLivingCreatureBehaviors:function(currentEntity){  //proc setLivingBehaviors {green indent_4}

            var _seba=SEBASTIAN;
            var h_key=_seba.GLOBALS.hours;
            var m_key=_seba.GLOBALS.minutes;
            var timeIdx=h_key*60+m_key;

            var bedtime_hours=20;
            var bedtime_minutes=30;
            var waketime_hours=7;
            var waketime_minutes=30;

            //2018-04-30 19:33:25
            // add calculation taking into account daylight saving time and offset
            var ddd=new Date();
            var offsetHours=ddd.getTimezoneOffset()/60;
            bedtime_hours-=-offsetHours;
            waketime_hours-=-offsetHours;
            //console.log('bed',bedtime_hours,'wake',waketime_hours);

            var bedtime_idx =bedtime_hours*60+bedtime_minutes;
            var waketime_idx=waketime_hours*60+waketime_minutes;
            //set a random bedtime/waketime interval
            var max=20;
            var min=0;
            var rnd=Math.floor(Math.random()*(max-min+1))+min;//https://stackoverflow.com/questions/1527803/generating-random-whole-numbers-in-javascript-in-a-specific-range
            currentEntity.bedtime=bedtime_idx+rnd;
            var rnd=Math.floor(Math.random()*(max-min+1))+min;
            currentEntity.waketime=waketime_idx+rnd;
            //console.log('bedtime',currentEntity.bedtime,'waketime',currentEntity.waketime);

            if (currentEntity.isAwake===1){ //do not allow to set wake alarms on the past
              if (timeIdx<currentEntity.waketime)
                currentEntity.waketime=timeIdx-1;
            }

          },
          define:function(idx){ //proc define {green indent_4}
            var _seba=SEBASTIAN;
            var worldData=_seba.GLOBALS.worldData;
            var currentEntity=worldData.entities[idx];

            if (typeof currentEntity.isCluster!=='undefined' && currentEntity.isCluster){
              _seba.WEBGL.WORLD.ENTITIES.bootBot(idx);
              // The cluster is an invisible 'master' that is cloned
              currentEntity.isHidden=1;
              currentEntity.x=-5000; //'hide' geometry
            }else{
              // if the entity is not of type cluster,
              // create the lookup of entities by name
              //console.log(currentEntity.name);
              if (typeof currentEntity.name!=='undefined'){
                if (typeof currentEntity.isBot==='undefined' || !currentEntity.isBot){
                  if (typeof worldData.entitiesNamePointer==='undefined')
                    worldData.entitiesNamePointer={};
                  worldData.entitiesNamePointer[currentEntity.name]=worldData.entities[idx];
                  //console.log(currentEntity.name,SEBASTIAN.GLOBALS.worldData.entitiesNamePointer);
                }
              }
            }

            if (typeof currentEntity.isStanding==='undefined')
              currentEntity.isStanding=1;

            if (typeof currentEntity.isLightEmitter==='undefined')
              currentEntity.isLightEmitter=0;
            if (typeof currentEntity.hasShadowMitigation==='undefined')
              currentEntity.hasShadowMitigation=0;
            if (typeof currentEntity.isFlippedX==='undefined')
              currentEntity.isFlippedX=0;

            if (currentEntity.isLivingBeing){
              _seba.WEBGL.WORLD.ENTITIES.setLivingCreatureBehaviors(currentEntity);
            }

            if (typeof currentEntity.colorDynamic!=='undefined' && currentEntity.colorDynamic!==''){ //dDMKCWhHp4
              //var tDate=new Date();
              //var h_key=tDate.getHours();
              //var m_key=tDate.getMinutes();
              var h_key=_seba.GLOBALS.hours;
              var m_key=_seba.GLOBALS.minutes;
              var colorKey=h_key+'_'+m_key;
              var colorsPoolIdx='p'+currentEntity.colorDynamic;
              //var chosenColor=_seba.GLOBALS.lightColorTimeMap[colorKey][colorsPoolIdx];
              currentEntity.hasPoolColor=1;
              currentEntity.colorsPoolIdx=colorsPoolIdx;
            }

            var isFloatingOnWater=0;
            //proc geometry animation behavior INIT {yellow indent_5}
            switch (currentEntity.behaviorId){
              case 1: //taraxacum geometry deform (windflow-like)
                var minDeformSpeed=0.00001;
                var maxDeformSpeed=0.00003;
                var deformSpeed=(Math.random() * (maxDeformSpeed-minDeformSpeed)) + minDeformSpeed;
                currentEntity.counterDeltaIncrement=deformSpeed;
              break;
              case 2: //breath geometry deform (organic-life-like)
                var minDeformSpeed=0.00002;
                var maxDeformSpeed=0.000025;
                var breathDeformLimit=0.05;
                if (typeof currentEntity.breatheSpeed_range!=='undefined'){
                  minDeformSpeed=currentEntity.breatheSpeed_range[0];
                  maxDeformSpeed=currentEntity.breatheSpeed_range[1];
                }
                var deformSpeed=(Math.random() * (maxDeformSpeed-minDeformSpeed)) + minDeformSpeed;
                if (typeof currentEntity.breathDeformLimit!=='undefined')
                  breathDeformLimit=currentEntity.breathDeformLimit;
                currentEntity.counterDeltaIncrement=deformSpeed;
                currentEntity.breathDeformLimit=breathDeformLimit;
                if (currentEntity.isFloatingOnWater===1)
                  isFloatingOnWater=1;
              break;
              case 3: //water geometry deform (windflow-like)

                var minDeformSpeed=0.00003;
                var maxDeformSpeed=0.00005;
                var deformSpeed=(Math.random() * (maxDeformSpeed-minDeformSpeed)) + minDeformSpeed;
                //var deformSpeed=0.0001;
                currentEntity.counterDeltaIncrement=deformSpeed;
              break;
              case 4: // objects that float in the water
                isFloatingOnWater=1;
              break;
            }

            if (isFloatingOnWater){
              currentEntity.counterDeltaYIncrement=0.000012;
              currentEntity.counterYTotalDistance=0.0015;
            }

            //SEBASTIAN.GLOBALS.worldData.entities[
            //console.log(idx,SEBASTIAN.GLOBALS.worldData.entities[idx].scale);
            SEBASTIAN.WEBGL.WORLD.ENTITIES.setGeometry(idx);
          },
          checkEntitiesDepthVisibilityMousePos(updateDepth,updateVisibility,applyMouseDetection){ //proc checkEntitiesDepthVisibilityMousePos {green indent_4}
            //cycle on all entities, check their visibility,calculate their depth
            //and update the zdepth array

            // if updateDepth is 1 updates the position z of all entities

            // we need it for
            // - reorder the zbuffer if necessary
            // - determine the entity under the mouse
            // - to determine if an entity is visible (if invisible we do not animate geometry)

            var _seba=SEBASTIAN;
            var vertexPositions=_seba.WEBGL.GLOBALS.buffers.entitiesVertexPositionsZ.data;
            var worldData=_seba.GLOBALS.worldData;
            var depths=_seba.WEBGL.WORLD.ENTITIES.GLOBALS.depthSorted;
            var xPos                         =_seba.WEBGL.WORLD.GLOBALS.xPos;
            var yPos                         =_seba.WEBGL.WORLD.GLOBALS.yPos;
            var zPos                         =_seba.WEBGL.WORLD.GLOBALS.zPos;
            var pMatrix                      =_seba.WEBGL.WORLD.GLOBALS.pMatrix;
            var mvMatrix                     =_seba.WEBGL.WORLD.GLOBALS.mvMatrix;
            var gl                           =_seba.GLOBALS.webgl_ctx; //lookup var
            var mx=_seba.GLOBALS.mx;
            var my=_seba.GLOBALS.my;

            var skippedEntities=0;
            var foundEntityUnderMouse=0;

            for (var z=0,zEnd=worldData.entities.length;z<zEnd;z++){
              var currentEntity=worldData.entities[z];

              if (updateVisibility){
                currentEntity.isVisible=1;

                var idx=z*6*3; //the entities have ids based on their creation
                if (vertexPositions[idx+2]+currentEntity.z>zPos){
                  currentEntity.isVisible=0;
                  skippedEntities++;
                }else{
                  var aa=_seba.WEBGL.project(vertexPositions[idx+0],vertexPositions[idx+1],vertexPositions[idx+2],xPos,yPos,zPos,currentEntity.x,currentEntity.y,currentEntity.z,pMatrix,mvMatrix);
                  var ff=_seba.WEBGL.project(vertexPositions[idx+15],vertexPositions[idx+16],vertexPositions[idx+17],xPos,yPos,zPos,currentEntity.x,currentEntity.y,currentEntity.z,pMatrix,mvMatrix);
                  if (ff[0]<-100 || aa[0]>gl.viewportWidth+100 || ff[1]<-100 || aa[1]>gl.viewportHeight+100)
                    currentEntity.isVisible=0;
                }

                if (applyMouseDetection && _seba.GLOBALS.isMouseDirty && currentEntity.isVisible){
                  var ee=_seba.WEBGL.project(vertexPositions[idx+12],vertexPositions[idx+13],vertexPositions[idx+14],xPos,yPos,zPos,currentEntity.x,currentEntity.y,currentEntity.z,pMatrix,mvMatrix);
                  if (mx>=aa[0] && mx<ee[0]){
                    if (my>=aa[1] && my<ff[1]){
                      foundEntityUnderMouse=1;
                      currentEntity.color1=_seba.GLOBALS.entitySelectionColor;
                      _seba.WEBGL.WORLD.ENTITIES.syncBuffersData(currentEntity); // since we have changed a property we have to update the buffers
                      _seba.GLOBALS.currentEntityId=z;
                      _seba.GLOBALS.isMouseDirty=0;
                    }
                  }
                }
              }

              if (updateDepth){
                depths[currentEntity.id]={

                  //make a calculation to determine the z-index of the current Entity
                  //A bit Approximate but functional. Enough for our simplified 3d simulated world.
                  //for our simulation simplified 3d
                  //depth:_seba.WEBGL.getDepth(xPos,yPos,zPos,quat4,currentEntity.x,currentEntity.y,currentEntity.z)
                  // Performance trick
                  //if we consider that we will never make particular
                  //rotations for rendering, we just need to use z as a depth factor ...

                  depth:currentEntity.z,
                  entityId:currentEntity.id
                };
              }
            }

            if (updateDepth){
              depths.sort(function(a,b){ //sort by depth, painter's algorithm.
                return a.depth-b.depth;
              });
            }

            if (applyMouseDetection)
              return [foundEntityUnderMouse,skippedEntities];
          },

          sortZBuffersByEntityPosition(){  //proc sortZBuffersByEntityPosition {green indent_4}
            //very similar to syncBuffersData but for all entities
            //after Z sorting
            var _seba=SEBASTIAN;
            var worldData=_seba.GLOBALS.worldData;
            var depths=_seba.WEBGL.WORLD.ENTITIES.GLOBALS.depthSorted;
            var gl                           =_seba.GLOBALS.webgl_ctx; //lookup var

            //we need only the z position of the entities -> exclude the rest
            _seba.WEBGL.WORLD.ENTITIES.checkEntitiesDepthVisibilityMousePos(1,0,0);

            //var dEntitiesTextureCoords=_seba.WEBGL.GLOBALS.buffers.entitiesTextureCoords.data;
            var dEntitiesVertexPositions=_seba.WEBGL.GLOBALS.buffers.entitiesVertexPositions.data;
            //var dEntitiesTranslations=_seba.WEBGL.GLOBALS.buffers.entitiesTranslations.data;
            //var dEntitiesSpriteColors=_seba.WEBGL.GLOBALS.buffers.entitiesSpriteColors.data;
            //var dEntitiesSpriteAlphas=_seba.WEBGL.GLOBALS.buffers.entitiesSpriteAlphas.data;
            //--
            var dEntitiesTextureCoordsZ=_seba.WEBGL.GLOBALS.buffers.entitiesTextureCoordsZ.data;
            var dEntitiesVertexPositionsZ=_seba.WEBGL.GLOBALS.buffers.entitiesVertexPositionsZ.data;
            var dEntitiesTranslationsZ=_seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ.data;
            var dEntitiesSpriteColorsZ=_seba.WEBGL.GLOBALS.buffers.entitiesSpriteColorsZ.data;
            //var dEntitiesSpriteAlphasZ=_seba.WEBGL.GLOBALS.buffers.entitiesSpriteAlphasZ.data;
            //var dEntitiesTextureLayerIndexesZ=_seba.WEBGL.GLOBALS.buffers.entitiesTextureLayerIndexesZ.data;
            var dEntitiesPropertiesZ=_seba.WEBGL.GLOBALS.buffers.entitiesPropertiesZ.data;

            var dEntitiesVertexPositionsZIdx=0;
            var dEntitiesTextureCoordsZIdx=0;
            var dEntitiesTranslationsZIdx=0;
            var dEntitiesSpriteColorsZIdx=0;
            //var dEntitiesSpriteAlphasZIdx=0;
            //var dEntitiesTextureLayerIndexesZIdx=0;
            var dEntitiesPropertiesZIdx=0;
            for (var z=0,zEnd=worldData.entities.length;z<zEnd;z++){
              var currentEntity=worldData.entities[depths[z].entityId]; //we obtain the ordered entities based on depth, and we use the painter algorithm to draw them
              currentEntity.depthId=z;
              //var idx0=currentEntity.id*6;
              //var idx2=currentEntity.id*6*2;
              var idx3=currentEntity.id*6*3;

              var currentTexture=_seba.WEBGL.GLOBALS.textures[currentEntity.textureId];
              if (typeof currentTexture==='undefined'){
                currentTexture=_seba.WEBGL.GLOBALS.textures['pavement_01'];
                //console.log(currentEntity.oTextureId,'->','pavement_01');
              }

              //texture coordinates (2 points per vertex)
              dEntitiesTextureCoordsZ[dEntitiesTextureCoordsZIdx+0]=currentTexture.textureCoordinates[0];
              dEntitiesTextureCoordsZ[dEntitiesTextureCoordsZIdx+1]=currentTexture.textureCoordinates[1];
              dEntitiesTextureCoordsZ[dEntitiesTextureCoordsZIdx+2]=currentTexture.textureCoordinates[2];
              dEntitiesTextureCoordsZ[dEntitiesTextureCoordsZIdx+3]=currentTexture.textureCoordinates[3];
              dEntitiesTextureCoordsZ[dEntitiesTextureCoordsZIdx+4]=currentTexture.textureCoordinates[4];
              dEntitiesTextureCoordsZ[dEntitiesTextureCoordsZIdx+5]=currentTexture.textureCoordinates[5];
              dEntitiesTextureCoordsZ[dEntitiesTextureCoordsZIdx+6]=currentTexture.textureCoordinates[6];
              dEntitiesTextureCoordsZ[dEntitiesTextureCoordsZIdx+7]=currentTexture.textureCoordinates[7];
              dEntitiesTextureCoordsZ[dEntitiesTextureCoordsZIdx+8]=currentTexture.textureCoordinates[8];
              dEntitiesTextureCoordsZ[dEntitiesTextureCoordsZIdx+9]=currentTexture.textureCoordinates[9];
              dEntitiesTextureCoordsZ[dEntitiesTextureCoordsZIdx+10]=currentTexture.textureCoordinates[10];
              dEntitiesTextureCoordsZ[dEntitiesTextureCoordsZIdx+11]=currentTexture.textureCoordinates[11];
              dEntitiesTextureCoordsZIdx+=12;
              //vertex positions (3 points per vertex)
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+0]=dEntitiesVertexPositions[idx3+0];
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+1]=dEntitiesVertexPositions[idx3+1];
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+2]=dEntitiesVertexPositions[idx3+2];
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+3]=dEntitiesVertexPositions[idx3+3];
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+4]=dEntitiesVertexPositions[idx3+4];
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+5]=dEntitiesVertexPositions[idx3+5];
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+6]=dEntitiesVertexPositions[idx3+6];
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+7]=dEntitiesVertexPositions[idx3+7];
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+8]=dEntitiesVertexPositions[idx3+8];
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+9]=dEntitiesVertexPositions[idx3+9];
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+10]=dEntitiesVertexPositions[idx3+10];
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+11]=dEntitiesVertexPositions[idx3+11];
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+12]=dEntitiesVertexPositions[idx3+12];
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+13]=dEntitiesVertexPositions[idx3+13];
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+14]=dEntitiesVertexPositions[idx3+14];
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+15]=dEntitiesVertexPositions[idx3+15];
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+16]=dEntitiesVertexPositions[idx3+16];
              dEntitiesVertexPositionsZ[dEntitiesVertexPositionsZIdx+17]=dEntitiesVertexPositions[idx3+17];
              dEntitiesVertexPositionsZIdx+=18;
              //vertex translations (3 points per vertex)
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+0]=currentEntity.x;
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+1]=currentEntity.y;
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+2]=currentEntity.z;
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+3]=currentEntity.x;
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+4]=currentEntity.y;
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+5]=currentEntity.z;
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+6]=currentEntity.x;
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+7]=currentEntity.y;
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+8]=currentEntity.z;
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+9]=currentEntity.x;
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+10]=currentEntity.y;
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+11]=currentEntity.z;
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+12]=currentEntity.x;
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+13]=currentEntity.y;
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+14]=currentEntity.z;
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+15]=currentEntity.x;
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+16]=currentEntity.y;
              dEntitiesTranslationsZ[dEntitiesTranslationsZIdx+17]=currentEntity.z;
              dEntitiesTranslationsZIdx+=18;
              //vertex colors (4 points per vertex)
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+0]=currentEntity.color1[0];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+1]=currentEntity.color1[1];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+2]=currentEntity.color1[2];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+3]=currentEntity.opacity;

              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+4]=currentEntity.color1[0];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+5]=currentEntity.color1[1];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+6]=currentEntity.color1[2];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+7]=currentEntity.opacity;

              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+8]=currentEntity.color1[0];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+9]=currentEntity.color1[1];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+10]=currentEntity.color1[2];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+11]=currentEntity.opacity;

              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+12]=currentEntity.color1[0];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+13]=currentEntity.color1[1];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+14]=currentEntity.color1[2];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+15]=currentEntity.opacity;

              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+16]=currentEntity.color1[0];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+17]=currentEntity.color1[1];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+18]=currentEntity.color1[2];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+19]=currentEntity.opacity;

              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+20]=currentEntity.color1[0];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+21]=currentEntity.color1[1];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+22]=currentEntity.color1[2];
              dEntitiesSpriteColorsZ[dEntitiesSpriteColorsZIdx+23]=currentEntity.opacity;
              dEntitiesSpriteColorsZIdx+=24;

              dEntitiesPropertiesZ[  dEntitiesPropertiesZIdx]=currentTexture.oX;
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentTexture.layerId;
              //pack flags
              //http://theinstructionlimit.com/encoding-boolean-flags-into-a-float-in-hlsl
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentTexture.textureCoordinates[4];
              //proc ENTITIES.PROPERTIES.FLAGS {violet bold indent_5}
              //     the properties are mutually exclusive
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentEntity.hasShadowMitigation+10*currentEntity.isLightEmitter+20*currentEntity.isFlippedX;
              //--
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentTexture.oX;
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentTexture.layerId;
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentTexture.textureCoordinates[4];
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentEntity.hasShadowMitigation+10*currentEntity.isLightEmitter+20*currentEntity.isFlippedX;
              //--
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentTexture.oX;
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentTexture.layerId;
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentTexture.textureCoordinates[4];
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentEntity.hasShadowMitigation+10*currentEntity.isLightEmitter+20*currentEntity.isFlippedX;
              //--
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentTexture.oX;
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentTexture.layerId;
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentTexture.textureCoordinates[4];
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentEntity.hasShadowMitigation+10*currentEntity.isLightEmitter+20*currentEntity.isFlippedX;
              //--
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentTexture.oX;
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentTexture.layerId;
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentTexture.textureCoordinates[4];
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentEntity.hasShadowMitigation+10*currentEntity.isLightEmitter+20*currentEntity.isFlippedX;
              //--
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentTexture.oX;
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentTexture.layerId;
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentTexture.textureCoordinates[4];
              dEntitiesPropertiesZ[++dEntitiesPropertiesZIdx]=currentEntity.hasShadowMitigation+10*currentEntity.isLightEmitter+20*currentEntity.isFlippedX;
              dEntitiesPropertiesZIdx++;
            }

            gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.entitiesPropertiesZ.buffer);
            gl.bufferData(gl.ARRAY_BUFFER,dEntitiesPropertiesZ,gl.DYNAMIC_DRAW);

            gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.entitiesSpriteColorsZ.buffer);
            gl.bufferData(gl.ARRAY_BUFFER,dEntitiesSpriteColorsZ,gl.DYNAMIC_DRAW);

            gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ.buffer);
            gl.bufferData(gl.ARRAY_BUFFER,dEntitiesTranslationsZ,gl.DYNAMIC_DRAW);

            gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.entitiesVertexPositionsZ.buffer);
            gl.bufferData(gl.ARRAY_BUFFER,dEntitiesVertexPositionsZ,gl.DYNAMIC_DRAW);

            gl.bindBuffer(gl.ARRAY_BUFFER,_seba.WEBGL.GLOBALS.buffers.entitiesTextureCoordsZ.buffer);
            gl.bufferData(gl.ARRAY_BUFFER,dEntitiesTextureCoordsZ,gl.DYNAMIC_DRAW);

          },

          syncBuffersData:function(currentEntity){ //proc syncBuffersData {green indent_4}
            //since we have abandoned the simplicity of having a drawcall
            //for each entity I am forced to update all the buffers to keep
            //the properties of the entity coupled to the data that are used by opengl to draw it

            //we synchronize the Zbuffers according to the entity status
            var _seba=SEBASTIAN;
            var gl                           =_seba.GLOBALS.webgl_ctx; //lookup var

            var zIdx0   =currentEntity.depthId*6;
            var zIdx2   =currentEntity.depthId*6*2;
            var zIdx3   =currentEntity.depthId*6*3;
            var zIdx4   =currentEntity.depthId*6*4; //2018-05-03 17:20:45
            //console.log(_seba.WEBGL.WORLD.ENTITIES.GLOBALS.totalProperties);
            var zIdxProp=currentEntity.depthId*6*_seba.WEBGL.WORLD.ENTITIES.GLOBALS.totalProperties;

            //--
            var dEntitiesTextureCoordsZ=_seba.WEBGL.GLOBALS.buffers.entitiesTextureCoordsZ.data;
            var dEntitiesTranslationsZ=_seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ.data;
            var dEntitiesSpriteColorsZ=_seba.WEBGL.GLOBALS.buffers.entitiesSpriteColorsZ.data;

            var dEntitiesPropertiesZ=_seba.WEBGL.GLOBALS.buffers.entitiesPropertiesZ.data;

            var currentTexture=_seba.WEBGL.GLOBALS.textures[currentEntity.textureId];
            if (typeof currentTexture==='undefined'){
              currentTexture=_seba.WEBGL.GLOBALS.textures['pavement_01'];
              //console.log(currentEntity.oTextureId,'->','pavement_01');
            }

            dEntitiesTextureCoordsZ[zIdx2+0]=currentTexture.textureCoordinates[0];
            dEntitiesTextureCoordsZ[zIdx2+1]=currentTexture.textureCoordinates[1];
            dEntitiesTextureCoordsZ[zIdx2+2]=currentTexture.textureCoordinates[2];
            dEntitiesTextureCoordsZ[zIdx2+3]=currentTexture.textureCoordinates[3];
            dEntitiesTextureCoordsZ[zIdx2+4]=currentTexture.textureCoordinates[4];
            dEntitiesTextureCoordsZ[zIdx2+5]=currentTexture.textureCoordinates[5];
            dEntitiesTextureCoordsZ[zIdx2+6]=currentTexture.textureCoordinates[6];
            dEntitiesTextureCoordsZ[zIdx2+7]=currentTexture.textureCoordinates[7];
            dEntitiesTextureCoordsZ[zIdx2+8]=currentTexture.textureCoordinates[8];
            dEntitiesTextureCoordsZ[zIdx2+9]=currentTexture.textureCoordinates[9];
            dEntitiesTextureCoordsZ[zIdx2+10]=currentTexture.textureCoordinates[10];
            dEntitiesTextureCoordsZ[zIdx2+11]=currentTexture.textureCoordinates[11];

            //vertex translations (3 points per vertex)
            dEntitiesTranslationsZ[zIdx3+0]=currentEntity.x;
            dEntitiesTranslationsZ[zIdx3+1]=currentEntity.y;
            dEntitiesTranslationsZ[zIdx3+2]=currentEntity.z;
            dEntitiesTranslationsZ[zIdx3+3]=currentEntity.x;
            dEntitiesTranslationsZ[zIdx3+4]=currentEntity.y;
            dEntitiesTranslationsZ[zIdx3+5]=currentEntity.z;
            dEntitiesTranslationsZ[zIdx3+6]=currentEntity.x;
            dEntitiesTranslationsZ[zIdx3+7]=currentEntity.y;
            dEntitiesTranslationsZ[zIdx3+8]=currentEntity.z;
            dEntitiesTranslationsZ[zIdx3+9]=currentEntity.x;
            dEntitiesTranslationsZ[zIdx3+10]=currentEntity.y;
            dEntitiesTranslationsZ[zIdx3+11]=currentEntity.z;
            dEntitiesTranslationsZ[zIdx3+12]=currentEntity.x;
            dEntitiesTranslationsZ[zIdx3+13]=currentEntity.y;
            dEntitiesTranslationsZ[zIdx3+14]=currentEntity.z;
            dEntitiesTranslationsZ[zIdx3+15]=currentEntity.x;
            dEntitiesTranslationsZ[zIdx3+16]=currentEntity.y;
            dEntitiesTranslationsZ[zIdx3+17]=currentEntity.z;

            //vertex colors (3 points per vertex)
            dEntitiesSpriteColorsZ[zIdx4+0]=currentEntity.color1[0];
            dEntitiesSpriteColorsZ[zIdx4+1]=currentEntity.color1[1];
            dEntitiesSpriteColorsZ[zIdx4+2]=currentEntity.color1[2];
            dEntitiesSpriteColorsZ[zIdx4+3]=currentEntity.opacity;

            dEntitiesSpriteColorsZ[zIdx4+4]=currentEntity.color1[0];
            dEntitiesSpriteColorsZ[zIdx4+5]=currentEntity.color1[1];
            dEntitiesSpriteColorsZ[zIdx4+6]=currentEntity.color1[2];
            dEntitiesSpriteColorsZ[zIdx4+7]=currentEntity.opacity;

            dEntitiesSpriteColorsZ[zIdx4+8]=currentEntity.color1[0];
            dEntitiesSpriteColorsZ[zIdx4+9]=currentEntity.color1[1];
            dEntitiesSpriteColorsZ[zIdx4+10]=currentEntity.color1[2];
            dEntitiesSpriteColorsZ[zIdx4+11]=currentEntity.opacity;

            dEntitiesSpriteColorsZ[zIdx4+12]=currentEntity.color1[0];
            dEntitiesSpriteColorsZ[zIdx4+13]=currentEntity.color1[1];
            dEntitiesSpriteColorsZ[zIdx4+14]=currentEntity.color1[2];
            dEntitiesSpriteColorsZ[zIdx4+15]=currentEntity.opacity;

            dEntitiesSpriteColorsZ[zIdx4+16]=currentEntity.color1[0];
            dEntitiesSpriteColorsZ[zIdx4+17]=currentEntity.color1[1];
            dEntitiesSpriteColorsZ[zIdx4+18]=currentEntity.color1[2];
            dEntitiesSpriteColorsZ[zIdx4+19]=currentEntity.opacity;

            dEntitiesSpriteColorsZ[zIdx4+20]=currentEntity.color1[0];
            dEntitiesSpriteColorsZ[zIdx4+21]=currentEntity.color1[1];
            dEntitiesSpriteColorsZ[zIdx4+22]=currentEntity.color1[2];
            dEntitiesSpriteColorsZ[zIdx4+23]=currentEntity.opacity;

            dEntitiesPropertiesZ[  zIdxProp]=currentTexture.oX;
            dEntitiesPropertiesZ[++zIdxProp]=currentTexture.layerId;
            //proc ENTITIES.PROPERTIES.FLAGS {violet bold indent_5}
            dEntitiesPropertiesZ[++zIdxProp]=currentTexture.textureCoordinates[4];//currentEntity.isLightEmitter;
            //console.log(currentEntity.hasShadowMitigation+10*currentEntity.isLightEmitter+20*currentEntity.isFlippedX);
            dEntitiesPropertiesZ[++zIdxProp]=currentEntity.hasShadowMitigation+10*currentEntity.isLightEmitter+20*currentEntity.isFlippedX; //poors man bitwise...
            //--
            dEntitiesPropertiesZ[++zIdxProp]=currentTexture.oX;
            dEntitiesPropertiesZ[++zIdxProp]=currentTexture.layerId;
            dEntitiesPropertiesZ[++zIdxProp]=currentTexture.textureCoordinates[4];//currentEntity.isLightEmitter;
            dEntitiesPropertiesZ[++zIdxProp]=currentEntity.hasShadowMitigation+10*currentEntity.isLightEmitter+20*currentEntity.isFlippedX;
            //--
            dEntitiesPropertiesZ[++zIdxProp]=currentTexture.oX;
            dEntitiesPropertiesZ[++zIdxProp]=currentTexture.layerId;
            dEntitiesPropertiesZ[++zIdxProp]=currentTexture.textureCoordinates[4];//currentEntity.isLightEmitter;
            dEntitiesPropertiesZ[++zIdxProp]=currentEntity.hasShadowMitigation+10*currentEntity.isLightEmitter+20*currentEntity.isFlippedX;
            //--
            dEntitiesPropertiesZ[++zIdxProp]=currentTexture.oX;
            dEntitiesPropertiesZ[++zIdxProp]=currentTexture.layerId;
            dEntitiesPropertiesZ[++zIdxProp]=currentTexture.textureCoordinates[4];//currentEntity.isLightEmitter;
            dEntitiesPropertiesZ[++zIdxProp]=currentEntity.hasShadowMitigation+10*currentEntity.isLightEmitter+20*currentEntity.isFlippedX;
            //--
            dEntitiesPropertiesZ[++zIdxProp]=currentTexture.oX;
            dEntitiesPropertiesZ[++zIdxProp]=currentTexture.layerId;
            dEntitiesPropertiesZ[++zIdxProp]=currentTexture.textureCoordinates[4];//currentEntity.isLightEmitter;
            dEntitiesPropertiesZ[++zIdxProp]=currentEntity.hasShadowMitigation+10*currentEntity.isLightEmitter+20*currentEntity.isFlippedX;
            //--
            dEntitiesPropertiesZ[++zIdxProp]=currentTexture.oX;
            dEntitiesPropertiesZ[++zIdxProp]=currentTexture.layerId;
            dEntitiesPropertiesZ[++zIdxProp]=currentTexture.textureCoordinates[4];//currentEntity.isLightEmitter;
            dEntitiesPropertiesZ[++zIdxProp]=currentEntity.hasShadowMitigation+10*currentEntity.isLightEmitter+20*currentEntity.isFlippedX;

          },
          setScale:function(currentEntity){ //proc setScale {green indent_4}

            var _seba=SEBASTIAN;

            var entitiesVertPosZdata=_seba.WEBGL.GLOBALS.buffers.entitiesVertexPositionsZ.data;
            var entitiesVertPosdata=_seba.WEBGL.GLOBALS.buffers.entitiesVertexPositions.data;
            var currentEntityVertexZIndex=currentEntity.depthId*6*3;
            var currentEntityVertexIndex=currentEntity.id*6*3;
            //--
            var currentTexture=_seba.WEBGL.GLOBALS.textures[currentEntity.textureId];
            if (typeof currentTexture==='undefined'){
              currentTexture=_seba.WEBGL.GLOBALS.textures['pavement_01'];
              //console.log(currentEntity.oTextureId,'->','pavement_01');
            }
            //console.log(currentEntity.scale);

            if (currentEntity.isStanding===1){
              entitiesVertPosdata[currentEntityVertexIndex+0]=0.0*currentEntity.scale*currentTexture.scaleX;
              entitiesVertPosdata[currentEntityVertexIndex+1]=1.0*currentEntity.scale*currentTexture.scaleY;
              entitiesVertPosdata[currentEntityVertexIndex+2]=0.0;

              entitiesVertPosdata[currentEntityVertexIndex+3]=0.0*currentEntity.scale*currentTexture.scaleX;
              entitiesVertPosdata[currentEntityVertexIndex+4]=0.0*currentEntity.scale*currentTexture.scaleY;
              entitiesVertPosdata[currentEntityVertexIndex+5]=0.0;

              entitiesVertPosdata[currentEntityVertexIndex+6]=1.0*currentEntity.scale*currentTexture.scaleX;
              entitiesVertPosdata[currentEntityVertexIndex+7]=0.0*currentEntity.scale*currentTexture.scaleY;
              entitiesVertPosdata[currentEntityVertexIndex+8]=0.0;

              entitiesVertPosdata[currentEntityVertexIndex+9] =0.0*currentEntity.scale*currentTexture.scaleX;
              entitiesVertPosdata[currentEntityVertexIndex+10]=1.0*currentEntity.scale*currentTexture.scaleY;
              entitiesVertPosdata[currentEntityVertexIndex+11]=0.0;

              entitiesVertPosdata[currentEntityVertexIndex+12]=1.0*currentEntity.scale*currentTexture.scaleX;
              entitiesVertPosdata[currentEntityVertexIndex+13]=1.0*currentEntity.scale*currentTexture.scaleY;
              entitiesVertPosdata[currentEntityVertexIndex+14]=0.0;

              entitiesVertPosdata[currentEntityVertexIndex+15]=1.0*currentEntity.scale*currentTexture.scaleX;
              entitiesVertPosdata[currentEntityVertexIndex+16]=0.0*currentEntity.scale*currentTexture.scaleY;
              entitiesVertPosdata[currentEntityVertexIndex+17]=0.0;

              //--
              entitiesVertPosZdata[currentEntityVertexZIndex+0]=entitiesVertPosdata[currentEntityVertexIndex];
              entitiesVertPosZdata[currentEntityVertexZIndex+1]=entitiesVertPosdata[currentEntityVertexIndex+1];
              entitiesVertPosZdata[currentEntityVertexZIndex+2]=entitiesVertPosdata[currentEntityVertexIndex+2];

              entitiesVertPosZdata[currentEntityVertexZIndex+3]=entitiesVertPosdata[currentEntityVertexIndex+3];
              entitiesVertPosZdata[currentEntityVertexZIndex+4]=entitiesVertPosdata[currentEntityVertexIndex+4];
              entitiesVertPosZdata[currentEntityVertexZIndex+5]=entitiesVertPosdata[currentEntityVertexIndex+5];

              entitiesVertPosZdata[currentEntityVertexZIndex+6]=entitiesVertPosdata[currentEntityVertexIndex+6];
              entitiesVertPosZdata[currentEntityVertexZIndex+7]=entitiesVertPosdata[currentEntityVertexIndex+7];
              entitiesVertPosZdata[currentEntityVertexZIndex+8]=entitiesVertPosdata[currentEntityVertexIndex+8];

              entitiesVertPosZdata[currentEntityVertexZIndex+9] =entitiesVertPosdata[currentEntityVertexIndex+9];
              entitiesVertPosZdata[currentEntityVertexZIndex+10]=entitiesVertPosdata[currentEntityVertexIndex+10];
              entitiesVertPosZdata[currentEntityVertexZIndex+11]=entitiesVertPosdata[currentEntityVertexIndex+11];

              entitiesVertPosZdata[currentEntityVertexZIndex+12]=entitiesVertPosdata[currentEntityVertexIndex+12];
              entitiesVertPosZdata[currentEntityVertexZIndex+13]=entitiesVertPosdata[currentEntityVertexIndex+13];
              entitiesVertPosZdata[currentEntityVertexZIndex+14]=entitiesVertPosdata[currentEntityVertexIndex+14];

              entitiesVertPosZdata[currentEntityVertexZIndex+15]=entitiesVertPosdata[currentEntityVertexIndex+15];
              entitiesVertPosZdata[currentEntityVertexZIndex+16]=entitiesVertPosdata[currentEntityVertexIndex+16];
              entitiesVertPosZdata[currentEntityVertexZIndex+17]=entitiesVertPosdata[currentEntityVertexIndex+17];

              //if (currentEntity.oTextureId.indexOf('grass')!==-1){
              //  console.log(currentEntity.oTextureId,currentEntity.scale,currentTexture.scaleX);
              //}

            }else{ // entity such as water

              entitiesVertPosdata[currentEntityVertexIndex+0]=0.0;
              entitiesVertPosdata[currentEntityVertexIndex+1]=0.0;
              entitiesVertPosdata[currentEntityVertexIndex+2]=0.0;

              entitiesVertPosdata[currentEntityVertexIndex+3]=0.0;
              entitiesVertPosdata[currentEntityVertexIndex+4]=0.0;
              entitiesVertPosdata[currentEntityVertexIndex+5]=1.0*currentEntity.scale*currentTexture.scaleY;

              entitiesVertPosdata[currentEntityVertexIndex+6]=1.0*currentEntity.scale*currentTexture.scaleX;
              entitiesVertPosdata[currentEntityVertexIndex+7]=0.0;
              entitiesVertPosdata[currentEntityVertexIndex+8]=1.0*currentEntity.scale*currentTexture.scaleY;;

              entitiesVertPosdata[currentEntityVertexIndex+9] =0.0;
              entitiesVertPosdata[currentEntityVertexIndex+10]=0.0;
              entitiesVertPosdata[currentEntityVertexIndex+11]=0.0;

              entitiesVertPosdata[currentEntityVertexIndex+12]=1.0*currentEntity.scale*currentTexture.scaleX;
              entitiesVertPosdata[currentEntityVertexIndex+13]=0.0;
              entitiesVertPosdata[currentEntityVertexIndex+14]=0.0;

              entitiesVertPosdata[currentEntityVertexIndex+15]=1.0*currentEntity.scale*currentTexture.scaleX;
              entitiesVertPosdata[currentEntityVertexIndex+16]=0.0;
              entitiesVertPosdata[currentEntityVertexIndex+17]=1.0*currentEntity.scale*currentTexture.scaleY;

              //--------

              entitiesVertPosZdata[currentEntityVertexZIndex+0]=entitiesVertPosdata[currentEntityVertexIndex];
              entitiesVertPosZdata[currentEntityVertexZIndex+1]=entitiesVertPosdata[currentEntityVertexIndex+1];
              entitiesVertPosZdata[currentEntityVertexZIndex+2]=entitiesVertPosdata[currentEntityVertexIndex+2];

              entitiesVertPosZdata[currentEntityVertexZIndex+3]=entitiesVertPosdata[currentEntityVertexIndex+3];
              entitiesVertPosZdata[currentEntityVertexZIndex+4]=entitiesVertPosdata[currentEntityVertexIndex+4];
              entitiesVertPosZdata[currentEntityVertexZIndex+5]=entitiesVertPosdata[currentEntityVertexIndex+5];

              entitiesVertPosZdata[currentEntityVertexZIndex+6]=entitiesVertPosdata[currentEntityVertexIndex+6];
              entitiesVertPosZdata[currentEntityVertexZIndex+7]=entitiesVertPosdata[currentEntityVertexIndex+7];
              entitiesVertPosZdata[currentEntityVertexZIndex+8]=entitiesVertPosdata[currentEntityVertexIndex+8];

              entitiesVertPosZdata[currentEntityVertexZIndex+9] =entitiesVertPosdata[currentEntityVertexIndex+9];
              entitiesVertPosZdata[currentEntityVertexZIndex+10]=entitiesVertPosdata[currentEntityVertexIndex+10];
              entitiesVertPosZdata[currentEntityVertexZIndex+11]=entitiesVertPosdata[currentEntityVertexIndex+11];

              entitiesVertPosZdata[currentEntityVertexZIndex+12]=entitiesVertPosdata[currentEntityVertexIndex+12];
              entitiesVertPosZdata[currentEntityVertexZIndex+13]=entitiesVertPosdata[currentEntityVertexIndex+13];
              entitiesVertPosZdata[currentEntityVertexZIndex+14]=entitiesVertPosdata[currentEntityVertexIndex+14];

              entitiesVertPosZdata[currentEntityVertexZIndex+15]=entitiesVertPosdata[currentEntityVertexIndex+15];
              entitiesVertPosZdata[currentEntityVertexZIndex+16]=entitiesVertPosdata[currentEntityVertexIndex+16];
              entitiesVertPosZdata[currentEntityVertexZIndex+17]=entitiesVertPosdata[currentEntityVertexIndex+17];

            }
          },

          animate:function(idx,delta,timeIdx){ //proc animate {green indent_4}
            var worldData=SEBASTIAN.GLOBALS.worldData;
            var currentEntity=worldData.entities[idx];
            var _seba=SEBASTIAN;
            var currentEntityVertexZIndex=currentEntity.depthId*6*3; // index of the first vertex of the entity in the zbuffer array

            //behavior
            //sleep animation
            if (currentEntity.isLivingBeing){
              if (currentEntity.currentAnimation!=='sleep'){
                if (timeIdx>=currentEntity.bedtime || timeIdx<=currentEntity.waketime){
                  currentEntity.currentAnimation='sleep';
                  currentEntity.currentAnimationFrameIdx=0;
                  currentEntity.isPlayingAnim=1;
                  currentEntity.hasEnteredFrame=0;
                  currentEntity.saved_textureId=currentEntity.oTextureId; // save the current texture
                  currentEntity.isAwake=0;
                  //console.log(currentEntity.name,'is sleeping at',currentEntity.bedtime);
                }
              }else{
                if (currentEntity.isAwake===0){
                  if (timeIdx>currentEntity.waketime && timeIdx<currentEntity.bedtime){
                    currentEntity.isPlayingAnim=0;
                    currentEntity.isAwake=1;
                    currentEntity.textureId=currentEntity.oTextureId; // save the current texture
                    //console.log(timeIdx,1440-timeIdx,currentEntity.waketime,currentEntity.bedtime,currentEntity.name,'is awake');
                    //set random sleep/wake time
                    SEBASTIAN.WEBGL.WORLD.ENTITIES.setLivingCreatureBehaviors(currentEntity);
                    //console.log(currentEntity.name,'is awake at',currentEntity.waketime);
                  }
                }
              }
            }

            //movement
            //speed X animation
            //---------------

            if (currentEntity.speed!==0){ // We update the x position only if the speed of the entity is different from 0 (clouds)
              currentEntity.x+=currentEntity.speed*delta;
              _seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ.data[currentEntityVertexZIndex+0]=currentEntity.x;
              //_seba.WEBGL.GLOBALS.buffers.entitiesTranslations.data[currentEntity.id*6*3+1]=currentEntity.x;
              //_seba.WEBGL.GLOBALS.buffers.entitiesTranslations.data[currentEntity.id*6*3+2]=currentEntity.x;
              _seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ.data[currentEntityVertexZIndex+3]=currentEntity.x;
              //_seba.WEBGL.GLOBALS.buffers.entitiesTranslations.data[currentEntity.id*6*3+4]=currentEntity.x;
              //_seba.WEBGL.GLOBALS.buffers.entitiesTranslations.data[currentEntity.id*6*3+5]=currentEntity.x;
              _seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ.data[currentEntityVertexZIndex+6]=currentEntity.x;
              //_seba.WEBGL.GLOBALS.buffers.entitiesTranslations.data[currentEntity.id*6*3+7]=currentEntity.x;
              //_seba.WEBGL.GLOBALS.buffers.entitiesTranslations.data[currentEntity.id*6*3+8]=currentEntity.x;
              _seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ.data[currentEntityVertexZIndex+9]=currentEntity.x;
              //_seba.WEBGL.GLOBALS.buffers.entitiesTranslations.data[currentEntity.id*6*3+10]=currentEntity.x;
              //_seba.WEBGL.GLOBALS.buffers.entitiesTranslations.data[currentEntity.id*6*3+11]=currentEntity.x;
              _seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ.data[currentEntityVertexZIndex+12]=currentEntity.x;
              //_seba.WEBGL.GLOBALS.buffers.entitiesTranslations.data[currentEntity.id*6*3+13]=currentEntity.x;
              //_seba.WEBGL.GLOBALS.buffers.entitiesTranslations.data[currentEntity.id*6*3+14]=currentEntity.x;
              _seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ.data[currentEntityVertexZIndex+15]=currentEntity.x;
              //_seba.WEBGL.GLOBALS.buffers.entitiesTranslations.data[currentEntity.id*6*3+16]=currentEntity.x;
              //_seba.WEBGL.GLOBALS.buffers.entitiesTranslations.data[currentEntity.id*6*3+17]=currentEntity.x;

              if (currentEntity.isBot){
                if (currentEntity.x>currentEntity.xLimit){
                  currentEntity.x=0-currentEntity.speed;
                  _seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ.data[currentEntityVertexZIndex+0]=currentEntity.x;
                  _seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ.data[currentEntityVertexZIndex+3]=currentEntity.x;
                  _seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ.data[currentEntityVertexZIndex+6]=currentEntity.x;
                  _seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ.data[currentEntityVertexZIndex+9]=currentEntity.x;
                  _seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ.data[currentEntityVertexZIndex+12]=currentEntity.x;
                  _seba.WEBGL.GLOBALS.buffers.entitiesTranslationsZ.data[currentEntityVertexZIndex+15]=currentEntity.x;
                  //--
                  SEBASTIAN.WEBGL.WORLD.ENTITIES.bootBot(currentEntity.id,currentEntity.id);
                  SEBASTIAN.WEBGL.WORLD.ENTITIES.syncBuffersData(currentEntity);
                }
              }

            }

            //aspect
            if (typeof currentEntity.animations!=='undefined'){
              //console.log(currentEntity.name,'has animations');
              //idle animation
              if (currentEntity.isPlayingAnim!==1){
                if (typeof currentEntity.animations.idle!=='undefined'){
                  // we get a random element from the array of available idle animations
                  var randomIdleAnimId=currentEntity.animations.idle.animations[Math.floor(Math.random()*currentEntity.animations.idle.animations.length)];
                  //console.log(randomIdleAnimId);
                  currentEntity.currentAnimation=randomIdleAnimId;
                  currentEntity.currentAnimationFrameIdx=0;
                  currentEntity.isPlayingAnim=1;
                  currentEntity.hasEnteredFrame=0;
                  currentEntity.saved_textureId=currentEntity.textureId; //salviamo la texture corrente
                  _seba.WEBGL.WORLD.ENTITIES.syncBuffersData(currentEntity);
                }
              }else{ // animation execution in progress

                if (typeof currentEntity.animations[currentEntity.currentAnimation]!=='undefined'){
                  if (currentEntity.hasEnteredFrame!==1){
                    var currentAnim=currentEntity.animations[currentEntity.currentAnimation];
                    var currentAnimFrameIdx=currentEntity.currentAnimationFrameIdx;
                    var chance=Math.random(); //0 inclusive 1 exclusive
                    //console.log(currentAnim.probability_range[0],chance,currentAnim.probability_range[1]);
                    if ((typeof currentAnim.probability_range==='undefined') || (chance>currentAnim.probability_range[0] && chance<currentAnim.probability_range[1])){
                      currentEntity.textureId=currentAnim.frames[currentAnimFrameIdx].textureId;
                      _seba.WEBGL.WORLD.ENTITIES.syncBuffersData(currentEntity);
                      currentEntity.hasEnteredFrame=1;
                      currentEntity.frameTimer=0;
                      if (typeof currentAnim.frames[currentAnimFrameIdx].duration_range!=='undefined') {
                        var frameDurationMin=currentAnim.frames[currentAnimFrameIdx].duration_range[0];
                        var frameDurationMax=currentAnim.frames[currentAnimFrameIdx].duration_range[1];
                        currentEntity.frameDisplayDuration=(Math.random()*(frameDurationMax-frameDurationMin))+frameDurationMin;
                      }else{
                        currentEntity.frameDisplayDuration=Infinity; //unlimited duration
                      }
                      //console.log('dur',currentEntity.frameDisplayDuration,frameDurationMin,frameDurationMax);
                    }else{
                      //console.log(currentEntity.currentAnimation,'failed',chance);
                      currentEntity.isPlayingAnim=0; // the animation has not been launched -> return to idle state
                    }
                  }else{ //we are inside the current animation frame

                    if (currentEntity.frameDisplayDuration!==Infinity){
                      var currentAnim=currentEntity.animations[currentEntity.currentAnimation];
                      currentEntity.frameTimer+=0.001*delta;
                      //console.log('ft',currentEntity.frameTimer);
                      if (currentEntity.frameTimer>currentEntity.frameDisplayDuration){
                        currentEntity.currentAnimationFrameIdx++;
                        if (currentEntity.currentAnimationFrameIdx>=currentAnim.frames.length){
                          // we have reached the total number of frames for this animation, return to idle
                          currentEntity.textureId=currentEntity.saved_textureId;
                          _seba.WEBGL.WORLD.ENTITIES.syncBuffersData(currentEntity);
                          currentEntity.isPlayingAnim=0;
                        }else{ // there are still other frames for this animation
                          currentEntity.hasEnteredFrame=0;
                          currentEntity.currentAnimationFrameIdx=0; // reset the current frame
                        }
                      }
                    }

                  }
                }

              }

            }

          },
          animateGeometry:function(idx,delta){ //proc animateGeometry {green indent_4}

            var _seba=SEBASTIAN;
            var worldData=SEBASTIAN.GLOBALS.worldData;
            var currentEntity=worldData.entities[idx];

            var entitiesVertPosZdata=_seba.WEBGL.GLOBALS.buffers.entitiesVertexPositionsZ.data;
            var entitiesVertPosdata=_seba.WEBGL.GLOBALS.buffers.entitiesVertexPositions.data;
            var currentEntityVertexZIndex=currentEntity.depthId*6*3;
            var currentEntityVertexIndex=currentEntity.id*6*3;

            //proc geometry animation behavior RUN {yellow indent_5}
            if (currentEntity.behaviorId===2){ //breath geometry deform (organic-life-like)
              var ms=currentEntity.deformDelta;

              entitiesVertPosZdata[currentEntityVertexZIndex+0 ]=entitiesVertPosdata[currentEntityVertexIndex+0]-ms;
              entitiesVertPosZdata[currentEntityVertexZIndex+9 ]=entitiesVertPosdata[currentEntityVertexIndex+9]-ms;
              entitiesVertPosZdata[currentEntityVertexZIndex+12]=entitiesVertPosdata[currentEntityVertexIndex+12]-ms;

              entitiesVertPosZdata[currentEntityVertexZIndex+1 ]=entitiesVertPosdata[currentEntityVertexIndex+1]-ms;
              entitiesVertPosZdata[currentEntityVertexZIndex+10]=entitiesVertPosdata[currentEntityVertexIndex+10]-ms;
              entitiesVertPosZdata[currentEntityVertexZIndex+13]=entitiesVertPosdata[currentEntityVertexIndex+13]-ms;

              currentEntity.deformDelta=currentEntity.counterDelta;
              if (!currentEntity.backCounter)
                currentEntity.counterDelta+=currentEntity.counterDeltaIncrement*delta;
              else
                currentEntity.counterDelta-=currentEntity.counterDeltaIncrement*delta;
              if (currentEntity.counterDelta<0 && currentEntity.backCounter){
                currentEntity.counterDelta=0;
                currentEntity.backCounter=0;
              }
              if (currentEntity.counterDelta>currentEntity.breathDeformLimit){
                currentEntity.backCounter=1;
              }
            }

            if (currentEntity.behaviorId===1){ //taraxacum geometry deform (windflow-like)
              var ms=currentEntity.deformDelta;

              //if(currentEntity.id===500){
              //  console.log(delta);
              //}

              entitiesVertPosZdata[currentEntityVertexZIndex+0 ]=entitiesVertPosdata[currentEntityVertexIndex+0]+ms;
              entitiesVertPosZdata[currentEntityVertexZIndex+9 ]=entitiesVertPosdata[currentEntityVertexIndex+9]+ms;
              entitiesVertPosZdata[currentEntityVertexZIndex+12]=entitiesVertPosdata[currentEntityVertexIndex+12]+ms;

              currentEntity.deformDelta=currentEntity.counterDelta;
              if (!currentEntity.isIdle){
                if (!currentEntity.backCounter)
                  currentEntity.counterDelta+=currentEntity.counterDeltaIncrement*delta;
                else
                  currentEntity.counterDelta-=currentEntity.counterDeltaIncrement*delta;
                if (currentEntity.counterDelta<0 && currentEntity.backCounter){
                  currentEntity.counterDelta=0;
                  currentEntity.backCounter=0;
                  //--
                  currentEntity.isIdle=1;
                  currentEntity.idleCounter=0;
                  var maxIdleWait=0.2;
                  var minIdleWait=0.01;
                  currentEntity.idleWait=(Math.random() * (maxIdleWait-minIdleWait)) + minIdleWait;
                }
                //console.log(delta);
                if (currentEntity.counterDelta>0.2){
                  currentEntity.backCounter=1;
                  //--
                  currentEntity.isIdle=1;
                  currentEntity.idleCounter=0;
                  var maxIdleWait=0.2;
                  var minIdleWait=0.01;
                  currentEntity.idleWait=(Math.random() * (maxIdleWait-minIdleWait)) + minIdleWait;
                }
              }else{
                currentEntity.idleCounter+=0.001*delta;
                if (currentEntity.idleCounter>currentEntity.idleWait){
                  currentEntity.isIdle=0;
                  currentEntity.idleCounter=0;
                }
              }
            }

            if (currentEntity.behaviorId===3){ //water geometry deform (windflow-like)
              var ms=currentEntity.deformDelta;

              entitiesVertPosZdata[currentEntityVertexZIndex+0 ]=entitiesVertPosdata[currentEntityVertexIndex+0 ]-ms;
              entitiesVertPosZdata[currentEntityVertexZIndex+3 ]=entitiesVertPosdata[currentEntityVertexIndex+3 ]-ms;
              entitiesVertPosZdata[currentEntityVertexZIndex+9 ]=entitiesVertPosdata[currentEntityVertexIndex+9 ]-ms;
              entitiesVertPosZdata[currentEntityVertexZIndex+12]=entitiesVertPosdata[currentEntityVertexIndex+12]+ms;
              entitiesVertPosZdata[currentEntityVertexZIndex+15]=entitiesVertPosdata[currentEntityVertexIndex+15]+ms;
              entitiesVertPosZdata[currentEntityVertexZIndex+6 ]=entitiesVertPosdata[currentEntityVertexIndex+6 ]+ms;
              entitiesVertPosZdata[currentEntityVertexZIndex+2 ]=entitiesVertPosdata[currentEntityVertexIndex+2 ]-ms;
              entitiesVertPosZdata[currentEntityVertexZIndex+11]=entitiesVertPosdata[currentEntityVertexIndex+11]-ms;
              entitiesVertPosZdata[currentEntityVertexZIndex+14]=entitiesVertPosdata[currentEntityVertexIndex+14]-ms;
              entitiesVertPosZdata[currentEntityVertexZIndex+5 ]=entitiesVertPosdata[currentEntityVertexIndex+5 ]+ms;
              entitiesVertPosZdata[currentEntityVertexZIndex+8 ]=entitiesVertPosdata[currentEntityVertexIndex+8 ]+ms;
              entitiesVertPosZdata[currentEntityVertexZIndex+17]=entitiesVertPosdata[currentEntityVertexIndex+17]+ms;

              currentEntity.deformDelta=currentEntity.counterDelta;
              if (!currentEntity.isIdle){
                if (!currentEntity.backCounter)
                  currentEntity.counterDelta+=currentEntity.counterDeltaIncrement*delta;
                else
                  currentEntity.counterDelta-=currentEntity.counterDeltaIncrement*delta;
                if (currentEntity.counterDelta<0 && currentEntity.backCounter){
                  currentEntity.counterDelta=0;
                  currentEntity.backCounter=0;
                  //--
                  currentEntity.isIdle=1;
                  currentEntity.idleCounter=0;
                  currentEntity.idleWait=0.2;
                }
                if (currentEntity.counterDelta>0.20){ //deformation limit
                  currentEntity.backCounter=1;
                  //--
                  currentEntity.isIdle=1;
                  currentEntity.idleCounter=0;
                  currentEntity.idleWait=0.2;
                }
              }else{
                currentEntity.idleCounter+=0.001*delta;
                if (currentEntity.idleCounter>currentEntity.idleWait){
                  currentEntity.isIdle=0;
                  currentEntity.idleCounter=0;
                }
              }

            }

            //position 2018-04-30 18:55:57
            if (currentEntity.behaviorId===4 || currentEntity.isFloatingOnWater===1){ //breath geometry deform (organic-life-like)

              //it is not necessary to update this kind of position animation
              //for non-visible elements
              //so it goes inside the animateGeometry function even if it does not animate the geometry

              if (typeof currentEntity.backCounterY==='undefined'){
                currentEntity.backCounterY=1;
                currentEntity.timerY=0;
              }
              if (!currentEntity.backCounterY){
                currentEntity.y+=currentEntity.counterDeltaYIncrement*delta;
                currentEntity.timerY+=currentEntity.counterDeltaYIncrement;
              }else{
                currentEntity.y-=currentEntity.counterDeltaYIncrement*delta;
                currentEntity.timerY+=currentEntity.counterDeltaYIncrement;
              }
              //console.log(currentEntity.timerY,currentEntity.counterYTotalDistance);
              if (currentEntity.timerY>currentEntity.counterYTotalDistance){
                currentEntity.timerY=0;
                if (!currentEntity.backCounterY)
                  currentEntity.backCounterY=1;
                else
                  currentEntity.backCounterY=0;
              }

              SEBASTIAN.WEBGL.WORLD.ENTITIES.syncBuffersData(currentEntity);
            }

          },

          setGeometry:function(idx){ //proc setGeometry {green indent_4}
            //var DYNAMIC_ENTITY_GEOMETRY=1;
            var _seba=SEBASTIAN;
            var gl=SEBASTIAN.GLOBALS.webgl_ctx; //lookup var
            var worldData=SEBASTIAN.GLOBALS.worldData;
            var currentEntity=worldData.entities[idx];
            var currentEntityTexture;
            currentEntityTexture=_seba.WEBGL.GLOBALS.textures[currentEntity.textureId];
            if (typeof currentEntityTexture==='undefined'){
              console.log('undefined texture',currentEntity.textureId);
              currentEntityTexture=_seba.WEBGL.GLOBALS.textures['pavement_01'];
            }

            if (SEBASTIAN.GLOBALS.dynamicEntitiesGeometry){
              //define vertext animation params
              currentEntity.deformDelta=0;
              currentEntity.counterDelta=0;
              currentEntity.backCounter=0;
            }

            //worldData.entities[idx].dynamicVertexTextureCoordBuffer=SEBASTIAN.WEBGL.WORLD.GLOBALS.globalVertexTextureCoordBuffer;

          }
        },

      }
    },
  };
  SEBASTIAN.init(); //LET'S START THE SHOW

              
            
!
999px

Console