ADN Open CIS
Сообщество программистов Autodesk в СНГ

25/10/2017

Плавные переходы камеры в Forge Viewer

На прошлой неделе у меня возникла идея оживить камеру Forge Viewer вдоль пути, с целью создания эффект пролета, поэтому я решил попробовать. У меня уже был пример анимации камеры: Forge Viewer Skybox and Camera animation. Но анимация пути была жестко запрограммирована и не меогла быть настроена, так что здесь будет использоваться совсем другой подход.

Это также является возможностью поиграть с крутой и популярной библиотекой (почти 5000 звезд на github), которая называется Tween.js: она направлен на создание плавного параметризованного перехода, так называемого "tween", между исзодным и конечным состояниями.

Идея заключается в создании интерфейса, позволяющего пользователю сохранять состояния зрителя и использовать параметры видового экрана для перехода между текущим и следующим состояниями камеры. Переходы могут быть базовыми, или более сложными, и все типы настройки выполняются при помощи Tween.js. К счастью, API-интерфейс viewer использует только три вектора для определения состояния камеры: позиция, цель и upVector, поэтому нет необходимости подгонять матрицу вращения или кватернион. Твинирование вектора якляется очень простым, поэтому я создал функцию, которая представлена  ниже. Она принимает состояние, полученное из viewer.getState () и твин от текущих состояний до этого целевого состояния:

Код - JavaScript: [Выделить]
  1. /////////////////////////////////////////////////////////
  2. // Плавный переход камеры из текущего состояния в
  3. // целевое состояние с использованием Tween.js
  4. //
  5. /////////////////////////////////////////////////////////
  6. tweenCameraTo (state) {
  7.  
  8. // Параметры tween, характерные для моего приложения,
  9. // их легко адаптировать...
  10. const {
  11.  
  12.   targetTweenDuration,
  13.   posTweenDuration,
  14.   upTweenDuration,
  15.  
  16.   targetTweenEasing,
  17.   posTweenEasing,
  18.   upTweenEasing
  19.  
  20. } = this.react.getState()
  21.  
  22. const targetEnd = new THREE.Vector3(
  23.   state.viewport.target[0],
  24.   state.viewport.target[1],
  25.   state.viewport.target[2])
  26.  
  27. const posEnd = new THREE.Vector3(
  28.   state.viewport.eye[0],
  29.   state.viewport.eye[1],
  30.   state.viewport.eye[2])
  31.  
  32. const upEnd = new THREE.Vector3(
  33.   state.viewport.up[0],
  34.   state.viewport.up[1],
  35.   state.viewport.up[2])
  36.  
  37. const nav = this.navigation
  38.  
  39. const target = new THREE.Vector3().copy(
  40.   nav.getTarget())
  41.  
  42. const pos = new THREE.Vector3().copy(
  43.   nav.getPosition())
  44.  
  45. const up = new THREE.Vector3().copy(
  46.   nav.getCameraUpVector())
  47.  
  48.  
  49. const targetTween = this.createTween({
  50.   easing: targetTweenEasing.id,
  51.   onUpdate: (v) => {
  52. nav.setTarget(v)
  53.   },
  54.   duration: targetTweenDuration,
  55.   object: target,
  56.   to: targetEnd
  57. })
  58.  
  59. const posTween = this.createTween({
  60.   easing: posTweenEasing.id,
  61.   onUpdate: (v) => {
  62. nav.setPosition(v)
  63.   },
  64.   duration: posTweenDuration,
  65.   object: pos,
  66.   to: posEnd
  67. })
  68.  
  69. const upTween = this.createTween({
  70.   easing: upTweenEasing.id,
  71.   onUpdate: (v) => {
  72. nav.setCameraUpVector(v)
  73.   },
  74.   duration: upTweenDuration,
  75.   object: up,
  76.   to: upEnd
  77. })
  78.  
  79. Promise.all([
  80.   targetTween,
  81.   posTween,
  82.   upTween]).then(() => {
  83.  
  84.   this.animate = false
  85. })
  86.  
  87. this.runAnimation(true)
  88.   }

Я также завернул создание твинов в функцию, которая возвращает Promise, поэтому очень удобно обрабатывать несколько твинов, работающих параллельно, которые могут иметь другое время выполнения. Также легко запускать код, как только они будут завершены, в этом сценарии, выполняющем переход к следующему состоянию:

Код - JavaScript: [Выделить]
  1. /////////////////////////////////////////////////////////
  2. // Tween, который возвращает Promise
  3. //
  4. /////////////////////////////////////////////////////////
  5. createTween (params) {
  6.  
  7. return new Promise ((resolve) => {
  8.  
  9.   new TWEEN.Tween(params.object)
  10. .to(params.to, params.duration)
  11. .onComplete(() => resolve())
  12. .onUpdate(params.onUpdate)
  13. .easing(params.easing)
  14. .start()
  15. })
  16.   }

Сохранение/восстановление состояния Viewer не требует больших усилий, однако создание приятного пользовательского интерфейса, и API-интерфейса на стороне сервера для доступа к вашей базе данных - с целью организации живучести, - является нетривиальной частью работы. Благодаря гибкости моих расширений, основанных на React, я смог легко объединить этот новый образец tween с моим существующим Viewing.Extension.ConfigManager и буквально внедрить это расширение UI в новый, используя то, что я сделал ранее, без каких-либо дополнительных затрат.

Вот источник этой новой демонстрации: живая версия Viewing.Extension.CameraTween, с которой вы можете поиграть, и демонстрация последовательности, которую я создал, представлена ниже:

Источник: https://forge.autodesk.com/blog/smooth-camera-transitions-forge-viewer

Автор перевода: Дмитрий Емельянов

Обсуждение: http://adn-cis.org/forum/index.php?topic=8116

Опубликовано 25.10.2017