block by HarryStevens a15fac8f9954e94fa32d243c596ab9a7

Julian's Head

Full Screen

Use the arrows keys to move, A and S to rotate. Adjust the lighting with the controls at the top right. Is that back wall really real?

index.html

<!DOCTYPE html>
<html>
<head>
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }
    #loading {
      font-family: "Helvetica Neue", sans-serif;
      position: absolute;
      top: 33%;
      text-align: center;
      width: 100%;
    }
  </style>
</head>
<body>
  <div id="scene"></div>
  <div id="stats"></div>
  <div id="loading">Loading (<span class="pct">0</span>%)</div>
  <script src="https://unpkg.com/three@0.105.2/build/three.min.js"></script>
  <script src="https://unpkg.com/three@0.105.2/examples/js/libs/stats.min.js"></script>
  <script src="https://unpkg.com/three@0.105.2/examples/js/loaders/OBJLoader.js"></script>
  <script src="https://unpkg.com/three@0.105.2/examples/js/libs/dat.gui.min.js"></script>
  <script>
    let left = 0, right = 0, up = 0, down = 0, a = 0, s = 0;

    const controls = { lightIntensity: 1.75, lightDistance: 22, lightColor: "#fff" };
    const gui = (_ => {
      const gui = new dat.GUI();
      gui.add(controls, "lightIntensity", 0, 3);
      gui.add(controls, "lightDistance", 1, 100);
      gui.addColor(controls, "lightColor").onChange(e => {
        pointLight.color = new THREE.Color(e);
      });
      return gui;
    })();

    const stats = (_ => {
      const stats = new Stats();
      stats.domElement.style.position = "absolute";
      stats.domElement.style.left = "0px";
      stats.domElement.style.top = "0px";
      document.getElementById("stats").appendChild(stats.domElement);
      return stats;
    })();

    const scene = new THREE.Scene();

    const camera = (_ => {
      const camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 0.1, 1000);
      camera.position.set(0, 7.5, 30);
      camera.lookAt(0, 7.5, 0);
      return camera;
    })();

    const renderer = (_ => {
      const renderer = new THREE.WebGLRenderer({ antialias: true });
      renderer.setClearColor("#000");
      renderer.setPixelRatio(devicePixelRatio);
      renderer.shadowMap.enabled = true;
      renderer.shadowMap.type = THREE.PCFSoftShadowMap;
      renderer.gammaOutput = true;
      renderer.gammaFactor = 2.2;
      document.getElementById("scene").appendChild(renderer.domElement);
      return renderer;
    })();

    const floor = (_ => {
      const geo = new THREE.PlaneBufferGeometry(20, 20);
      const mat = new THREE.MeshPhongMaterial({ shininess: 10, color: "#fff" });
      const mesh = new THREE.Mesh(geo, mat);
      mesh.rotation.x = Math.PI * -.5;
      mesh.receiveShadow = true;
      scene.add(mesh);
      return mesh;
    })();

    const leftWall = (_ => {
      const geo = new THREE.PlaneBufferGeometry(20, 15);
      const mat = new THREE.MeshPhongMaterial({ color: "#fff" });
      const mesh = new THREE.Mesh(geo, mat);
      mesh.rotation.y = Math.PI * 0.5;
      mesh.position.set(-10, 7.5, 0);
      mesh.receiveShadow = true;
      scene.add(mesh);
      return mesh;
    })();

    const rightWall = (_ => {
      const geo = new THREE.PlaneBufferGeometry(20, 15);
      const mat = new THREE.MeshPhongMaterial({ color: "#fff" });
      const mesh = new THREE.Mesh(geo, mat);
      mesh.rotation.y = Math.PI * -0.5;
      mesh.position.set(10, 7.5, 0);
      mesh.receiveShadow = true;
      scene.add(mesh);
      return mesh;
    })();

    const backWall = (_ => {
      const geo = new THREE.PlaneBufferGeometry(15, 20);
      const mat = new THREE.MeshPhongMaterial({ shininess: 1, color: "#fff" });
      const mesh = new THREE.Mesh(geo, mat);
      mesh.rotation.z = Math.PI * -0.5;
      mesh.position.set(0, 7.5, -10);
      mesh.receiveShadow = true;
      scene.add(mesh);
      return mesh;
    })();

    const ceiling = (_ => {
      const geo = new THREE.PlaneBufferGeometry(20, 20);
      const mat = new THREE.MeshPhongMaterial({ color: "#fff" });
      const mesh = new THREE.Mesh(geo, mat);
      mesh.rotation.x = Math.PI * 0.5;
      mesh.position.set(0, 15, 0);
      mesh.receiveShadow = true;
      scene.add(mesh);
      return mesh;
    })();

    const lightPanel = (_ => {
      const geo = new THREE.CylinderBufferGeometry(2.5, 2.5, 1, 64);
      const mat = new THREE.MeshBasicMaterial()
      const mesh = new THREE.Mesh(geo, mat);
      mesh.position.set(0, 15, 3);
      scene.add(mesh);
      return mesh;
    })();

    const pointLight = (_ => {
      const light = new THREE.PointLight(controls.lightColor, controls.lightIntensity);
      light.position.set(0, 13, 3);
      light.distance = controls.lightDistance;
      light.castShadow = true;
      light.shadow.mapSize.width = 1024;
      light.shadow.mapSize.height = 1024;
      scene.add(light);
      return light;      
    })();

    const ambientLight = (_ => {
      const light = new THREE.AmbientLight("#0c0c0c");
      scene.add(light);
      return light;
    })();

    document.addEventListener("keydown", e => {
      if (e.key === "ArrowLeft"){
        left = 1;
        e.preventDefault();
      }
      if (e.key === "ArrowRight"){
        right = 1;
        e.preventDefault();
      }
      if (e.key === "ArrowUp"){
        up = 1;
        e.preventDefault();
      }
      if (e.key === "ArrowDown"){
        down = 1;
        e.preventDefault();
      }
      if (e.key === "a"){
        a = 1;
        e.preventDefault();
      }
      if (e.key === "s"){
        s = 1;
        e.preventDefault();
      }
    });
    document.addEventListener("keyup", e => {
      if (e.key === "ArrowLeft"){
        left = 0;
        e.preventDefault();
      }
      if (e.key === "ArrowRight"){
        right = 0;
        e.preventDefault();
      }
      if (e.key === "ArrowUp"){
        up = 0;
        e.preventDefault();
      }
      if (e.key === "ArrowDown"){
        down = 0;
        e.preventDefault();
      }
      if (e.key === "a"){
        a = 0;
        e.preventDefault();
      }
      if (e.key === "s"){
        s = 0;
        e.preventDefault();
      }
    });

    const loader = new THREE.OBJLoader();

    // load a resource
    loader.load(
      // resource URL
      "JulianHead_at4inches.obj",
      // called when resource is loaded
      object => {
        const element = document.getElementById("loading");
        element.parentNode.removeChild(element);
        object.name = "Julian";
        object.children = object.children.filter((f, i) => i === 4);
        object.children[0].material = new THREE.MeshStandardMaterial({
          color: "#ffe685",
          metalness: 1,
          roughness: .5
        });
        object.children[0].castShadow = true;
        object.position.set(-1, 0, -6);
        object.rotation.x = -.5 * Math.PI;
        object.rotation.z = .05;
        object.scale.set(2.5, 2.5, 2.5);
        scene.add(object);
      },
      // called when loading is in progresses
      xhr => {
        document.querySelector(".pct").innerHTML = (xhr.loaded / xhr.total * 100).toFixed(1);
      },
      // called when loading has errors
      error => {
        console.log("An error happened.");
      }
    );

    function julian(){
      return scene.getObjectByName("Julian", true);
    }

    function animate(){
      requestAnimationFrame(animate);

      pointLight.intensity = controls.lightIntensity;
      pointLight.distance = controls.lightDistance;

      if (left && julian().position.x > -4.8){
        julian().position.x -= 0.1;
      }
      if (right && julian().position.x < 2.3){
        julian().position.x += 0.1;
      }
      if (up && julian().position.z > -15){
        julian().position.z -= 0.1;         
      }
      if (down && julian().position.z < 7){
        julian().position.z += 0.1; 
      }
      if (a && julian()){
        julian().rotation.z -= 0.025;
      }
      if (s && julian()){
        julian().rotation.z += 0.025;
      }

      let newLightColor = {};
      Object.keys(lightPanel.material.color).forEach(channel => {
        newLightColor[channel] = new THREE.Color(controls.lightColor)[channel] * controls.lightIntensity;
      });
      lightPanel.material.color = newLightColor;

      stats.update();
      renderer.render(scene, camera);
    }
    animate();

    function size(){
      camera.aspect = innerWidth / innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(innerWidth, innerHeight);
    }
    size();
    onresize = size;  
  </script>
</body>
</html>