NLeSC/ahn-pointcloud-viewer

View on GitHub
app/scripts/pointcloud/pointcloud.service.js

Summary

Maintainability
F
1 wk
Test Coverage
/* global requestAnimationFrame:false  */

(function() {
  'use strict';

  function PointcloudService(THREE, Potree, $window, $rootScope, $location,
    DrivemapService,
    CameraService, SceneService,
    PathControls, MeasuringService, EarthcontrolsService,
    RailService, ExtractionDrawingService, PointcloudExtractionSelectionService,
    cfpLoadingBar, Messagebus, GradientsService) { // jshint ignore:line

    var QUALITIES = { Splats:'Splats', Low:'Circle' };

    var me = this;

    this.elRenderArea = null;

    me.settings = {
      pointCountTarget: 2.5,
      pointSize: 2.00,
      opacity: 1,
      showSkybox: false,
      interpolate: false,
      showStats: false,
      highQuality: false,
      showBoundingBox: false,

      pointColorTypes: Potree.PointColorType,
      clipModes: Potree.ClipMode,
      pointSizeType: Potree.PointSizeType.FIXED,
      pointColorType: Potree.PointColorType.HEIGHT,
      pointShape: Potree.PointShape.SQUARE,
      clipMode: Potree.ClipMode.HIGHLIGHT_INSIDE,
      quality: QUALITIES.Low,
      qualities: QUALITIES,

      useDEMCollisions: false,
      minNodeSize: 100,
      heightMin: -5,
      heightMax: 45,
      useEDL: false
    };

    me.predefinedSettings = {
      'LOW': {
      pointCountTarget: 2.5,
      pointSize: 2.00,
      opacity: 1,
      showSkybox: false,
      interpolate: false,
      showStats: false,
      highQuality: false,
      showBoundingBox: false,

      pointColorTypes: Potree.PointColorType,
      clipModes: Potree.ClipMode,
      pointSizeType: Potree.PointSizeType.FIXED,
      pointColorType: Potree.PointColorType.HEIGHT,
      pointShape: Potree.PointShape.SQUARE,
      clipMode: Potree.ClipMode.HIGHLIGHT_INSIDE,
      quality: QUALITIES.Low,
      qualities: QUALITIES,
      useDEMCollisions: false,
      minNodeSize: 100,
      heightMin: -5,
      heightMax: 45,
      useEDL: false
    },
      'STANDARD': {
      pointCountTarget: 5.0,
      pointSize: 1.00,
      opacity: 1,
      showSkybox: true,
      interpolate: false,
      showStats: false,
      highQuality: false,
      showBoundingBox: false,

      pointColorTypes: Potree.PointColorType,
      clipModes: Potree.ClipMode,
      pointSizeType: Potree.PointSizeType.ATTENUATED,
      pointColorType: Potree.PointColorType.HEIGHT,
      pointShape: Potree.PointShape.CIRCLE,
      clipMode: Potree.ClipMode.HIGHLIGHT_INSIDE,
      quality: QUALITIES.Low,
      qualities: QUALITIES,
      useDEMCollisions: false,
      minNodeSize: 100,
      heightMin: -5,
      heightMax: 45,
      useEDL: false
    },
    'HIGH': {
      pointCountTarget: 5,
      pointSize: 1.00,
      opacity: 1,
      showSkybox: true,
      interpolate: true,
      showStats: false,
      highQuality: true,
      showBoundingBox: false,

      pointColorTypes: Potree.PointColorType,
      clipModes: Potree.ClipMode,
      pointSizeType: Potree.PointSizeType.ATTENUATED,
      pointColorType: Potree.PointColorType.HEIGHT,
      pointShape: Potree.PointShape.CIRCLE,
      clipMode: Potree.ClipMode.HIGHLIGHT_INSIDE,
      quality: QUALITIES.Splats,
      qualities: QUALITIES,
      useDEMCollisions: false,
      minNodeSize: 100,
      heightMin: -5,
      heightMax: 45,
      useEDL: false
    },
    'ULTRA': {
      pointCountTarget: 10,
      pointSize: 0.5,
      opacity: 1,
      showSkybox: true,
      interpolate: true,
      showStats: false,
      highQuality: false,
      showBoundingBox: false,

      pointColorTypes: Potree.PointColorType,
      clipModes: Potree.ClipMode,
      pointSizeType: Potree.PointSizeType.ATTENUATED,
      pointColorType: Potree.PointColorType.HEIGHT,
      pointShape: Potree.PointShape.CIRCLE,
      clipMode: Potree.ClipMode.HIGHLIGHT_INSIDE,
      quality: QUALITIES.Splats,
      qualities: QUALITIES,
      useDEMCollisions: false,
      minNodeSize: 100,
      heightMin: -5,
      heightMax: 45,
      useEDL: true
    }};

    me.settings=me.predefinedSettings.STANDARD;

    me.stats = {
      nrPoints: 0,
      nrNodes: 0,
      sceneCoordinates: {
        x: 0,
        y: 0,
        z: 0
      },
      lasCoordinates: {
        x: 0,
        y: 0,
        z: 0,
        crs: 'unknown'
      }
    };

    me.renderer = null;
    var camera;
    var scene;
    var pointcloud;

    var skybox;

    me.pathMesh = null;

    var referenceFrame = SceneService.referenceFrame;
    var mouse = {
      x: 0,
      y: 0
    };

    this.orbitControls = null;
    this.isInOrbitMode = false;

    var directionalLight;

    var drivemapMaterial = new Potree.PointCloudMaterial();
    drivemapMaterial.gradient = Potree.Gradients.VIRIDIS;

    // function loadSkybox(path) {
    //   var camera = new THREE.PerspectiveCamera(75, $window.innerWidth / $window.innerHeight, 1, 100000);
    //   cameraBG = new THREE.Camera();
    //   var scene = new THREE.Scene();
    //
    //   var format = '.jpg';
    //   var urls = [
    //     path + 'px' + format, path + 'nx' + format,
    //     path + 'py' + format, path + 'ny' + format,
    //     path + 'pz' + format, path + 'nz' + format
    //   ];
    //
    //   var textureCube = THREE.ImageUtils.loadTextureCube(urls, new THREE.CubeRefractionMapping());
    //
    //   var shader = THREE.ShaderLib.cube;
    //   shader.uniforms.tCube.value = textureCube;
    //
    //   var material = new THREE.ShaderMaterial({
    //     fragmentShader: shader.fragmentShader,
    //     vertexShader: shader.vertexShader,
    //     uniforms: shader.uniforms,
    //     depthWrite: false,
    //     side: THREE.BackSide
    //   }),
    //
    //   mesh = new THREE.Mesh(new THREE.BoxGeometry(100000, 100000, 100000), material);
    //   scene.add(mesh);
    //
    //   return {
    //     'camera': camera,
    //     'scene': scene
    //   };
    // }

    function getMousePointCloudIntersection() {
      var vector = new THREE.Vector3(mouse.x, mouse.y, 0.5);
      vector.unproject(camera);
      var direction = vector.sub(camera.position).normalize();
      var ray = new THREE.Ray(camera.position, direction);

      var pointClouds = [];
      scene.traverse(function(object) {
        if (object instanceof Potree.PointCloudOctree) {
          pointClouds.push(object);
        }
      });

      var closestPoint = null;
      var closestPointDistance = null;

      for (var i = 0; i < pointClouds.length; i++) {
        var pointcloud = pointClouds[i];
        var point = pointcloud.pick(me.renderer, camera, ray, {
          accuracy: 0.5
        });

        if (!point) {
          continue;
        }

        var distance = camera.position.distanceTo(point.position);

        if (!closestPoint || distance < closestPointDistance) {
          closestPoint = point;
          closestPointDistance = distance;
        }
      }

      return closestPoint ? closestPoint.position : null;
    }

    function updateStats() {
      if (me.settings.showStats) {
        if (pointcloud) {
          me.stats.nrPoints = pointcloud.numVisiblePoints;
          me.stats.nrNodes = pointcloud.numVisibleNodes;
        } else {
          me.stats.nrPoints = 'none';
          me.stats.nrNodes = 'none';
        }

        var I = getMousePointCloudIntersection();
        if (I) {
          var sceneCoordinates = I;
          me.stats.sceneCoordinates.x = sceneCoordinates.x.toFixed(2);
          me.stats.sceneCoordinates.y = sceneCoordinates.y.toFixed(2);
          me.stats.sceneCoordinates.z = sceneCoordinates.z.toFixed(2);
          var geoCoordinates = SceneService.toGeo(sceneCoordinates);
          me.stats.lasCoordinates.x = geoCoordinates.x.toFixed(2);
          me.stats.lasCoordinates.y = geoCoordinates.y.toFixed(2);
          me.stats.lasCoordinates.z = geoCoordinates.z.toFixed(2);
        }

        // stats are changed in requestAnimationFrame loop,
        // which is outside the AngularJS $digest loop
        // to have changes to stats propagated to angular, we need to trigger a digest
        $rootScope.$digest();
      }
    }

    function onMouseMove(event) {
      mouse.x = (event.clientX / me.renderer.domElement.clientWidth) * 2 - 1;
      mouse.y = -(event.clientY / me.renderer.domElement.clientHeight) * 2 + 1;
    }

    this.initThree = function() {
      var width = $window.innerWidth;
      var height = $window.innerHeight;

      scene = SceneService.getScene();
      camera = CameraService.camera;

      me.renderer = new THREE.WebGLRenderer();
      me.renderer.setSize(width, height);
      me.renderer.autoClear = false;
        me.elRenderArea.appendChild(me.renderer.domElement);
      me.renderer.domElement.addEventListener('mousemove', onMouseMove, false);

        directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
        directionalLight.position.set( 10, 10, 10 );
        directionalLight.lookAt( new THREE.Vector3(0, 0, 0));
        scene.add( directionalLight );

        var light = new THREE.AmbientLight( 0x555555 ); // soft white light
        scene.add( light );

      MeasuringService.init(me.renderer, scene, camera);

      PointcloudExtractionSelectionService.init(me.renderer, scene, camera);
      ExtractionDrawingService.init(me.renderer, scene, camera);

      skybox = Potree.utils.loadSkybox('images/skybox/');

      // enable frag_depth extension for the interpolation shader, if available
      me.renderer.context.getExtension('EXT_frag_depth');

      DrivemapService.ready.then(this.loadPointcloud);
    };

    this.loadPointcloud = function() {
      // load pointcloud
      var pointcloudPath = DrivemapService.getPointcloudUrl();
      me.stats.lasCoordinates.crs = DrivemapService.getCrs();

      Potree.POCLoader.load(pointcloudPath, function(geometry) {
        pointcloud = new Potree.PointCloudOctree(geometry, drivemapMaterial);

        pointcloud.material.pointSizeType = Potree.PointSizeType.ADAPTIVE;
        pointcloud.material.size = me.settings.pointSize;
        pointcloud.visiblePointsTarget = me.settings.pointCountTarget * 1000 * 1000;

        referenceFrame.add(pointcloud);
        referenceFrame.updateMatrixWorld(true); // doesn't seem to do anything
        // reference frame position to pointcloud position:
        referenceFrame.position.set(-pointcloud.position.x, -pointcloud.position.y, 0);
        // rotates to some unknown orientation:
        referenceFrame.updateMatrixWorld(true);
        // rotates point cloud to align with horizon
        referenceFrame.applyMatrix(new THREE.Matrix4().set(
          1, 0, 0, 0,
          0, 0, 1, 0,
          0, -1, 0, 0,
          0, 0, 0, 1
        ));
        referenceFrame.updateMatrixWorld(true);

        var location = $location.search();

        if (location.hasOwnProperty('camera_x')) {
          var cameraPos, lookatPos;
          /* jshint ignore:start */
          cameraPos = new THREE.Vector3(parseFloat(location.camera_x), parseFloat(location.camera_y), parseFloat(location.camera_z));
          lookatPos = new THREE.Vector3(parseFloat(location.lookat_x), parseFloat(location.lookat_y), parseFloat(location.lookat_z));
          /* jshint ignore:end */

          var vector = lookatPos.clone();
          vector.sub(cameraPos);

          var cameraPos2 = cameraPos.clone().add(vector);
          var lookatPos2 = lookatPos.clone().add(vector);

          var cameraPathSurrogate = [[cameraPos.x, cameraPos.y, cameraPos.z],[cameraPos2.x, cameraPos2.y, cameraPos2.z]];
          var lookPathSurrogate = [[lookatPos.x, lookatPos.y, lookatPos.z],[lookatPos2.x, lookatPos2.y, lookatPos2.z]];
          RailService.setCameraAndLookAtWaypoints(
            cameraPathSurrogate, lookPathSurrogate
          );
        } else {
          RailService.setCameraAndLookAtWaypoints(
            DrivemapService.getCameraPath(),
            DrivemapService.getLookPath()
          );
        }

        //var miny = 306740, maxy = 615440, minx = 13420, maxx = 322120;
        // var minx = 79692.68, maxx = 96258.93, miny = 383917.51, maxy = 422503.12;
        // var worldHeight = 256, worldWidth = 256;

        // var leftTop = SceneService.toLocal(new THREE.Vector3(minx, maxy, 0));
        // var rightBottom = SceneService.toLocal(new THREE.Vector3(maxx, miny, 0));

        // var diffx = rightBottom.x - leftTop.x;
        // var diffy = rightBottom.z - leftTop.z;

        //var planeGeometry = new THREE.PlaneBufferGeometry( diffx, diffy, worldWidth - 1, worldHeight - 1 );
        //planeGeometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
        //planeGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( leftTop.x+(diffx/2), -50, rightBottom.z-(diffy/2)) );
                //var planeMesh = new THREE.Mesh( planeGeometry, new THREE.MeshBasicMaterial( { color: 0xffff00 } ) );
        //SceneService.addMaximap(planeMesh);
                //scene.add( planeMesh );


        PathControls.init(me.elRenderArea);

        MeasuringService.setPointcloud(pointcloud);

        EarthcontrolsService.init(camera, me.renderer, scene, pointcloud, me.elRenderArea);

        Messagebus.publish('legendTexture change', Potree.Gradients.VIRIDIS);
      });
    };

    this.disableDrivemap = function() {
      referenceFrame.remove(pointcloud);
    };

    this.enableDrivemap = function() {
      referenceFrame.add(pointcloud);
    };

    var emptyVector = new THREE.Vector3();
    var updateCounter = 0;


    this.update = function() {
      Potree.pointLoadLimit =  me.settings.pointCountTarget * 2 * 1000 * 1000;

      if (me.settings.useEDL) {
        directionalLight.position.copy(camera.position);
        directionalLight.lookAt(emptyVector.set(0,0,0).addVectors(camera.position, camera.getWorldDirection()));
      }

      if (pointcloud) {
      //   var bbWorld = Potree.utils.computeTransformedBoundingBox(pointcloud.boundingBox, pointcloud.matrixWorld);
      //
      //   if (!intensityMax) {
      //     var root = pointcloud.pcoGeometry.root;
      //     if (root !== null && root.loaded) {
      //       var attributes = pointcloud.pcoGeometry.root.geometry.attributes;
      //       if (attributes.intensity) {
      //         var array = attributes.intensity.array;
      //         var max = 0;
      //         for (var i = 0; i < array.length; i++) {
      //           max = Math.max(array[i]);
      //         }
      //
      //         if (max <= 1) {
      //           intensityMax = 1;
      //         } else if (max <= 256) {
      //           intensityMax = 255;
      //         } else {
      //           intensityMax = max;
      //         }
      //       }
      //     }
      //   }
      //   if (heightMin === null) {
      //     heightMin = bbWorld.min.y;
      //     heightMax = bbWorld.max.y;
      //   }


        pointcloud.material.clipMode = me.settings.clipMode;
        pointcloud.material.heightMin = me.settings.heightMin;
        pointcloud.material.heightMax = me.settings.heightMax;
        pointcloud.material.intensityMin = 0;
        pointcloud.material.intensityMax = 65000;
        pointcloud.showBoundingBox = me.settings.showBoundingBox;
            pointcloud.generateDEM = me.settings.useDEMCollisions;
            pointcloud.minimumNodePixelSize = me.settings.minNodeSize;



        // pointcloud.material.size = me.settings.pointSize;
        // pointcloud.visiblePointsTarget = me.settings.pointCountTarget * 1000 * 1000;
        // pointcloud.material.opacity = me.settings.opacity;
        // pointcloud.material.pointSizeType = me.settings.pointSizeType;
        // pointcloud.material.pointColorType = me.settings.pointColorType;
        // pointcloud.material.pointShape = me.settings.pointShape;
        // pointcloud.material.interpolate = me.settings.interpolate;
        // pointcloud.material.minSize = 2;
        //pointcloud.material.weighted = true;

        pointcloud.update(camera, me.renderer);


        // update progress bar
        updateCounter++;
        if (updateCounter > 100) {
          var progress = pointcloud.visibleNodes.length / pointcloud.visibleGeometry.length;
          var prevProgress = cfpLoadingBar.status();
          if (progress === 1 && prevProgress < 1){
            cfpLoadingBar.complete();
          } else if (progress < 1 && progress !== prevProgress){
            cfpLoadingBar.start();
            cfpLoadingBar.set(progress);
          } else if (progress === Infinity && prevProgress < 1) {
            cfpLoadingBar.complete();
          }
          updateCounter = 0;
        }
      } else {
        // loading metadata
        cfpLoadingBar.start();
      }

      // if (me.isInOrbitMode) {
      //   me.orbitControls.update();
      // } else {
      // }

      MeasuringService.update();

      CameraService.update();

      if (pointcloud) {
        if (EarthcontrolsService.enabled ) {
          EarthcontrolsService.update();
        } else {
          PathControls.updateInput();
        }
      }

      // SceneService.update();

      updateStats();
    };

    var PotreeRenderer = function() {
      var resize = function(width, height) {
        var aspect = width / height;

        camera.aspect = aspect;
        camera.updateProjectionMatrix();

        me.renderer.setSize(width, height);
      };

      this.render = function() {
        resize(me.elRenderArea.clientWidth, me.elRenderArea.clientHeight);

        // render skybox
        if (me.settings.showSkybox) {
          skybox.camera.rotation.copy(camera.rotation);
          me.renderer.render(skybox.scene, skybox.camera);
        // } else {
        //   me.renderer.render(sceneBG, cameraBG);
        }

        if (pointcloud) {
          if (pointcloud.originalMaterial) {
            pointcloud.material = pointcloud.originalMaterial;
          }

          // var bbWorld = Potree.utils.computeTransformedBoundingBox(pointcloud.boundingBox, pointcloud.matrixWorld);

          pointcloud.visiblePointsTarget = me.settings.pointCountTarget * 1000 * 1000;
          pointcloud.material.size = me.settings.pointSize;
          pointcloud.material.opacity = me.settings.opacity;
          pointcloud.material.pointColorType = me.settings.pointColorType;
          pointcloud.material.pointSizeType = me.settings.pointSizeType;
          pointcloud.material.pointShape = me.settings.PointShape;
          pointcloud.material.interpolate = me.settings.interpolate;
          pointcloud.material.weighted = false;
          pointcloud.material.gradient = Potree.Gradients.VIRIDIS;
        }

        // render scene
        me.renderer.render(scene, camera);
        // me.renderer.render(scenePointCloud, camera);

        // MeasuringService.tools.heightprofile.render();
        // MeasuringService.tools.volume.render();
        me.renderer.clearDepth();
        // MeasuringService.tools.measuring.render();
        // MeasuringService.tools.transformation.render();
      };
    };
    var potreeRenderer = new PotreeRenderer();

    // high quality rendering using splats
    var highQualityRenderer = null;
    var HighQualityRenderer = function() {

      var depthMaterial = null;
      var attributeMaterial = null;
      var normalizationMaterial = null;

      var rtDepth;
      var rtNormalize;

      var initHQSPlats = function() {
        if (depthMaterial !== null) {
          return;
        }

        depthMaterial = new Potree.PointCloudMaterial();
        attributeMaterial = new Potree.PointCloudMaterial();

        depthMaterial.pointColorType = Potree.PointColorType.DEPTH;
        depthMaterial.pointShape = Potree.PointShape.CIRCLE;
        depthMaterial.interpolate = false;
        depthMaterial.weighted = false;
        depthMaterial.minSize = 2;

        attributeMaterial.pointShape = Potree.PointShape.CIRCLE;
        attributeMaterial.gradient = Potree.Gradients.VIRIDIS;
        attributeMaterial.interpolate = false;
        attributeMaterial.weighted = true;
        attributeMaterial.minSize = 2;

        rtDepth = new THREE.WebGLRenderTarget(1024, 1024, {
          minFilter: THREE.NearestFilter,
          magFilter: THREE.NearestFilter,
          format: THREE.RGBAFormat,
          type: THREE.FloatType
        });

        rtNormalize = new THREE.WebGLRenderTarget(1024, 1024, {
          minFilter: THREE.LinearFilter,
          magFilter: THREE.NearestFilter,
          format: THREE.RGBAFormat,
          type: THREE.FloatType
        });

        var uniformsNormalize = {
          depthMap: {
            type: 't',
            value: rtDepth
          },
          texture: {
            type: 't',
            value: rtNormalize
          }
        };

        normalizationMaterial = new THREE.ShaderMaterial({
          uniforms: uniformsNormalize,
          vertexShader: Potree.Shaders['normalize.vs'],
          fragmentShader: Potree.Shaders['normalize.fs']
        });
      };

      var resize = function(width, height) {
        if (rtDepth.width === width && rtDepth.height === height) {
          return;
        }

        rtDepth.dispose();
        rtNormalize.dispose();

        camera.aspect = width / height;
        camera.updateProjectionMatrix();

        me.renderer.setSize(width, height);
        rtDepth.setSize(width, height);
        rtNormalize.setSize(width, height);
      };

      // render with splats
      this.render = function(renderer) {

        var width = me.elRenderArea.clientWidth;
        var height = me.elRenderArea.clientHeight;

        initHQSPlats();

        resize(width, height);


        renderer.clear();
        if (me.settings.showSkybox) {
          skybox.camera.rotation.copy(camera.rotation);
          renderer.render(skybox.scene, skybox.camera);
        // } else {
        //   renderer.render(sceneBG, cameraBG);
        }
        renderer.render(scene, camera);

        if (pointcloud) {

          depthMaterial.uniforms.octreeSize.value = pointcloud.pcoGeometry.boundingBox.size().x;
          attributeMaterial.uniforms.octreeSize.value = pointcloud.pcoGeometry.boundingBox.size().x;

          pointcloud.visiblePointsTarget = me.settings.pointCountTarget * 1000 * 1000;
          var originalMaterial = pointcloud.material;

          { // DEPTH PASS
            depthMaterial.size = me.settings.pointSize;
            depthMaterial.pointSizeType = me.settings.pointSizeType;
            depthMaterial.screenWidth = width;
            depthMaterial.screenHeight = height;
            depthMaterial.uniforms.visibleNodes.value = pointcloud.material.visibleNodesTexture;
            depthMaterial.uniforms.octreeSize.value = pointcloud.pcoGeometry.boundingBox.size().x;
            depthMaterial.fov = camera.fov * (Math.PI / 180);
            depthMaterial.spacing = pointcloud.pcoGeometry.spacing;
            depthMaterial.near = camera.near;
            depthMaterial.far = camera.far;
            depthMaterial.heightMin = me.settings.heightMin;
            depthMaterial.heightMax = me.settings.heightMax;
            depthMaterial.uniforms.visibleNodes.value = pointcloud.material.visibleNodesTexture;
            depthMaterial.uniforms.octreeSize.value = pointcloud.pcoGeometry.boundingBox.size().x;
            depthMaterial.bbSize = pointcloud.material.bbSize;
            depthMaterial.treeType = pointcloud.material.treeType;

            scene.overrideMaterial = depthMaterial;
            renderer.clearTarget(rtDepth, true, true, true);
            renderer.render(scene, camera, rtDepth);
            scene.overrideMaterial = null;
          }

          { // ATTRIBUTE PASS
            attributeMaterial.size = me.settings.pointSize;
            attributeMaterial.pointSizeType = me.settings.pointSizeType;
            attributeMaterial.screenWidth = width;
            attributeMaterial.screenHeight = height;
            attributeMaterial.pointColorType = me.settings.pointColorType;
            attributeMaterial.depthMap = rtDepth;
            attributeMaterial.uniforms.visibleNodes.value = pointcloud.material.visibleNodesTexture;
            attributeMaterial.uniforms.octreeSize.value = pointcloud.pcoGeometry.boundingBox.size().x;
            attributeMaterial.fov = camera.fov * (Math.PI / 180);
            attributeMaterial.spacing = pointcloud.pcoGeometry.spacing;
            attributeMaterial.near = camera.near;
            attributeMaterial.far = camera.far;
            attributeMaterial.heightMin = me.settings.heightMin;
            attributeMaterial.heightMax = me.settings.heightMax;
            attributeMaterial.intensityMin = pointcloud.material.intensityMin;
            attributeMaterial.intensityMax = pointcloud.material.intensityMax;
            attributeMaterial.setClipBoxes(pointcloud.material.clipBoxes);
            attributeMaterial.clipMode = pointcloud.material.clipMode;
            attributeMaterial.bbSize = pointcloud.material.bbSize;
            attributeMaterial.treeType = pointcloud.material.treeType;

            scene.overrideMaterial = attributeMaterial;
            renderer.clearTarget(rtNormalize, true, true, true);
            renderer.render(scene, camera, rtNormalize);
            scene.overrideMaterial = null;
          }

          { // NORMALIZATION PASS
            normalizationMaterial.uniforms.depthMap.value = rtDepth;
            normalizationMaterial.uniforms.texture.value = rtNormalize;
            Potree.utils.screenPass.render(renderer, normalizationMaterial);
          }

          pointcloud.material = originalMaterial;

          MeasuringService.tools.volume.render();
          me.renderer.clearDepth();
          MeasuringService.tools.heightprofile.render();
          MeasuringService.tools.measuring.render();
          MeasuringService.tools.transformation.render();
        }


      };
    };



    var edlRenderer = null;
    var EDLRenderer = function() {

      var edlMaterial = null;
      var attributeMaterial = null;

      //var depthTexture = null;

      var rtColor = null;
      // var gl = me.renderer.context;

      var initEDL = function() {
        if (edlMaterial !== null) {
          return;
        }

        //var depthTextureExt = gl.getExtension("WEBGL_depth_texture");

        edlMaterial = new Potree.EyeDomeLightingMaterial();
        attributeMaterial = new Potree.PointCloudMaterial();
        attributeMaterial.gradient = Potree.Gradients.VIRIDIS;

        attributeMaterial.pointShape = Potree.PointShape.CIRCLE;
        attributeMaterial.interpolate = false;
        attributeMaterial.weighted = false;
        // attributeMaterial.minSize = 2;
        attributeMaterial.useLogarithmicDepthBuffer = false;
        attributeMaterial.useEDL = true;

        rtColor = new THREE.WebGLRenderTarget(1024, 1024, {
          minFilter: THREE.LinearFilter,
          magFilter: THREE.NearestFilter,
          format: THREE.RGBAFormat,
          type: THREE.FloatType,
          //type: THREE.UnsignedByteType,
          //depthBuffer: false,
          //stencilBuffer: false
        });

        //depthTexture = new THREE.Texture();
        //depthTexture.__webglInit = true;
        //depthTexture.__webglTexture = gl.createTexture();;
        //gl.bindTexture(gl.TEXTURE_2D, depthTexture.__webglTexture);
        //gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
        //gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
        //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);
        //gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, 1024, 1024, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_SHORT, null);
      };

      var resize = function() {
        var width = me.elRenderArea.clientWidth;
        var height = me.elRenderArea.clientHeight;
        var aspect = width / height;

        var needsResize = (rtColor.width !== width || rtColor.height !== height);

        // disposal will be unnecessary once this fix made it into three.js master:
        // https://github.com/mrdoob/three.js/pull/6355
        if (needsResize) {
          rtColor.dispose();
        }

        camera.aspect = aspect;
        camera.updateProjectionMatrix();

        me.renderer.setSize(width, height);
        rtColor.setSize(width, height);

        //if(needsResize){
        //    renderer.setRenderTarget(rtColor);
        //    var framebuffer = rtColor.__webglFramebuffer;
        //    gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
        //
        //
        //    gl.bindRenderbuffer( gl.RENDERBUFFER, rtColor.__webglRenderbuffer );
        //    gl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, rtColor.width, rtColor.height );
        //    gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, null );
        //
        //    gl.bindTexture(gl.TEXTURE_2D, depthTexture.__webglTexture);
        //    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
        //    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
        //    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);
        //    gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, width, height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null);
        //
        //    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTexture.__webglTexture, 0);
        //
        //    renderer.setRenderTarget(null);
        //}
      };

      this.render = function() {
        initEDL();

        resize();

        me.renderer.clear();
        if (me.settings.showSkybox) {
          skybox.camera.rotation.copy(camera.rotation);
          me.renderer.render(skybox.scene, skybox.camera);
        // } else {
        //   me.renderer.render(sceneBG, cameraBG);
        }
        me.renderer.render(scene, camera);

        if (pointcloud) {
          var width = me.elRenderArea.clientWidth;
          var height = me.elRenderArea.clientHeight;

          var octreeSize = pointcloud.pcoGeometry.boundingBox.size().x;

          pointcloud.visiblePointsTarget = me.settings.pointCountTarget * 1000 * 1000;
          // var originalMaterial = pointcloud.material;

          { // COLOR & DEPTH PASS
            attributeMaterial.size = me.settings.pointSize;
            attributeMaterial.pointSizeType = me.settings.pointSizeType;
            attributeMaterial.screenWidth = width;
            attributeMaterial.screenHeight = height;
            attributeMaterial.pointColorType = me.settings.pointColorType;
            attributeMaterial.uniforms.visibleNodes.value = pointcloud.material.visibleNodesTexture;
            attributeMaterial.uniforms.octreeSize.value = octreeSize;
            attributeMaterial.fov = camera.fov * (Math.PI / 180);
            attributeMaterial.spacing = pointcloud.pcoGeometry.spacing;
            attributeMaterial.near = camera.near;
            attributeMaterial.far = camera.far;
            attributeMaterial.heightMin = me.settings.heightMin;
            attributeMaterial.heightMax = me.settings.heightMax;
            attributeMaterial.intensityMin = pointcloud.material.intensityMin;
            attributeMaterial.intensityMax = pointcloud.material.intensityMax;
            attributeMaterial.setClipBoxes(pointcloud.material.clipBoxes);
            attributeMaterial.clipMode = pointcloud.material.clipMode;
            attributeMaterial.bbSize = pointcloud.material.bbSize;
            attributeMaterial.treeType = pointcloud.material.treeType;

            scene.overrideMaterial = attributeMaterial;
            me.renderer.clearTarget(rtColor, true, true, true);
            me.renderer.render(scene, camera, rtColor);
            scene.overrideMaterial = null;
          }

          { // EDL OCCLUSION PASS
            edlMaterial.uniforms.screenWidth.value = width;
            edlMaterial.uniforms.screenHeight.value = height;
            edlMaterial.uniforms.near.value = camera.near;
            edlMaterial.uniforms.far.value = camera.far;
            edlMaterial.uniforms.colorMap.value = rtColor;
            edlMaterial.uniforms.expScale.value = camera.far;

            //edlMaterial.uniforms.depthMap.value = depthTexture;

            Potree.utils.screenPass.render(me.renderer, edlMaterial);
          }

          me.renderer.render(scene, camera);

          MeasuringService.tools.heightprofile.render();
          MeasuringService.tools.volume.render();
          me.renderer.clearDepth();
          MeasuringService.tools.measuring.render();
          MeasuringService.tools.transformation.render();
        }


      };
    };

    this.loop = function() {
      requestAnimationFrame(me.loop);

      me.update();

            // if(me.settings.highQuality) {
      //   me.renderHighQuality();
            // }else{
            //     me.render();
            // }

      if (me.settings.useEDL) {
        if (!edlRenderer) {
          edlRenderer = new EDLRenderer();
        }
        edlRenderer.render(me.renderer);
      } else if (me.settings.quality === 'Splats') {
        if (!highQualityRenderer) {
          highQualityRenderer = new HighQualityRenderer();
        }
        highQualityRenderer.render(me.renderer);
      } else {
        potreeRenderer.render();
      }
    };

    this.attachCanvas = function(el) {
      me.elRenderArea = el;
      me.initThree();
      var canvas = me.renderer.domElement;
      el.appendChild(canvas);
      me.loop();
    };
  }

  angular.module('pattyApp.pointcloud')
    .service('PointcloudService', PointcloudService);
})();