Untitled

mail@pastecode.io avatar
unknown
typescript
a year ago
8.7 kB
3
Indexable
Never
import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';

import * as THREE from 'three';
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls';
import {IUniform} from 'three/src/renderers/shaders/UniformsLib';

const vertexShader: string = `
    uniform float amplitude;
    attribute vec3 color;
    varying vec4 varColor;
    void main()
    {
        varColor = vec4(color, 1.0);
        vec4 pos = vec4(position, 1.0);
        pos.z *= amplitude;
        vec4 mvPosition = modelViewMatrix * pos;
        gl_PointSize = 1.0;
        gl_Position = projectionMatrix * mvPosition;
    }
`;

const fragmentShader: string = `
    varying vec4 varColor;
    void main()
    {
        gl_FragColor = varColor;
    }
`;

@Component({
  selector: 'photo-welcome',
  templateUrl: './photo.component.html',
})
export class PhotoComponent implements OnInit {
  image = '/assets/szyszki.jpg';
  startZoom = 3000;

  imageData?: Uint8ClampedArray;
  imageWidth = 0;
  imageHeight = 0;
  uniforms: { [uniform: string]: IUniform } = {
    amplitude: {
      value: 0,
    },
  };
  animationTime = 0;
  animationDelta = 0.53;
  controls!: OrbitControls;
  clock = new THREE.Clock();
  renderer!: THREE.WebGLRenderer;
  scene = new THREE.Scene();
  camera: THREE.PerspectiveCamera = new THREE.PerspectiveCamera(
    20,
    window.innerWidth / window.innerHeight,
    1,
    10000
  );

  v = 0.5;
  weights = [0.1126, 0.2152, 10.7722];

  // stopTime = 0;
  // isReturningToStartZoom = false;
  // lastZoomChangeTime?: number;
  // playing = true;

  @ViewChild('container')
  private containerRef: ElementRef | undefined;

  ngOnInit() {
    this.createScene();
  }

  private error(message: string): void {
    throw new Error(message);
  }

  private createScene() {
    const container = document.getElementById('container');
    if (!container) {
      this.error('createScene - missing container');
      return;
    }

    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
    this.scene.add(ambientLight);

    const pointLight = new THREE.PointLight(0xffffff, 0.5);
    pointLight.position.x = 0;
    pointLight.position.y = 0;
    pointLight.position.z = 0;
    this.scene.add(pointLight);

    this.renderer = new THREE.WebGLRenderer({
      canvas: container,
      antialias: true,
    });

    const canvasSizes = {
      width: window.innerWidth,
      height: window.innerHeight,
    };

    this.camera.position.z = this.startZoom;
    this.camera.position.x = 0;
    this.camera.position.y = 0;
    this.camera.lookAt(this.scene.position);
    this.scene.add(this.camera);

    this.renderer.setClearColor(0x000000, 1);
    this.renderer.setSize(canvasSizes.width, canvasSizes.height);

    window.addEventListener('resize', () => {
      if (!this.renderer || !this.camera || !this.scene) {
        this.error(
          'createScene.addEventListener - missing container or scene or camera'
        );
        return;
      }

      canvasSizes.width = window.innerWidth;
      canvasSizes.height = window.innerHeight;

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

      this.renderer.setSize(canvasSizes.width, canvasSizes.height);
      this.renderer.render(this.scene, this.camera);
    });

    // window.addEventListener('keyup', (e) => {
    //   if (e.code === 'Space') {
    //     this.playing = !this.playing;
    //   }
    // })

    this.createControls();
  }

  private createControls() {
    this.controls = new OrbitControls(this.camera, this.renderer.domElement);
    this.controls.rotateSpeed = 1;
    this.controls.zoomSpeed = 1;
    this.controls.panSpeed = 0.5;
    this.controls.enableZoom = true;
    this.controls.enablePan = true;
    this.controls.enableRotate = true;
    this.controls.enableDamping = true;
    this.controls.dampingFactor = 0.55;
    // this.controls.addEventListener('change', () => {
    //   if (!this.isReturningToStartZoom) {
    //     this.lastZoomChangeTime = Date.now();
    //   }
    // });

    this.createPixelData();
  }

