
window.apearingChild = function(from, to) {
  return function( child ){
    var newMaterial = child.material.clone();
    newMaterial.transparent = true;

    newMaterial.onBeforeCompile = function( shader ){
      shader.transparent = true;
      shader.vertexShader = shader.vertexShader
        .replace( `void main() {`, `varying vec4 vPosition;
void main() {
vPosition = modelMatrix * vec4(position, 1.0);` )
      ;

      var Ffrom = from.toFixed(1),
          Fto = to.toFixed(1),
          Fdiff = (to-from).toFixed(1);
      shader.fragmentShader = shader.fragmentShader.replace(
        `uniform float`,
        `
varying vec4 vPosition;
varying float noise;

uniform float`
      ).replace(
        '#include <dithering_fragment>',
        `#include <dithering_fragment>
            float time = opacity;
            vec4 pos = vPosition;
            if(time<${Ffrom}){
                gl_FragColor.a = 0.0;// *= sin(vUv.y*10.0+uTime)>0.5?1.0:0.0;                
            }else if(time >${Fto}){            
                gl_FragColor.a = 1.0;
            }else{
                vec3 color = mix(vec3(gl_FragColor), vec3(1.0,1.0,1.0), (${Fto}-time)/${Fdiff});
                gl_FragColor = vec4( color.rgb, -pos.y/(time-372.0)*10.0 );
            }
            ` );
    }
    child.material = newMaterial;

    /*child.children[0].material = newMaterial*/
    this.newMaterial = newMaterial;

  }
}

