Home Reference Source Repository

js/mmd/MMDCameraAnimator.js

'use strict'

import Matrix from '../base/Matrix'
import Vector3 from '../base/Vector3'
import CameraAnimator from '../base/CameraAnimator'

/**
 * MMDCameraAnimator class
 * @access public
 */
export default class MMDCameraAnimator extends CameraAnimator {
  /**
   * constructor
   * @access public
   * @constructor
   */
  constructor() {
    super()

    this._work_vec3_1 = new Vector3()
    this._work_vec3_2 = new Vector3()
    this._work_mat = new Matrix()

    // debug
    this.camera_dx = 0
    this.camera_dy = 0
    this.camera_dz = 0

    // debug
    this._rot_pattern = 0
    this._eye_pattern = 0
    this._rot_minusx = false
    this._rot_minusy = false
    this._rot_minusz = false
  }

  updateMotion(camera, elapsedTime) {
    const motion = camera._motion

    // FIXME
    //if(!camera._animating)
    //  return

    //if(camera._animating){
    camera._animationTime += elapsedTime
    const frameNo = camera._animationTime * motion.defaultFPS
    if(frameNo > motion.frameLength){
      if(camera._loop){
        camera._animationTime -= motion.frameLength / motion.defaultFPS
      }else{
        camera._animationTime = motion.frameLength / motion.defaultFPS
        camera._animating = false
      }
    }
    if(camera._motionBlendStep){
      camera._motionBlendCount -= camera._motionBlendStep * motion.defaultFPS * elapsedTime
      if(camera._motionBlendCount < 0){
        camera._motionBlendCount = 0
        camera._motionBlendStep = 0
      }
    }
    //}
    camera._animationFrame = camera._animationTime * motion.defaultFPS

    // update camera parameters 
    let indexNo = camera._motionNumCache
    const motionArray = motion.motionArray
    const motionLen = motionArray.length
    const time = camera._animationFrame
    let key0 = 0
    let key1 = 0
    if(!indexNo)
      indexNo = 0

    if(motionLen <= indexNo)
      indexNo = motionLen - 1

    if(motionArray[indexNo].frameNo < time){
      for(; indexNo < motionLen; indexNo++){
        if(motionArray[indexNo].frameNo >= time)
          break
      }
      key0 = indexNo - 1
      key1 = indexNo
    }else{
      for(; indexNo >= 0; indexNo--){
        if(motionArray[indexNo].frameNo < time)
          break
      }
      key0 = indexNo
      key1 = indexNo + 1
    }
    if(key0 <= 0)         key0 = 0
    if(key1 >= motionLen) key1 = motionLen - 1

    camera._motionNumCache = key0

    const motion0 = motionArray[key0]
    const motion1 = motionArray[key1]
    const time0 = motion0.frameNo
    const time1 = motion1.frameNo

    let k = 0
    if(!camera._pos){
      camera._pos = new Vector3()
      camera._rot = new Vector3()
    }
    const pos = camera._pos
    const rot = camera._rot
    let distance = 0
    let angle = 0

    if(time0 !== time1){
      k = 127 * (time - time0) / (time1 - time0)
      /*
      pos.x = this.getBezierValue(motion1.interpolation[0],  motion1.interpolation[1],
                                  motion1.interpolation[2],  motion1.interpolation[3],
                                  motion0.position.x, motion1.position.x, k)
      pos.y = this.getBezierValue(motion1.interpolation[4],  motion1.interpolation[5],
                                  motion1.interpolation[6],  motion1.interpolation[7],
                                  motion0.position.y, motion1.position.y, k)
      pos.z = this.getBezierValue(motion1.interpolation[8],  motion1.interpolation[9],
                                  motion1.interpolation[10], motion1.interpolation[11],
                                  motion0.position.z, motion1.position.z, k)

      const r = this.getBezierValue(motion1.interpolation[12], motion1.interpolation[13],
                                  motion1.interpolation[14], motion1.interpolation[15],
                                  0, 1, k)
      //rot.slerp(motion0.rotate, motion1.rotate, r)
      rot.lerp(motion0.rotate, motion1.rotate, r)

      distance = this.getBezierValue(motion1.interpolation[16], motion1.interpolation[17],
                                     motion1.interpolation[18], motion1.interpolation[19],
                                     motion0.distance, motion1.distance, k)

      angle = this.getBezierValue(motion1.interpolation[20], motion1.interpolation[21],
                                  motion1.interpolation[22], motion1.interpolation[23],
                                  motion0.angle, motion1.angle, k)
      */
      pos.x = this.getBezierValue(motion1.interpolation[0],  motion1.interpolation[2],
                                  motion1.interpolation[1],  motion1.interpolation[3],
                                  motion0.position.x, motion1.position.x, k)
      pos.y = this.getBezierValue(motion1.interpolation[4],  motion1.interpolation[6],
                                  motion1.interpolation[5],  motion1.interpolation[7],
                                  motion0.position.y, motion1.position.y, k)
      pos.z = this.getBezierValue(motion1.interpolation[8],  motion1.interpolation[10],
                                  motion1.interpolation[9],  motion1.interpolation[11],
                                  motion0.position.z, motion1.position.z, k)

      const r = this.getBezierValue(motion1.interpolation[12], motion1.interpolation[14],
                                  motion1.interpolation[13], motion1.interpolation[15],
                                  0, 1, k)
      //rot.slerp(motion0.rotate, motion1.rotate, r)
      rot.lerp(motion0.rotate, motion1.rotate, r)

      distance = this.getBezierValue(motion1.interpolation[16], motion1.interpolation[18],
                                     motion1.interpolation[17], motion1.interpolation[19],
                                     motion0.distance, motion1.distance, k)

      angle = this.getBezierValue(motion1.interpolation[20], motion1.interpolation[22],
                                  motion1.interpolation[21], motion1.interpolation[23],
                                  motion0.angle, motion1.angle, k)

    }else{
      pos.setValue(motion0.position)
      rot.setValue(motion0.rotate)
      distance = motion0.distance
      angle = motion0.angle
    }

    if(camera._motionBlendCount){
      // FIXME: implementation
      //pos.lerp(pos, camera._pos, camera._motionBlendCount)
    }

    // projection matrix
    const near = 1.0
    const far = 10000.0
    const aspect = camera._aspect // FIXME
    if(motion0.perspective){
      camera.perspective(angle, aspect, near, far)
    }else{
      const height = 20.0 // FIXME
      const width = height * aspect
      const left = -0.5 * width
      const right = 0.5 * width
      const bottom = 0.5 * height
      const top = -0.5 * height
//alert('ortho: left=' + left + ', right=' + right + ', bottom=' + bottom + ', top=' + top
//        + ', near=' + near + ', far=' + far)
      camera.ortho(left, right, bottom, top, near, far)
    }

    // roll: z, yaw: y, pitch: x
    // view matrix
    //var cosX = Math.cos(rot.x) const sinX = Math.sin(rot.x)
    //var cosY = Math.cos(rot.y) const sinY = Math.sin(rot.y)
    //var cosZ = Math.cos(rot.z) const sinZ = Math.sin(rot.z)
    const eye = this._work_vec3_1
    const up = this._work_vec3_2
    const rotMat = this._work_mat
    eye.setValue(0, 0, -distance)
    up.setValue(0, 1, 0)

    rotMat.identity()
    rotMat.rotate(rotMat,  rot.z, 0, 0, 1)
    rotMat.rotate(rotMat, -rot.x, 1, 0, 0)
    rotMat.rotate(rotMat, -rot.y, 0, 1, 0)
    // ******** DEBUG **************
    if(this._rot_pattern){
      rotMat.identity()
      let rotX = 0
      let rotY = 0
      let rotZ = 0
      if(this._rot_minusx){
        rotX = -rot.x
      }else{
        rotX = rot.x
      }
      if(this._rot_minusy){
        rotY = -rot.y
      }else{
        rotY = rot.y
      }
      if(this._rot_minusz){
        rotZ = -rot.z
      }else{
        rotZ = rot.z
      }
      switch(this._rot_pattern) {
        case 1:
          rotMat.rotate(rotMat, rotX, 1, 0, 0)
          rotMat.rotate(rotMat, rotY, 0, 1, 0)
          rotMat.rotate(rotMat, rotZ, 0, 0, 1)
          break
        case 2:
          rotMat.rotate(rotMat, rotX, 1, 0, 0)
          rotMat.rotate(rotMat, rotZ, 0, 0, 1)
          rotMat.rotate(rotMat, rotY, 0, 1, 0)
          break
        case 3:
          rotMat.rotate(rotMat, rotY, 0, 1, 0)
          rotMat.rotate(rotMat, rotX, 1, 0, 0)
          rotMat.rotate(rotMat, rotZ, 0, 0, 1)
          break
        case 4:
          rotMat.rotate(rotMat, rotY, 0, 1, 0)
          rotMat.rotate(rotMat, rotZ, 0, 0, 1)
          rotMat.rotate(rotMat, rotX, 1, 0, 0)
          break
        case 5:
          rotMat.rotate(rotMat, rotZ, 0, 0, 1)
          rotMat.rotate(rotMat, rotX, 1, 0, 0)
          rotMat.rotate(rotMat, rotY, 0, 1, 0)
          break
        case 6:
          rotMat.rotate(rotMat, rotZ, 0, 0, 1)
          rotMat.rotate(rotMat, rotY, 0, 1, 0)
          rotMat.rotate(rotMat, rotX, 1, 0, 0)
          break
        default:
          break
      }
      switch(this._eye_pattern) {
        case 1:
          eye.setValue(distance, 0, 0)
          break
        case 2:
          eye.setValue(0, distance, 0)
          break
        case 3:
          eye.setValue(0, 0, distance)
          break
        case 4:
          eye.setValue(-distance, 0, 0)
          break
        case 5:
          eye.setValue(0, -distance, 0)
          break
        case 6:
        default:
          eye.setValue(0, 0, -distance)
          break
      }
    }

    eye.rotate(eye, rotMat)
    up.rotate(up, rotMat)

    // ******* DEBUG ************
    pos.x += this.camera_dx
    pos.y += this.camera_dy
    pos.z += this.camera_dz
    //up.x = 0
    //up.y = 1
    //up.z = 0
    //eye.x = pos.x
    //eye.y = pos.y
    //eye.z = pos.z - 15.0

    eye.x += pos.x
    eye.y += pos.y
    eye.z += pos.z

    camera.lookat(eye.x, eye.y, eye.z, pos.x, pos.y, pos.z, up.x, up.y, up.z)
  }

  getBezierValue(bx1, by1, bx2, by2, y0, y1, k) {
    let r = 0
    let val = 0
    let t0 = 0
    let t1 = 127 / 127.0
    let t = 63.5 / 127.0
    const nx1 = bx1 / 127.0
    const ny1 = by1 / 127.0
    const nx2 = bx2 / 127.0
    const ny2 = by2 / 127.0
    const nk = k / 127.0

    for(let i=0; i<8; i++){
      r = 1-t
      val = 3*t*r*(nx1*r + nx2*t) + t*t*t
      if(nk > val){
        t0 = t
      }else{
        t1 = t
      }
      t = (t0 + t1) / 2
    }
    r = 1-t
    val = (3*t*r*(ny1*r + ny2*t) + t*t*t)

    return (y0 + (y1 - y0) * val)
  }

}