  private tick() {
    const run = () => {
      // Render
      const elapsedTime = this.clock.getElapsedTime();
      this.uniforms['amplitude'].value = Math.min(0, Math.sin(elapsedTime) / 5) * -1;
      this.animationTime += this.animationDelta;

      // this.addRestorePosition();
      this.controls.update();
      this.renderer.render(this.scene, this.camera);

      // Call tick again on the next frame
      window.requestAnimationFrame(run);
    };

    run();
  }

  // addRestorePosition() {
  //   if (!this.camera) {
  //     this.error('addRestorePosition - missing camera');
  //     return;
  //   }
  //
  //   // Jeśli od ostatniej zmiany zoomu minęły 3 sekundy
  //   if (
  //     !!this.lastZoomChangeTime &&
  //     Date.now() - this.lastZoomChangeTime > 1000
  //   ) {
  //     // Rozpoczyna proces powrotu do początkowego zoomu
  //     this.isReturningToStartZoom = true;
  //
  //     this.camera.position.z += (this.startZoom - this.camera.position.z) * 0.1;
  //     this.camera.position.x += -this.camera.position.x * 0.1;
  //     this.camera.position.y += -this.camera.position.y * 0.1;
  //
  //     if (Math.abs(this.camera.position.z - this.startZoom) < 0.1) {
  //       this.camera.position.z = this.startZoom;
  //       this.isReturningToStartZoom = false; // Kończy proces powrotu do początkowego zoomu
  //       this.lastZoomChangeTime = undefined;
  //       this.camera.position.y = 0;
  //       this.camera.position.x = 0;
  //       console.log('Finish');
  //     }
  //   }
  // }

  private createPixelData() {
    const image = document.createElement('img');
    const canvas = document.createElement('canvas');
    if (!canvas) {
      this.error('createPixelData - missing canvas');
      return;
    }

    const context = canvas.getContext('2d');

    if (!context) {
      this.error('createPixelData - missing context');
      return;
    }

    image.crossOrigin = 'Anonymous';
    image.onload = () => {
      this.imageWidth = canvas.width = image.width;
      this.imageHeight = canvas.height = image.height;
      const pattern = context.createPattern(image, 'no-repeat');
      if (pattern instanceof CanvasPattern) {
        context.fillStyle = pattern;
        context.fillRect(0, 0, this.imageWidth, this.imageHeight);

        this.imageData = context.getImageData(
          0,
          0,
          this.imageWidth,
          this.imageHeight
        ).data;

        this.createParticles();
        this.tick();
      }
    };

    image.src = this.image;
  }

  private createParticles() {
    if (!this.imageData) {
      this.error('createParticles - missing imageData');
      return;
    }

    let c = 0;
    let x, y;
    const zRange = 1000;

    const geometry = new THREE.BufferGeometry();

    x = this.imageWidth * -this.v;
    y = this.imageHeight * this.v;

    const vertices = [];
    const colors = [];

    if (!vertexShader || !fragmentShader) {
      this.error(
        'createParticles - missing vertexShader or fragmentShader'
      );
      return;
    }

    const shaderMaterial = new THREE.ShaderMaterial({
      uniforms: this.uniforms,
      vertexShader,
      fragmentShader,
    });

    for (let i = 0; i < this.imageHeight; i++) {
      for (let j = 0; j < this.imageWidth; j++) {
        const color = new THREE.Color();

        color.setRGB(
          this.imageData[c] / 255,
          this.imageData[c + 1] / 255,
          this.imageData[c + 2] / 255
        );
        colors.push(color.r, color.g, color.b);

        const weight =
          color.r * this.weights[0] +
          color.g * this.weights[1] +
          color.b * this.weights[2];

        const vertex = new THREE.Vector3();

        vertex.x = x;
        vertex.y = y;
        vertex.z = zRange * -this.v + zRange * weight;

        vertices.push(vertex.x, vertex.y, vertex.z);

        c += 4;
        x++;
      }

      x = this.imageWidth * -this.v;
      y--;
    }

    geometry.setAttribute(
      'position',
      new THREE.Float32BufferAttribute(vertices, 3)
    );
    geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));

    const particleSystem = new THREE.Points(geometry, shaderMaterial);
    this.scene.add(particleSystem);
  }
}