喷火龙

Published on
/
/趣玩前端
<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8" />
    <title>喷火龙</title>
    <link
      rel="stylesheet"
      href="https://public.codepenassets.com/css/reset-2.0.min.css"
    />
    <style>
      @import url(https://fonts.googleapis.com/css?family=Open+Sans:800);
      #world {
        background: #652e37;
        position: absolute;
        width: 100%;
        height: 100%;
        overflow: hidden;
      }
      #instructions {
        position: absolute;
        width: 100%;
        bottom: 0%;
        margin: auto;
        margin-bottom: 20px;
        font-family: "Open Sans", sans-serif;
        color: #fdde8c;
        font-size: 0.8em;
        text-transform: uppercase;
        text-align: center;
        line-height: 1.5;
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
      }
      .lightInstructions {
        color: #f89a78;
        font-size: 0.9em;
      }
      #credits {
        position: absolute;
        width: 100%;
        margin: auto;
        bottom: 0;
        margin-bottom: 20px;
        font-family: "Open Sans", sans-serif;
        color: #f7575f;
        font-size: 0.6em;
        text-transform: uppercase;
        text-align: center;
      }
      #credits a {
        color: #f89a78;
      }

      #power {
        position: absolute;
        margin-top: 100px;
        width: 100%;
        margin: auto;
        font-family: "Open Sans", sans-serif;
        color: #481f26;
        font-size: 4em;
        text-transform: uppercase;
        text-align: center;
        line-height: 1.5;
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
      }
    </style>
  </head>
  <body>
    <!-- partial:index.partial.html -->
    <div id="world"></div>
    <div id="instructions">
      点击时间越长,喷火龙喷火越猛<br /><span class="lightInstructions"
        >- 点击喷火龙 -</span
      >
    </div>

    <div id="power">00</div>
    <!-- partial -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.16.1/TweenMax.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js"></script>
    <script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/264161/OrbitControls.js"></script>
    <script>
      //THREEJS RELATED VARIABLES

      var scene,
        camera,
        controls,
        fieldOfView,
        aspectRatio,
        nearPlane,
        farPlane,
        shadowLight,
        backLight,
        light,
        renderer,
        container;

      //SCENE
      var env,
        floor,
        dragon,
        pepperBottle,
        sneezingRate = 0,
        fireRate = 0,
        maxSneezingRate = 8,
        sneezeDelay = 500,
        awaitingSmokeParticles = [],
        timeSmoke = 0,
        timeFire = 0,
        globalSpeedRate = 1,
        sneezeTimeout,
        powerField;

      //SCREEN VARIABLES

      var HEIGHT,
        WIDTH,
        windowHalfX,
        windowHalfY,
        mousePos = {
          x: 0,
          y: 0,
        };

      //INIT THREE JS, SCREEN AND MOUSE EVENTS

      function init() {
        powerField = document.getElementById("power");

        scene = new THREE.Scene();
        scene.fog = new THREE.Fog(0x652e37, 350, 500);

        HEIGHT = window.innerHeight;
        WIDTH = window.innerWidth;
        aspectRatio = WIDTH / HEIGHT;
        fieldOfView = 60;
        nearPlane = 1;
        farPlane = 2000;
        camera = new THREE.PerspectiveCamera(
          fieldOfView,
          aspectRatio,
          nearPlane,
          farPlane
        );
        camera.position.x = -300;
        camera.position.z = 300;
        camera.position.y = 100;
        camera.lookAt(new THREE.Vector3(0, 0, 0));
        renderer = new THREE.WebGLRenderer({
          alpha: true,
          antialias: true,
        });
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(WIDTH, HEIGHT);
        renderer.shadowMapEnabled = true;
        container = document.getElementById("world");
        container.appendChild(renderer.domElement);
        windowHalfX = WIDTH / 2;
        windowHalfY = HEIGHT / 2;
        window.addEventListener("resize", onWindowResize, false);
        document.addEventListener("mouseup", handleMouseUp, false);
        document.addEventListener("touchend", handleTouchEnd, false);
        //*
        controls = new THREE.OrbitControls(camera, renderer.domElement);
        controls.minPolarAngle = -Math.PI / 2;
        controls.maxPolarAngle = Math.PI / 2;
        controls.noZoom = true;
        controls.noPan = true;
        //*/
      }

      function onWindowResize() {
        HEIGHT = window.innerHeight;
        WIDTH = window.innerWidth;
        windowHalfX = WIDTH / 2;
        windowHalfY = HEIGHT / 2;
        renderer.setSize(WIDTH, HEIGHT);
        camera.aspect = WIDTH / HEIGHT;
        camera.updateProjectionMatrix();
      }

      function handleMouseUp(event) {
        if (sneezeTimeout) clearTimeout(sneezeTimeout);
        sneezingRate += (maxSneezingRate - sneezingRate) / 10;
        powerField.innerHTML = parseInt((sneezingRate * 100) / maxSneezingRate);
        dragon.prepareToSneeze(sneezingRate);
        sneezeTimeout = setTimeout(sneeze, sneezeDelay * globalSpeedRate);
        dragon.isSneezing = true;
      }

      function sneeze() {
        dragon.sneeze(sneezingRate);
        sneezingRate = 0;
        powerField.innerHTML = "00";
      }

      function handleTouchEnd(event) {
        if (sneezeTimeout) clearTimeout(sneezeTimeout);
        sneezingRate += (maxSneezingRate - sneezingRate) / 10;
        powerField.innerHTML = parseInt((sneezingRate * 100) / maxSneezingRate);
        dragon.prepareToSneeze(sneezingRate);
        sneezeTimeout = setTimeout(sneeze, sneezeDelay * globalSpeedRate);
        dragon.isSneezing = true;
      }

      function createLights() {
        light = new THREE.HemisphereLight(0xffffff, 0xb3858c, 0.8);

        shadowLight = new THREE.DirectionalLight(0xffffff, 0.8);
        shadowLight.position.set(-100, 100, 50);
        shadowLight.castShadow = true;
        shadowLight.shadowDarkness = 0.15;

        backLight = new THREE.DirectionalLight(0xffffff, 0.4);
        backLight.position.set(200, 100, 100);
        backLight.shadowDarkness = 0.1;
        backLight.castShadow = true;

        scene.add(backLight);
        scene.add(light);
        scene.add(shadowLight);
      }

      Dragon = function () {
        this.tailAmplitude = 3;
        this.tailAngle = 0;
        this.tailSpeed = 0.07;

        this.wingAmplitude = Math.PI / 8;
        this.wingAngle = 0;
        this.wingSpeed = 0.1;
        this.isSneezing = false;

        this.threegroup = new THREE.Group(); // this is a sort of container that will hold all the meshes and will be added to the scene;

        // Materials
        var greenMat = new THREE.MeshLambertMaterial({
          color: 0x5da683,
          shading: THREE.FlatShading,
        });
        var lightGreenMat = new THREE.MeshLambertMaterial({
          color: 0x95c088,
          shading: THREE.FlatShading,
        });

        var yellowMat = new THREE.MeshLambertMaterial({
          color: 0xfdde8c,
          shading: THREE.FlatShading,
        });

        var redMat = new THREE.MeshLambertMaterial({
          color: 0xcb3e4c,
          shading: THREE.FlatShading,
        });

        var whiteMat = new THREE.MeshLambertMaterial({
          color: 0xfaf3d7,
          shading: THREE.FlatShading,
        });

        var brownMat = new THREE.MeshLambertMaterial({
          color: 0x874a5c,
          shading: THREE.FlatShading,
        });

        var blackMat = new THREE.MeshLambertMaterial({
          color: 0x403133,
          shading: THREE.FlatShading,
        });
        var pinkMat = new THREE.MeshLambertMaterial({
          color: 0xd0838e,
          shading: THREE.FlatShading,
        });

        // body
        this.body = new THREE.Group();
        this.belly = makeCube(greenMat, 30, 30, 40, 0, 0, 0, 0, 0, Math.PI / 4);

        // Wings
        this.wingL = makeCube(
          yellowMat,
          5,
          30,
          20,
          15,
          15,
          0,
          -Math.PI / 4,
          0,
          -Math.PI / 4
        );
        this.wingL.geometry.applyMatrix(
          new THREE.Matrix4().makeTranslation(0, 15, 10)
        );
        this.wingR = this.wingL.clone();
        this.wingR.position.x = -this.wingL.position.x;
        this.wingR.rotation.z = -this.wingL.rotation.z;

        // pike body
        var pikeBodyGeom = new THREE.CylinderGeometry(0, 10, 10, 4, 1);
        this.pikeBody1 = new THREE.Mesh(pikeBodyGeom, greenMat);
        this.pikeBody1.scale.set(0.2, 1, 1);
        this.pikeBody1.position.z = 10;
        this.pikeBody1.position.y = 26;

        this.pikeBody2 = this.pikeBody1.clone();
        this.pikeBody2.position.z = 0;
        this.pikeBody3 = this.pikeBody1.clone();
        this.pikeBody3.position.z = -10;

        // tail
        this.tail = new THREE.Group();
        this.tail.position.z = -20;
        this.tail.position.y = 10;

        var tailMat = new THREE.LineBasicMaterial({
          color: 0x5da683,
          linewidth: 5,
        });

        var tailGeom = new THREE.Geometry();
        tailGeom.vertices.push(
          new THREE.Vector3(0, 0, 0),
          new THREE.Vector3(0, 5, -10),
          new THREE.Vector3(0, -5, -20),
          new THREE.Vector3(0, 0, -30)
        );

        this.tailLine = new THREE.Line(tailGeom, tailMat);

        // pike
        var pikeGeom = new THREE.CylinderGeometry(0, 10, 10, 4, 1);
        pikeGeom.applyMatrix(new THREE.Matrix4().makeRotationX(-Math.PI / 2));
        this.tailPike = new THREE.Mesh(pikeGeom, yellowMat);
        this.tailPike.scale.set(0.2, 1, 1);
        this.tailPike.position.z = -35;
        this.tailPike.position.y = 0;

        this.tail.add(this.tailLine);
        this.tail.add(this.tailPike);

        this.body.add(this.belly);
        this.body.add(this.wingL);
        this.body.add(this.wingR);
        this.body.add(this.tail);
        this.body.add(this.pikeBody1);
        this.body.add(this.pikeBody2);
        this.body.add(this.pikeBody3);

        // head
        this.head = new THREE.Group();

        // head face
        this.face = makeCube(greenMat, 60, 50, 80, 0, 25, 40, 0, 0, 0);

        // head horn
        var hornGeom = new THREE.CylinderGeometry(0, 6, 10, 4, 1);
        this.hornL = new THREE.Mesh(hornGeom, yellowMat);
        this.hornL.position.y = 55;
        this.hornL.position.z = 10;
        this.hornL.position.x = 10;

        this.hornR = this.hornL.clone();
        this.hornR.position.x = -10;

        // head ears
        this.earL = makeCube(greenMat, 5, 10, 20, 32, 42, 2, 0, 0, 0);
        this.earL.geometry.applyMatrix(
          new THREE.Matrix4().makeTranslation(0, 5, -10)
        );
        this.earL.geometry.applyMatrix(
          new THREE.Matrix4().makeRotationX(Math.PI / 4)
        );
        this.earL.geometry.applyMatrix(
          new THREE.Matrix4().makeRotationY(-Math.PI / 4)
        );

        this.earR = makeCube(greenMat, 5, 10, 20, -32, 42, 2, 0, 0, 0);
        this.earR.geometry.applyMatrix(
          new THREE.Matrix4().makeTranslation(0, 5, -10)
        );
        this.earR.geometry.applyMatrix(
          new THREE.Matrix4().makeRotationX(Math.PI / 4)
        );
        this.earR.geometry.applyMatrix(
          new THREE.Matrix4().makeRotationY(Math.PI / 4)
        );

        // head mouth
        this.mouth = new THREE.Group();
        this.mouth.position.z = 50;
        this.mouth.position.y = 3;
        this.mouth.rotation.x = 0; //Math.PI / 8;

        // head mouth jaw
        this.jaw = makeCube(greenMat, 30, 10, 30, 0, -5, 15, 0, 0, 0);
        this.mouth.add(this.jaw);

        // head mouth tongue
        this.tongue = makeCube(redMat, 20, 10, 20, 0, -3, 15, 0, 0, 0);
        this.mouth.add(this.tongue);

        // head smile
        var smileGeom = new THREE.TorusGeometry(6, 2, 2, 10, Math.PI);
        this.smile = new THREE.Mesh(smileGeom, blackMat);
        this.smile.position.z = 82;
        this.smile.position.y = 5;
        this.smile.rotation.z = -Math.PI;

        // head cheek
        this.cheekL = makeCube(lightGreenMat, 4, 20, 20, 30, 18, 55, 0, 0, 0);
        this.cheekR = this.cheekL.clone();
        this.cheekR.position.x = -this.cheekL.position.x;

        //head spots
        this.spot1 = makeCube(lightGreenMat, 2, 2, 2, 20, 16, 80, 0, 0, 0);

        this.spot2 = this.spot1.clone();
        this.spot2.position.x = 15;
        this.spot2.position.y = 14;

        this.spot3 = this.spot1.clone();
        this.spot3.position.x = 16;
        this.spot3.position.y = 20;

        this.spot4 = this.spot1.clone();
        this.spot4.position.x = 12;
        this.spot4.position.y = 18;

        this.spot5 = this.spot1.clone();
        this.spot5.position.x = -15;
        this.spot5.position.y = 14;

        this.spot6 = this.spot1.clone();
        this.spot6.position.x = -14;
        this.spot6.position.y = 20;

        this.spot7 = this.spot1.clone();
        this.spot7.position.x = -19;
        this.spot7.position.y = 17;

        this.spot8 = this.spot1.clone();
        this.spot8.position.x = -11;
        this.spot8.position.y = 17;

        // head eye
        this.eyeL = makeCube(whiteMat, 10, 22, 22, 27, 34, 18, 0, 0, 0);
        this.eyeR = this.eyeL.clone();
        this.eyeR.position.x = -27;

        // head iris
        this.irisL = makeCube(brownMat, 10, 12, 12, 28, 30, 24, 0, 0, 0);
        this.irisR = this.irisL.clone();
        this.irisR.position.x = -this.irisL.position.x;

        // head nose
        this.noseL = makeCube(blackMat, 5, 5, 8, 5, 40, 77, 0, 0, 0);
        this.noseR = this.noseL.clone();
        this.noseR.position.x = -this.noseL.position.x;

        this.head.position.z = 30;
        this.head.add(this.face);
        this.head.add(this.hornL);
        this.head.add(this.hornR);
        this.head.add(this.earL);
        this.head.add(this.earR);
        this.head.add(this.mouth);
        this.head.add(this.eyeL);
        this.head.add(this.eyeR);
        this.head.add(this.irisL);
        this.head.add(this.irisR);
        this.head.add(this.noseL);
        this.head.add(this.noseR);
        this.head.add(this.cheekL);
        this.head.add(this.cheekR);
        this.head.add(this.smile);
        /*
  this.head.add(this.spot1);
  this.head.add(this.spot2);
  this.head.add(this.spot3);
  this.head.add(this.spot4);
  this.head.add(this.spot5);
  this.head.add(this.spot6);
  this.head.add(this.spot7);
  this.head.add(this.spot8);
  */
        // legs
        this.legFL = makeCube(greenMat, 20, 10, 20, 20, -30, 15, 0, 0, 0);
        this.legFR = this.legFL.clone();
        this.legFR.position.x = -30;
        this.legBL = this.legFL.clone();
        this.legBL.position.z = -15;
        this.legBR = this.legBL.clone();
        this.legBR.position.x = -30;

        this.threegroup.add(this.body);
        this.threegroup.add(this.head);
        this.threegroup.add(this.legFL);
        this.threegroup.add(this.legFR);
        this.threegroup.add(this.legBL);
        this.threegroup.add(this.legBR);
        //this.threegroup.add(this.pike);

        this.threegroup.traverse(function (object) {
          if (object instanceof THREE.Mesh) {
            object.castShadow = true;
            object.receiveShadow = true;
          }
        });
      };

      Dragon.prototype.update = function () {
        this.tailAngle += this.tailSpeed / globalSpeedRate;
        this.wingAngle += this.wingSpeed / globalSpeedRate;
        for (var i = 0; i < this.tailLine.geometry.vertices.length; i++) {
          var v = this.tailLine.geometry.vertices[i];
          v.y =
            Math.sin(this.tailAngle - (Math.PI / 3) * i) *
            this.tailAmplitude *
            i *
            i;
          v.x =
            Math.cos(this.tailAngle / 2 + (Math.PI / 10) * i) *
            this.tailAmplitude *
            i *
            i;
          if (i == this.tailLine.geometry.vertices.length - 1) {
            this.tailPike.position.x = v.x;
            this.tailPike.position.y = v.y;
            this.tailPike.rotation.x = v.y / 30;
          }
        }
        this.tailLine.geometry.verticesNeedUpdate = true;

        this.wingL.rotation.z =
          -Math.PI / 4 + Math.cos(this.wingAngle) * this.wingAmplitude;
        this.wingR.rotation.z =
          Math.PI / 4 - Math.cos(this.wingAngle) * this.wingAmplitude;
      };

      Dragon.prototype.prepareToSneeze = function (s) {
        var _this = this;
        var speed = 0.7 * globalSpeedRate;
        TweenLite.to(this.head.rotation, speed, {
          x: -s * 0.12,
          ease: Back.easeOut,
        });
        TweenLite.to(this.head.position, speed, {
          z: 30 - s * 2.2,
          y: s * 2.2,
          ease: Back.easeOut,
        });
        TweenLite.to(this.mouth.rotation, speed, {
          x: s * 0.18,
          ease: Back.easeOut,
        });

        TweenLite.to(this.smile.position, speed / 2, {
          z: 75,
          y: 10,
          ease: Back.easeOut,
        });
        TweenLite.to(this.smile.scale, speed / 2, {
          x: 0,
          y: 0,
          ease: Back.easeOut,
        });

        TweenMax.to(this.noseL.scale, speed, {
          x: 1 + s * 0.1,
          y: 1 + s * 0.1,
          ease: Back.easeOut,
        });
        TweenMax.to(this.noseR.scale, speed, {
          x: 1 + s * 0.1,
          y: 1 + s * 0.1,
          ease: Back.easeOut,
        });
        TweenMax.to(this.eyeL.scale, speed, {
          y: 1 + s * 0.01,
          ease: Back.easeOut,
        });
        TweenMax.to(this.eyeR.scale, speed, {
          y: 1 + s * 0.01,
          ease: Back.easeOut,
        });
        TweenMax.to(this.irisL.scale, speed, {
          y: 1 + s * 0.05,
          z: 1 + s * 0.05,
          ease: Back.easeOut,
        });
        TweenMax.to(this.irisR.scale, speed, {
          y: 1 + s * 0.05,
          z: 1 + s * 0.05,
          ease: Back.easeOut,
        });
        TweenMax.to(this.irisL.position, speed, {
          y: 30 + s * 0.8,
          z: 24 - s * 0.4,
          ease: Back.easeOut,
        });
        TweenMax.to(this.irisR.position, speed, {
          y: 30 + s * 0.8,
          z: 24 - s * 0.4,
          ease: Back.easeOut,
        });
        TweenMax.to(this.earL.rotation, speed, {
          x: -s * 0.1,
          y: -s * 0.1,
          ease: Back.easeOut,
        });
        TweenMax.to(this.earR.rotation, speed, {
          x: -s * 0.1,
          y: s * 0.1,
          ease: Back.easeOut,
        });
        TweenMax.to(this.wingL.rotation, speed, {
          z: -Math.PI / 4 - s * 0.1,
          ease: Back.easeOut,
        });
        TweenMax.to(this.wingR.rotation, speed, {
          z: Math.PI / 4 + s * 0.1,
          ease: Back.easeOut,
        });
        TweenMax.to(this.body.rotation, speed, {
          x: -s * 0.05,
          ease: Back.easeOut,
        });
        TweenMax.to(this.body.scale, speed, {
          y: 1 + s * 0.01,
          ease: Back.easeOut,
        });
        TweenMax.to(this.body.position, speed, {
          z: -s * 2,
          ease: Back.easeOut,
        });

        TweenMax.to(this.tail.rotation, speed, {
          x: s * 0.1,
          ease: Back.easeOut,
        });
      };

      Dragon.prototype.sneeze = function (s) {
        var _this = this;
        var sneezeEffect = 1 - s / maxSneezingRate;
        var speed = 0.1 * globalSpeedRate;
        timeFire = Math.round(s * 10);

        TweenLite.to(this.head.rotation, speed, {
          x: s * 0.05,
          ease: Back.easeOut,
        });
        TweenLite.to(this.head.position, speed, {
          z: 30 + s * 2.4,
          y: -s * 0.4,
          ease: Back.easeOut,
        });

        TweenLite.to(this.mouth.rotation, speed, {
          x: 0,
          ease: Strong.easeOut,
        });

        TweenLite.to(this.smile.position, speed * 2, {
          z: 82,
          y: 5,
          ease: Strong.easeIn,
        });

        TweenLite.to(this.smile.scale, speed * 2, {
          x: 1,
          y: 1,
          ease: Strong.easeIn,
        });

        TweenMax.to(this.noseL.scale, speed, {
          y: sneezeEffect,
          ease: Strong.easeOut,
        });
        TweenMax.to(this.noseR.scale, speed, {
          y: sneezeEffect,
          ease: Strong.easeOut,
        });
        TweenMax.to(this.noseL.position, speed, {
          y: 40, // - (sneezeEffect * 5),
          ease: Strong.easeOut,
        });
        TweenMax.to(this.noseR.position, speed, {
          y: 40, // - (sneezeEffect * 5),
          ease: Strong.easeOut,
        });
        TweenMax.to(this.irisL.scale, speed, {
          y: sneezeEffect / 2,
          z: 1,
          ease: Strong.easeOut,
        });
        TweenMax.to(this.irisR.scale, speed, {
          y: sneezeEffect / 2,
          z: 1,
          ease: Strong.easeOut,
        });
        TweenMax.to(this.eyeL.scale, speed, {
          y: sneezeEffect / 2,
          ease: Back.easeOut,
        });
        TweenMax.to(this.eyeR.scale, speed, {
          y: sneezeEffect / 2,
          ease: Back.easeOut,
        });

        TweenMax.to(this.wingL.rotation, speed, {
          z: -Math.PI / 4 + s * 0.15,
          ease: Back.easeOut,
        });
        TweenMax.to(this.wingR.rotation, speed, {
          z: Math.PI / 4 - s * 0.15,
          ease: Back.easeOut,
        });

        TweenMax.to(this.body.rotation, speed, {
          x: s * 0.02,
          ease: Back.easeOut,
        });
        TweenMax.to(this.body.scale, speed, {
          y: 1 - s * 0.03,
          ease: Back.easeOut,
        });
        TweenMax.to(this.body.position, speed, {
          z: s * 2,
          ease: Back.easeOut,
        });

        TweenMax.to(this.irisL.position, speed * 7, {
          y: 35,
          ease: Back.easeOut,
        });
        TweenMax.to(this.irisR.position, speed * 7, {
          y: 35,
          ease: Back.easeOut,
        });
        TweenMax.to(this.earR.rotation, speed * 3, {
          x: s * 0.2,
          y: s * 0.2,
          ease: Back.easeOut,
        });
        TweenMax.to(this.earL.rotation, speed * 3, {
          x: s * 0.2,
          y: -s * 0.2,
          ease: Back.easeOut,
          onComplete: function () {
            _this.backToNormal(s);
            fireRate = s;
            console.log(fireRate);
          },
        });

        TweenMax.to(this.tail.rotation, speed * 3, {
          x: -s * 0.1,
          ease: Back.easeOut,
        });
      };

      Dragon.prototype.backToNormal = function (s) {
        var _this = this;
        var speed = 1 * globalSpeedRate;
        TweenLite.to(this.head.rotation, speed, {
          x: 0,
          ease: Strong.easeInOut,
        });
        TweenLite.to(this.head.position, speed, {
          z: 30,
          y: 0,
          ease: Back.easeOut,
        });
        TweenMax.to(this.noseL.scale, speed, {
          x: 1,
          y: 1,
          ease: Strong.easeInOut,
        });
        TweenMax.to(this.noseR.scale, speed, {
          x: 1,
          y: 1,
          ease: Strong.easeInOut,
        });
        TweenMax.to(this.noseL.position, speed, {
          y: 40,
          ease: Strong.easeInOut,
        });
        TweenMax.to(this.noseR.position, speed, {
          y: 40,
          ease: Strong.easeInOut,
        });
        TweenMax.to(this.irisL.scale, speed, {
          y: 1,
          z: 1,
          ease: Back.easeOut,
        });
        TweenMax.to(this.irisR.scale, speed, {
          y: 1,
          z: 1,
          ease: Back.easeOut,
        });
        TweenMax.to(this.irisL.position, speed * 0.7, {
          y: 30,
          ease: Back.easeOut,
        });
        TweenMax.to(this.irisR.position, speed * 0.7, {
          y: 30,
          ease: Back.easeOut,
        });
        TweenMax.to(this.eyeL.scale, speed, {
          y: 1,
          ease: Strong.easeOut,
        });
        TweenMax.to(this.eyeR.scale, speed, {
          y: 1,
          ease: Strong.easeOut,
        });
        TweenMax.to(this.body.rotation, speed, {
          x: 0,
          ease: Back.easeOut,
        });
        TweenMax.to(this.body.scale, speed, {
          y: 1,
          ease: Back.easeOut,
        });
        TweenMax.to(this.body.position, speed, {
          z: 0,
          ease: Back.easeOut,
        });

        TweenMax.to(this.wingL.rotation, speed * 1.3, {
          z: -Math.PI / 4,
          ease: Back.easeInOut,
        });
        TweenMax.to(this.wingR.rotation, speed * 1.3, {
          z: Math.PI / 4,
          ease: Back.easeInOut,
        });

        TweenMax.to(this.earL.rotation, speed * 1.3, {
          x: 0,
          y: 0,
          ease: Back.easeInOut,
        });
        TweenMax.to(this.earR.rotation, speed * 1.3, {
          x: 0,
          y: 0,
          ease: Back.easeInOut,
          onComplete: function () {
            _this.isSneezing = false;
            timeSmoke = Math.round(s * 5);
          },
        });

        TweenMax.to(this.tail.rotation, speed * 1.3, {
          x: 0,
          ease: Back.easeOut,
        });
      };

      function makeCube(mat, w, h, d, posX, posY, posZ, rotX, rotY, rotZ) {
        var geom = new THREE.BoxGeometry(w, h, d);
        var mesh = new THREE.Mesh(geom, mat);
        mesh.position.x = posX;
        mesh.position.y = posY;
        mesh.position.z = posZ;
        mesh.rotation.x = rotX;
        mesh.rotation.y = rotY;
        mesh.rotation.z = rotZ;
        return mesh;
      }

      function createFloor() {
        env = new THREE.Group();

        floor = new THREE.Mesh(
          new THREE.PlaneBufferGeometry(2000, 2000),
          new THREE.MeshBasicMaterial({
            color: 0x652e37,
          })
        );
        floor.rotation.x = -Math.PI / 2;
        floor.position.y = -36;
        floor.receiveShadow = true;

        env.add(floor);
        scene.add(env);
      }

      function createDragon() {
        dragon = new Dragon();
        scene.add(dragon.threegroup);
      }

      SmokeParticle = function () {
        this.color = {
          r: 0,
          g: 0,
          b: 0,
        };
        var particleMat = new THREE.MeshLambertMaterial({
          transparent: true,
          opacity: 0.5,
          shading: THREE.FlatShading,
        });
        this.mesh = makeCube(particleMat, 4, 4, 4, 0, 0, 0, 0, 0, 0);
        awaitingSmokeParticles.push(this);
      };

      SmokeParticle.prototype.initialize = function () {
        this.mesh.rotation.x = 0;
        this.mesh.rotation.y = 0;
        this.mesh.rotation.z = 0;

        this.mesh.position.x = 0;
        this.mesh.position.y = 0;
        this.mesh.position.z = 0;

        this.mesh.scale.x = 1;
        this.mesh.scale.y = 1;
        this.mesh.scale.z = 1;

        this.mesh.material.opacity = 0.5;
        awaitingSmokeParticles.push(this);
      };

      SmokeParticle.prototype.updateColor = function () {
        this.mesh.material.color.setRGB(
          this.color.r,
          this.color.g,
          this.color.b
        );
      };

      SmokeParticle.prototype.fly = function () {
        var _this = this;
        var speed = 10 * globalSpeedRate;
        var ease = Strong.easeOut;
        var initX = this.mesh.position.x;
        var initY = this.mesh.position.y;
        var initZ = this.mesh.position.z;
        var bezier = {
          type: "cubic",
          values: [
            {
              x: initX,
              y: initY,
              z: initZ,
            },
            {
              x: initX + 30 - Math.random() * 10,
              y: initY + 20 + Math.random() * 2,
              z: initZ + 20,
            },
            {
              x: initX + 10 + Math.random() * 20,
              y: initY + 40 + Math.random() * 5,
              z: initZ - 30,
            },
            {
              x: initX + 50 - Math.random() * 20,
              y: initY + 70 + Math.random() * 10,
              z: initZ + 20,
            },
          ],
        };
        TweenMax.to(this.mesh.position, speed, {
          bezier: bezier,
          ease: ease,
        });
        TweenMax.to(this.mesh.rotation, speed, {
          x: Math.random() * Math.PI * 3,
          y: Math.random() * Math.PI * 3,
          ease: ease,
        });
        TweenMax.to(this.mesh.scale, speed, {
          x: 5 + Math.random() * 5,
          y: 5 + Math.random() * 5,
          z: 5 + Math.random() * 5,
          ease: ease,
        });
        //*
        TweenMax.to(this.mesh.material, speed, {
          opacity: 0,
          ease: ease,
          onComplete: function () {
            _this.initialize();
          },
        });
        //*/
      };

      SmokeParticle.prototype.fire = function (f) {
        var _this = this;
        var speed = 1 * globalSpeedRate;
        var ease = Strong.easeOut;
        var initX = this.mesh.position.x;
        var initY = this.mesh.position.y;
        var initZ = this.mesh.position.z;

        TweenMax.to(this.mesh.position, speed, {
          x: 0,
          y: initY - 2 * f,
          z: Math.max(initZ + 15 * f, initZ + 40),
          ease: ease,
        });
        TweenMax.to(this.mesh.rotation, speed, {
          x: Math.random() * Math.PI * 3,
          y: Math.random() * Math.PI * 3,
          ease: ease,
        });

        var bezierScale = [
          {
            x: 1,
            y: 1,
            z: 1,
          },
          {
            x: f / maxSneezingRate + Math.random() * 0.3,
            y: f / maxSneezingRate + Math.random() * 0.3,
            z: (f * 2) / maxSneezingRate + Math.random() * 0.3,
          },
          {
            x: f / maxSneezingRate + Math.random() * 0.5,
            y: f / maxSneezingRate + Math.random() * 0.5,
            z: (f * 2) / maxSneezingRate + Math.random() * 0.5,
          },
          {
            x: (f * 2) / maxSneezingRate + Math.random() * 0.5,
            y: (f * 2) / maxSneezingRate + Math.random() * 0.5,
            z: (f * 4) / maxSneezingRate + Math.random() * 0.5,
          },
          {
            x: f * 2 + Math.random() * 5,
            y: f * 2 + Math.random() * 5,
            z: f * 2 + Math.random() * 5,
          },
        ];

        TweenMax.to(this.mesh.scale, speed * 2, {
          bezier: bezierScale,
          ease: ease,
          onComplete: function () {
            _this.initialize();
          },
        });

        TweenMax.to(this.mesh.material, speed, {
          opacity: 0,
          ease: ease,
        });
        //*

        var bezierColor = [
          {
            r: 255 / 255,
            g: 205 / 255,
            b: 74 / 255,
          },
          {
            r: 255 / 255,
            g: 205 / 255,
            b: 74 / 255,
          },
          {
            r: 255 / 255,
            g: 205 / 255,
            b: 74 / 255,
          },
          {
            r: 247 / 255,
            g: 34 / 255,
            b: 50 / 255,
          },
          {
            r: 0 / 255,
            g: 0 / 255,
            b: 0 / 255,
          },
        ];

        TweenMax.to(this.color, speed, {
          bezier: bezierColor,
          ease: Strong.easeOut,
          onUpdate: function () {
            _this.updateColor();
          },
        });
        //*/
      };

      function getSmokeParticle() {
        var p;
        if (!awaitingSmokeParticles.length) {
          p = new SmokeParticle();
        }
        p = awaitingSmokeParticles.pop();
        return p;
      }

      function loop() {
        render();
        if (!dragon.isSneezing) {
          dragon.update();
        }

        if (timeSmoke > 0) {
          //if (timeSmoke%2==0){
          var noseTarget = Math.random() > 0.5 ? dragon.noseL : dragon.noseR;
          var p = getSmokeParticle();
          var pos = noseTarget.localToWorld(new THREE.Vector3(0, 0, 2));

          p.mesh.position.x = pos.x;
          p.mesh.position.y = pos.y;
          p.mesh.position.z = pos.z;
          p.mesh.material.color.setHex(0x555555);
          p.mesh.material.opacity = 0.2;

          scene.add(p.mesh);
          p.fly();
          //}
          timeSmoke--;
        }

        if (timeFire > 0) {
          var noseTarget = Math.random() > 0.5 ? dragon.noseL : dragon.noseR;
          var colTarget = Math.random() > 0.5 ? 0xfdde8c : 0xcb3e4c;
          var f = getSmokeParticle();
          var posF = noseTarget.localToWorld(new THREE.Vector3(0, 0, 2));

          f.mesh.position.x = posF.x;
          f.mesh.position.y = posF.y;
          f.mesh.position.z = posF.z;
          f.color = {
            r: 255 / 255,
            g: 205 / 255,
            b: 74 / 255,
          };
          f.mesh.material.color.setRGB(f.color.r, f.color.g, f.color.b);
          f.mesh.material.opacity = 1;

          scene.add(f.mesh);
          f.fire(fireRate);
          timeFire--;
        }

        requestAnimationFrame(loop);
      }

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

      init();
      createLights();
      createFloor();
      createDragon();
      loop();

      //dragon.threegroup.rotation.y = Math.PI/4;

      function clamp(v, min, max) {
        return Math.min(Math.max(v, min), max);
      }

      function rule3(v, vmin, vmax, tmin, tmax) {
        var nv = Math.max(Math.min(v, vmax), vmin);
        var dv = vmax - vmin;
        var pc = (nv - vmin) / dv;
        var dt = tmax - tmin;
        var tv = tmin + pc * dt;
        return tv;
      }
    </script>
  </body>
</html>