window.initScene = function({scene, camera, renderer}){


  const GLTFLoader = THREE.GLTFLoader;
  const loader = new GLTFLoader();


  Object.assign( camera.position, window.cameraPosition || {
    x: -1.7743804661688403,
    y: 1.1573565480428425,
    z: -5.243816954496617
  } );
  Object.assign( camera.rotation, { x: -3.10768077311436387, y: -0.49165510718706528, z: -0.009893627612469875 } );

  window.camera = camera


  var mixers = [];
  var clock;

  var lines = [];
  var mat1, sha1;

  var cachedMaterials = {};
  var version = false;

  var GetMaterial = function( i ){
    var lm = new MeshLineMaterial( {
      "useMap": false,
      "color": new THREE.Color( 0xffffee ),
      "opacity": 0,
      "resolution": {
        "x": 1920,
        "y": 1080
      },
      "sizeAttenuation": false,
      "lineWidth": 15,
      "depthWrite": false,
      "depthTest": true,
      "alphaTest": 0,
      "transparent": true,
      "side": THREE.DoubleSide
    } );
    if( version !== false )
      lm.version = version;
    version = lm.version;
    return lm;
  }

  var LineFromVtx = function( vtx ){
    var lineObj = {
      lines: [],
      points: [ [ 0, 0, 0 ], [ 1, 1, 1 ] ].flat(),
      vtx: vtx
    };

    var i = 0.52;
    var line = new MeshLine();
    //line.setPoints( lineObj.points );

    lineObj.lines.push( line )
    var material = GetMaterial( i );
    line.mesh = new THREE.Mesh( line, material )
    line.mesh.line = line
    line.opacity = 1 - i * 1.4;
    line._opacity = line.mesh.material._opacity;


    return lineObj;
  }
  var subScenes = [];
  var inFps = 30, outFps = 30;
  var keyMaterial;


  var setLoopTime = function( time ){
    time *= inFps;

    var cfg = this.loopTimeCfg;

    if( !cfg )
      return this.setTime( time * outFps );

    if( time < cfg.start ){

      return this.setTime( 0 );
    }else if( cfg.loop === false ){
      time = Math.min( time, cfg.to - 0.001 );
    }
    time = ( ( ( time - cfg.start ) % ( cfg.to - cfg.from ) ) + cfg.from ) / outFps;

    if( cfg.fn )
      cfg.fn( time * outFps, this );
    return this.setTime( time );
  };

  var sceneLines = [];

  var loops = window.modelCfg;
  loader.load(
    window.modelFileName,
    function( gltf ){
      var subScene = gltf.scene.clone();
      var group = new THREE.Group();
      outer.group = group;
      var notUsedAnimations = gltf.animations[ 0 ].clone(),
        notUsedAnimationsHash = {};

      notUsedAnimations.tracks.forEach( clip => notUsedAnimationsHash[ clip.name ] = clip );
      if( window.childExtractor ){
        subScene.children = window.childExtractor( subScene.children );
      }
      subScene.children = subScene.children.filter( ( child, n ) => {
        var wLines = window.lines;
        for( var key in wLines ){
          if( child.name.indexOf( key ) === 0 ){
            var lineScene = sceneLines[ key ];
            if( !lineScene ){
              sceneLines[ key ] = {
                scene: gltf.scene.clone(),
                lines: []
              };
              lineScene = sceneLines[ key ];
              lineScene.scene.children.length = 0;


            }
            var lineObj = {
              vtx: child,
              line: LineFromVtx( child ),
              points: []
            };
            lineObj.mixer = new THREE.AnimationMixer( lineScene.scene );
            lineObj.mixer.setLoopTime = setLoopTime;
            lineObj.mixer.loopTimeCfg = wLines[ key ];

            lineScene.lines.push( lineObj );


            return false;
          }
        }

        if( child.name in window.objects ){
          window.objects[ child.name ].name = child.name;
          window.objects[ child.name ].obj = child;
          if( !child ){
            debugger
          }
          window.objects[ child.name ].fn( child, subScene );
        }

        return true;
      } );

      for( var key in sceneLines ){
        var lineScene = sceneLines[ key ];
        lineScene.lines.forEach( line => {
          var child = line.vtx, lineObj = line;
          lineScene.scene.add( line.line.vtx );


          var trackStart = child.name + '.'
          child.animations = [ gltf.animations[ 0 ].clone() ].map( a => {
            a.tracks = a.tracks.filter( t => {
              if( t.name.indexOf( trackStart ) === 0 ){
                delete notUsedAnimationsHash[ t.name ]
                return true;
              }
              return false;
            } );
            lineObj.mixer.clipAction( a ).play();
            return a;
          } )


          line.line.lines.forEach( line => {
            group.add( line.mesh );
          } );
        } );
      }

      subScene.mixer = new THREE.AnimationMixer( subScene );
      notUsedAnimations.tracks = Object.values( notUsedAnimationsHash );

      subScene.mixer.clipAction( notUsedAnimations ).play();
      group.add( subScene );
      scene.add(group);
      scene.mixer = subScene.mixer;


      clock = new THREE.Clock()
      animate();
      window.lateCB && window.lateCB();

    } );


  window.addEventListener( 'resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize( window.innerWidth, window.innerHeight );
    render();
  }, false );

  const stats = Stats();
  document.body.appendChild( stats.dom );
  var TIME = 0;
  var animateScene = false;
  var animate = function(){
    var delta = clock.getDelta();
    if(animateScene){
      TIME += delta;
      for( var key in window.lines ){
        var lineScene = sceneLines[ key ];
        for( var j = 0, _j = lineScene.lines.length; j < _j; j++ ){
          var line = lineScene.lines[ j ];
          var timeLength = line.vtx.scale.x * 1.5, interpolations = 40;
          var _p = 0;
          var linePoints = line.points,
            lineVtxPosition = line.vtx.position;
          for( var i = 0; i < interpolations; i++ ){
            line.mixer.setLoopTime( TIME + timeLength / interpolations * i );
            linePoints[ _p++ ] = lineVtxPosition.x;
            linePoints[ _p++ ] = lineVtxPosition.y;
            linePoints[ _p++ ] = lineVtxPosition.z;
          }


          var cfg = line.mixer.loopTimeCfg, time = TIME * inFps - cfg.start;
          if( cfg.opacity ){
            var children = line.line.lines,
              opacity = time > cfg.opacity + cfg.start ? 1 : ( time - cfg.start < cfg.opacity - 90 ? 0 : 1 + ( time - cfg.opacity - cfg.start ) / 90 );
            for( var i = 0, _i = children.length; i < _i; i++ ){
              var child = children[ i ];

              child._opacity.value = opacity * child.opacity;

            }

          }
          for( var k = 0, _k = line.line.lines.length; k < _k; k++ ){
            line.line.lines[ k ].setPoints( line.points );
          }
          line.mixer.setLoopTime( TIME );
        }


      }
      scene.mixer.setTime( Math.min( TIME, window.sceneAnimationLength / inFps ) );
      for( var i in window.objects ){
        if( window.objects[ i ].step ){
          window.objects[ i ].step( TIME * inFps )
        }
      }
    }
    requestAnimationFrame( animate );
    /*controls.update();*/
    //render();
    stats.update();
  };

  function render(){
    renderer.render( scene, camera );
  }

  var outer = {
    show: function() {
      animateScene = true;
    },
    setTime: function(time) {
      TIME = time;
    },
    getTime: function() {
      return TIME;
    },
    scene,
    camera
  };
  return outer;
}