Плавные переходы камеры в Forge Viewer
На прошлой неделе у меня возникла идея оживить камеру Forge Viewer вдоль пути, с целью создания эффект пролета, поэтому я решил попробовать. У меня уже был пример анимации камеры: Forge Viewer Skybox and Camera animation. Но анимация пути была жестко запрограммирована и не меогла быть настроена, так что здесь будет использоваться совсем другой подход.
Это также является возможностью поиграть с крутой и популярной библиотекой (почти 5000 звезд на github), которая называется Tween.js: она направлен на создание плавного параметризованного перехода, так называемого "tween", между исзодным и конечным состояниями.
Идея заключается в создании интерфейса, позволяющего пользователю сохранять состояния зрителя и использовать параметры видового экрана для перехода между текущим и следующим состояниями камеры. Переходы могут быть базовыми, или более сложными, и все типы настройки выполняются при помощи Tween.js. К счастью, API-интерфейс viewer использует только три вектора для определения состояния камеры: позиция, цель и upVector, поэтому нет необходимости подгонять матрицу вращения или кватернион. Твинирование вектора якляется очень простым, поэтому я создал функцию, которая представлена ниже. Она принимает состояние, полученное из viewer.getState () и твин от текущих состояний до этого целевого состояния:
- /////////////////////////////////////////////////////////
- // Плавный переход камеры из текущего состояния в
- // целевое состояние с использованием Tween.js
- //
- /////////////////////////////////////////////////////////
- tweenCameraTo (state) {
- // Параметры tween, характерные для моего приложения,
- // их легко адаптировать...
- const {
- targetTweenDuration,
- posTweenDuration,
- upTweenDuration,
- targetTweenEasing,
- posTweenEasing,
- upTweenEasing
- } = this.react.getState()
- const targetEnd = new THREE.Vector3(
- state.viewport.target[0],
- state.viewport.target[1],
- state.viewport.target[2])
- const posEnd = new THREE.Vector3(
- state.viewport.eye[0],
- state.viewport.eye[1],
- state.viewport.eye[2])
- const upEnd = new THREE.Vector3(
- state.viewport.up[0],
- state.viewport.up[1],
- state.viewport.up[2])
- const nav = this.navigation
- const target = new THREE.Vector3().copy(
- nav.getTarget())
- const pos = new THREE.Vector3().copy(
- nav.getPosition())
- const up = new THREE.Vector3().copy(
- nav.getCameraUpVector())
- const targetTween = this.createTween({
- easing: targetTweenEasing.id,
- onUpdate: (v) => {
- nav.setTarget(v)
- },
- duration: targetTweenDuration,
- object: target,
- to: targetEnd
- })
- const posTween = this.createTween({
- easing: posTweenEasing.id,
- onUpdate: (v) => {
- nav.setPosition(v)
- },
- duration: posTweenDuration,
- object: pos,
- to: posEnd
- })
- const upTween = this.createTween({
- easing: upTweenEasing.id,
- onUpdate: (v) => {
- nav.setCameraUpVector(v)
- },
- duration: upTweenDuration,
- object: up,
- to: upEnd
- })
- Promise.all([
- targetTween,
- posTween,
- upTween]).then(() => {
- this.animate = false
- })
- this.runAnimation(true)
- }
Я также завернул создание твинов в функцию, которая возвращает Promise, поэтому очень удобно обрабатывать несколько твинов, работающих параллельно, которые могут иметь другое время выполнения. Также легко запускать код, как только они будут завершены, в этом сценарии, выполняющем переход к следующему состоянию:
- /////////////////////////////////////////////////////////
- // Tween, который возвращает Promise
- //
- /////////////////////////////////////////////////////////
- createTween (params) {
- return new Promise ((resolve) => {
- new TWEEN.Tween(params.object)
- .to(params.to, params.duration)
- .onComplete(() => resolve())
- .onUpdate(params.onUpdate)
- .easing(params.easing)
- .start()
- })
- }
Сохранение/восстановление состояния 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