123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735 |
- import * as THREE from "../../../../libs/three.js/build/three.module.js";
- import math from '../../utils/math.js'
- import Tween from '../../utils/Tween.js'
- import {easing, lerp} from '../../utils/transitions.js'
- //有的动画,如小狗,进来如果不play停在第一帧,mesh会错,爪子在前面;但如果都停在第一帧,动作有可能很奇怪
- const tweens = {}
- const maxClipFadeTime = Potree.settings.maxClipFadeTime//渐变时间 s
- /* const pathStates = new Map */
- //actions中可能包含没有动作的 如TPose
- //包括无动画的模型在内的各项属性的过渡
- const rot90Qua = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1,0,0),-Math.PI/2)
- export default class AnimationEditor extends THREE.EventDispatcher{
- constructor(){
- super()
-
-
- this.poseKeys = new Map //transform
- this.descKeys = new Map //字幕
- this.pathKeys = new Map //之前设置好的Path , 优先级高于pose
- this.clipKeys = new Map //glb animation actions
- this.duration = 0 //动画时长
- this.time = 0 //当前播放时间
- this.cursorTime = 0 //时间轴指针时间
-
- this.keepDistance = true //focus的物体和相机保持不变的距离
- this.poseTransition = false //pose缓动
-
-
- if(Potree.settings.isOfficial){
- viewer.modules.MergeEditor.bus.addEventListener('changeSelect',()=>{
- let targetObject = viewer.modules.MergeEditor.selected
- targetObject = this.ifContainsModel(targetObject) ? targetObject : null
- this.setCameraFollow(targetObject)
- })
- }
-
- }
-
- addKey(model, keyType, key ){
- let keys = this[keyType+'Keys'].get(model)
- if(!keys){
- keys = []
- }
-
- let index = keys.findIndex(e=>e.time>key.time)
- if(index == -1){
- index = keys.length
- }
-
-
- keys = [...keys.slice(0,index), key, ...keys.slice(index,keys.length)]
- this[keyType+'Keys'].set(model,keys)
- this.updateTimeRange()
- }
-
- removeKey(model, keyType, key ){
- let keys = this[keyType+'Keys'].get(model)
- if(!keys)return console.warn('removeKey没找到key')
-
- let index = keys.indexOf(key)
- if(index > -1){
- if(keyType == 'clip'){
- key.action.stop()
- }
- keys.splice(index,1)
- this.updateTimeRange()
- }
- }
-
- reOrderKey(model, keyType, key ){
- this.removeKey(model, keyType, key )
- this.addKey(model, keyType, key )
- }
-
-
- at(time, delta, force){
- this.dispatchEvent({type:'atTime', time}) //该时间可以大于本动画持续时间
- this.cursorTime = time
- /* if(time > this.duration + maxClipFadeTime/2){
- for(let [model, keys] of this.clipKeys){
- model.actions.forEach(a=>a.stop())
- }
- } */
-
- let maxTime = this.duration+maxClipFadeTime/2
- if(time >= maxTime) time = maxTime
- if(this.time == time && !force)return
- this.time = time //真实值
-
-
- let oldDisToCam = this.camFollowObject?.length == 1 && this.keepDistance && this.camFollowObject[0].boundCenter.distanceTo(viewer.mainViewport.view.position)
-
-
-
- let transitionRatio = 0.05 * delta * 60 //渐变系数,越小缓动程度越高,越平滑 //假设标准帧率为60fps,当帧率低时(delta大时) 降低缓动。速度快时缓动太高会偏移路径
- let transitionRatio2 = 0.8 * delta * 60
-
-
- let posePathModels = [];
-
-
- [this.poseKeys, this.pathKeys/* , this.clipKeys */].forEach((map)=>{
- Array.from(map.keys()).forEach(model=>{
- posePathModels.includes(model) || posePathModels.push(model)
- })
- })
- /*
- 路径>关键帧。但是如果每条路径开头和结尾以及过渡时没有关键帧,保持路径开头和结尾的姿态
-
- */
- posePathModels.forEach(model=>{
-
-
- let pathKeys = this.pathKeys.get(model) || []
- let poseKeys = this.poseKeys.get(model) || []
-
- let atPath //是否在path中 至多只有一个
- let lastPath
- let nextPath
-
- if(pathKeys.length){
- pathKeys.find(key=>{
- if(key.path.points.length < 2) return
- let startToFade = key.time - maxClipFadeTime/2
- let endFade = key.time + key.dur + maxClipFadeTime/2
- atPath = time >= key.time && time <= key.time + key.dur
- if(atPath){
- atPath = key //找到一个就退出
- return true
- }
- if(key.time + key.dur < time) lastPath = key
- else if(key.time > time && !nextPath) nextPath = key
-
- })
- }
-
-
-
-
- if(poseKeys.length){
- tweens.scale = new Tween(poseKeys.map(e=>e.time), poseKeys.map(e=>e.scale))
- model.scale.copy(tweens.scale.lerp(time))
-
- }else{
- /* if(pathKeys.length){
- model.quaternion.copy(model.defaultAniPose?.quaternion || new THREE.Quaternion()) //设置路径朝向前要先还原
- } */
- }
-
-
-
- if(atPath){//沿着curve行走,目视curve前方 (参照CameraAnimationCurve,搜quaFromCurveTan)
- let percent = THREE.Math.clamp((time - atPath.time) / atPath.dur, 0, 1)
- let {position , quaternion} = this.getPoseAtPathKey(atPath, percent, model) //模型文件先保证其center在脚底,如果要我手动将bound底部对齐路径高度再说
- model.position.copy(position);
- model.quaternion.copy(quaternion)
-
- model.atPath = atPath
-
- }else{
- model.atPath = null
- poseKeys = poseKeys.slice()
- let addPathToPoseKey = (pathKey, percent)=>{ //把当前前后的path姿态加入帧
- let {position , quaternion} = this.getPoseAtPathKey(pathKey, percent, model)
- let fakeKey = {
- isPath : true,
- time: pathKey.time + pathKey.dur * percent,
- pos:position , qua:quaternion
- }
- let index = poseKeys.findIndex(e=>e.time > fakeKey.time)
- if(index == -1){
- index = poseKeys.length
- }
- poseKeys = [...poseKeys.slice(0,index), fakeKey, ...poseKeys.slice(index,poseKeys.length)]
- }
- lastPath && addPathToPoseKey(lastPath, 1)
- nextPath && addPathToPoseKey(nextPath, 0)
-
- if(poseKeys.length){
- tweens.pos = new Tween(poseKeys.map(e=>e.time), poseKeys.map(e=>e.pos))
- model.position.copy(tweens.pos.lerp(time))
- tweens.qua = new Tween(poseKeys.map(e=>e.time), poseKeys.map(e=>e.qua))
- model.quaternion.copy(tweens.qua.lerp(time))
- /* let poseKeys2 = poseKeys.filter(e=>e.isPath)//妈呀为什么这么写我忘了
- if(poseKeys2.length ){
- tweens.qua = new Tween(poseKeys2.map(e=>e.time), poseKeys2.map(e=>e.qua))
- model.quaternion.copy(tweens.qua.lerp(time))
- } */
- }
- }
- if(poseKeys.length || pathKeys.length){
- model.dispatchEvent('position_changed')
- model.dispatchEvent('rotation_changed')
- }
- })
-
-
-
-
-
-
-
-
-
-
-
- for(let [model, keys] of this.clipKeys){
- if(keys.length == 0) continue
-
- let weights = keys.map((key,i)=>{ //计算每个动作权重(幅度)。
-
- /* if(delta == void 0){//无缓动 但会造成和缓动时动作time不同
- return time >= key.time && time <= key.time + key.dur ? 1 : 0
- } */
- key.index_ = i
-
- let fadeTimeStart = Math.min(maxClipFadeTime, key.dur, (keys[i-1]?.dur || maxClipFadeTime )) / 2 //过渡时间不超过当前和前一个的 half of dur
- let fadeTimeEnd = Math.min(maxClipFadeTime, key.dur, (keys[i+1]?.dur || maxClipFadeTime )) / 2 //过渡时间不超过当前和后一个的 half of dur
-
-
- let startTime1 = key.time - fadeTimeStart
- let endTime1 = key.time + key.dur + fadeTimeEnd
- let startTime2 = key.time + fadeTimeStart
- let endTime2 = key.time + key.dur - fadeTimeEnd
-
- key.action.tempSW_ = {scale:0,weight:0,time: null, sameLinks:[]},
- key.tempTime_ = THREE.Math.clamp(time - key.time, 0, key.dur) //time - startTime1 //当前动作时间
- key.startTime1 = startTime1
- key.endTime1 = endTime1
-
-
- if(i==0 && time<startTime2){//开始前维持第一个动作
- return 1
- }else if(i == keys.length-1 && time > endTime2){//所有动作播完后维持最后一个动作
- return 1
- }else{
- if(time < startTime1 || time > endTime1)return 0 //out bound
-
- if(time >= startTime2 && time <= endTime2 ) return 1
-
- if(time < startTime2 ){
- return Potree.math.linearClamp(time, [startTime1,startTime2],[0,1])
- }else{
- return Potree.math.linearClamp(time, [endTime2,endTime1],[1,0])
- }
- }
- })//最多有两个>0的,在过渡
-
- let animateActions = [] //在播的动作
- keys.forEach((key,i)=>{ weights[i]>0 && !animateActions.includes(key.action) && (key.action.tempSW_ = {scale:0,weight:0,time: null, sameLinks:[]}, animateActions.push(key.action) )})
- //万一前后是一个动作…… 所以用tempSW_计算总值
-
- keys.forEach((key,i)=>{ //要找到当前action之前所有不间断的所有key,他们之间要连续播放
- if(animateActions.includes(key.action)){
- if(key.startTime1 < time){//已播部分的key
- let last = key.action.tempSW_.sameLinks[key.action.tempSW_.sameLinks.length - 1]
- if(last){
- if( key.index_ == last.index_ + 1 && key.startTime1 <= last.endTime1 ){ //相连
- key.action.tempSW_.sameLinks.push(key)
- }else{
- key.action.tempSW_.sameLinks = [key] //clear
- }
- }else{
- key.action.tempSW_.sameLinks = [key]
- }
- }
- }
- })
-
-
- keys.forEach((key,i)=>{
- if(animateActions.includes(key.action)){
- let weight = weights[i] * key.weight //权重乘以自身幅度
- if(weight>0){//最多两个
- key.action.play()
- key.action.paused = true //停在某帧 //如果没有点击该动作块的话 不停
-
- key.action.tempSW_.weight += weight
- key.tempTime_ >= 0 && (key.action.tempSW_.scale = key.speed) //相同动作不允许叠加速度
-
- if(key.action.tempSW_.time == null){//如果两个动作相同 只需在第一个计算出总和
- let timeSum = 0
- key.action.tempSW_.sameLinks.forEach(key_ =>{
- timeSum += (key_.tempTime_ % (key_.action._clip.duration / key_.speed)) * key_.speed //相同动作可能速度不同,算出每个clip的时间
- })
- key.action.tempSW_.time = timeSum % key.action._clip.duration
- }
-
- //(老版本,过渡时播放时间会延长一点,有交集):
- //key.action.tempSW_.time == null && (key.action.tempSW_.time = key.tempTime_) //相同动作优先用前一个的时间
- //key.action.tempSW_.scale += key.speed // * weights[i] //乘以weight在开始和结束作为缓动效果好,但是不好计算实时time
- //speed time都没有交集,只有weight有,为了过渡
- }
- }else{
- key.action.stop() //不启动动画
-
- }
- })
-
- animateActions.forEach(action=>{
- action.setEffectiveTimeScale(action.tempSW_.scale) //speed 只有没paused时有效 这里都paused的所以没用
- action.setEffectiveWeight(action.tempSW_.weight );
- action.time = action.tempSW_.time //(action.tempSW_.time % (action._clip.duration / action.tempSW_.scale)) * action.tempSW_.scale //只有paused时有效
- //console.log('action', action._clip.name, action.time, action.weight, action.tempSW_.scale )
-
-
- })
-
- //model.mixer.timeScale = 1 ;
-
- }
-
- viewer.objs.children.forEach(obj=>{
- if(!obj.actions?.length)return
- let clipState = obj.actions.map(action=>{
- let played = action._mixer._isActiveAction( action );
- let paused = action.paused
- let time = action.time
- let weight = action.weight
- return {played,paused,time,weight}
- })
- clipState = JSON.stringify(clipState)
- if(obj.clipState != clipState){//动作是否改变
- obj.traverse(e=>e.isSkinnedMesh && (e.boundingSphere = null)) //动画会导致bound改变,清空,raycast时重新计算bound,否则hover不到模型
- obj.clipChanged = true
- }
- obj.clipState = clipState
-
- })
-
-
-
- {
-
- if(this.camFollowObject && !viewer.scene.monitors.some(e=>e.isWatching)){//in front of model
- if(this.camFollowObject.length == 1){
- let model = this.camFollowObject[0]
- if(viewer.images360.latestRequestMode == 'showPointCloud'){
- if(this.camFaceToObject){
- let dis = 4;
- let dir = new THREE.Vector3(0,0.1,1).normalize()//稍微朝上
- dir.multiplyScalar(dis).applyQuaternion(model.quaternion)
- let pos = new THREE.Vector3().addVectors(model.boundCenter, dir)
- viewer.mainViewport.view.position.copy(pos)
- viewer.mainViewport.view.lookAt(model.boundCenter)
- }else if(this.keepDistance){ //不改镜头方向 保持一定角度。如果要改镜头方向,把lookAt提前
- viewer.mainViewport.view.position.subVectors(model.boundCenter, viewer.mainViewport.view.direction.clone().multiplyScalar(oldDisToCam))
- viewer.mainViewport.view.radius = oldDisToCam
- }else{
- viewer.mainViewport.view.lookAt(model.boundCenter)
- }
- }else{
- viewer.mainViewport.view.lookAt(model.boundCenter)
- }
- }else{
- viewer.modules.MergeEditor.focusOn(this.camFollowObject, 0, true/* ,false,dirAve */)
- }
- }
- }
-
-
-
-
- viewer.dispatchEvent('content_changed')
-
- }
-
- getPoseAtPathKey(key, percent, model){
- let delta = 0.001
- let percent2 = percent + delta
- let curve = key.path.curve.clone()
- if(key.reverse) curve.points.reverse()
- let position = curve.getPointAt(percent);
- let pathQua, quaternion
-
- if(percent2 <= 1){
- let position2 = curve.getPointAt(percent2);
- pathQua = math.getQuaFromPosAim(position2, position)
- }else{
- percent2 = percent - delta
- let position2 = curve.getPointAt(percent2);
- pathQua = math.getQuaFromPosAim(position, position2)
- }
-
- pathQua.multiplyQuaternions( pathQua, rot90Qua ); //这是当模型导进来就旋转正确时的quaternion
- key.curQua_ = pathQua.clone() //记录下
- if(model.quaAtPath){
- quaternion = new THREE.Quaternion().multiplyQuaternions(pathQua, model.quaAtPath)
- }else{
- quaternion = pathQua.clone()
- }
-
-
- //model && quaternion.multiplyQuaternions( quaternion, model.quaternion ); //应用当前已有的quaternion
- //如果要将模型底部中心对准路径,需要先修改好模型scale ,然后boundingBox中心应用scale和qua, 加到position里
- //目前两个人物模型刚好模型pivot在脚底,如果是其他物体甚至直接用curve的朝向不太对,没有明确朝向。除非所有模型都保持上路径前的朝向
- //或者pos的z还用之前的
- //产品说位置偏移不管它,因为路径可以隐藏和修改。只要记录相对旋转即可。
- return {position, quaternion}
- }
-
-
-
-
-
- getModelQuaAtPath(model){ //当前时间在路径上时,旋转模型后立即执行该函数,获取相对旋转值
- if(!model.atPath)return
- let qua = new THREE.Quaternion().multiplyQuaternions(model.atPath.curQua_.clone().invert(), model.quaternion)
-
- //console.log('getModelQuaAtPath',qua)
- model.quaAtPath = qua //相对旋转
- return qua
- }
-
- play({ time = -maxClipFadeTime/2}={}){//动画时长比duration多一个maxClipFadeTime,为了给开始和结束动画过渡
-
-
- this.updateTimeRange()
- this.playing && this.pause()
-
-
- let maxTime = this.duration+maxClipFadeTime/2
- this.playing = true
-
- this.cursorTime = time
-
- this.onUpdate = (e)=>{
- this.cursorTime += e.delta
- if(!Potree.settings.isOfficial && time > maxTime) this.cursorTime = maxTime
- this.at(this.cursorTime, e.delta)
- if(!Potree.settings.isOfficial && time > maxTime) {
- this.dispatchEvent('stop')
- for(let [model, keys] of this.clipKeys){
- model.actions.forEach(a=>a.stop())
- }
- this.pause()
- }
-
- }
- viewer.addEventListener("update_start", this.onUpdate);
-
- }
-
- pause(){
- this.playing = false
- viewer.removeEventListener("update_start", this.onUpdate);
- /* for(let [model, keys] of this.clipKeys){
- model.actions.forEach(a=>a.stop())
- } */
- viewer.dispatchEvent('content_changed')
-
- }
-
-
- setCameraFollow(camFollowObject){//for test
- this.camFollowObject = camFollowObject
- if(!camFollowObject)return
-
- //Potree.settings.displayMode = 'showPointCloud'
-
- if(!(this.camFollowObject instanceof Array)){//支持相机跟随多个物体,对着bound的中心
- this.camFollowObject = [this.camFollowObject]
- }
-
- this.camFollowObject = this.camFollowObject.map(object=>{
- if(typeof object == 'string'){
- return viewer.objs.children.find(e=>e.name == object)
- }else{
- return object
- }
- })
-
- }
-
-
- updateTimeRange(){
- let maxTime = 0
- for(let [model, keys] of this.poseKeys){
- keys.length>0 && (maxTime = Math.max(maxTime, keys[keys.length - 1].time))
- }
-
- for(let [model, keys] of this.clipKeys){
- keys.length>0 && (maxTime = Math.max(maxTime, keys[keys.length - 1].time + keys[keys.length - 1].dur))
- }
-
- for(let [model, keys] of this.pathKeys){
- keys.length>0 && (maxTime = Math.max(maxTime, keys[keys.length - 1].time + keys[keys.length - 1].dur))
- }
- this.duration = maxTime //不算开始和结束动画的过渡时间的话
-
-
- /* for(let [model, keys] of this.clipKeys){
- max = Math.max(maxTime, keys[keys.length - 1].time + keys[keys.length - 1].dur)
- } */
- }
-
-
- /* removeModelCallback(model){
- this.poseKeys.get
- } */
-
-
- /////////////////////////////////
-
-
- addPoseKey({model,time,index }={}){
- /* if(replace){
- this.removeKey(model,'pose', index)
- } */
- let keys = this.poseKeys.get(model)
- if(!keys){
- keys = []
- }
- let key = {
- time,
- qua: model.quaternion.clone(),
- scale: model.scale.clone(),
- pos: model.position.clone()
- }
- if(index == void 0)index = keys.length
- keys = [...keys.slice(0,index), key, ...keys.slice(index,keys.length)]
- this.poseKeys.set(model,keys)
- return key
- }
-
-
-
- addClipKey({model, time, index, dur, actionIndex, weight=1, speed=1/* , replace */}={}){
-
- /* if(replace){
- this.removeKey(model,'clip',index)
- } */
- let keys = this.clipKeys.get(model)
- if(!keys){
- keys = []
- }
- let key = {
- time, //startTime
- dur,
- action: model.actions[actionIndex],
- speed, weight,
- }
- if(index == void 0)index = keys.length
- keys = [...keys.slice(0,index), key, ...keys.slice(index,keys.length)]
- this.clipKeys.set(model,keys)
- }
-
-
-
- addPathKey({model, time, index, dur, path/* , replace */ }={}){//what if path is deleted ?
- /* if(replace){
- this.removeKey(model,'path',index)
- } */
- let keys = this.pathKeys.get(model)
- if(!keys){
- keys = []
- }
- let key = {
- time, //startTime
- dur,
- path,
- }
- if(index == void 0)index = keys.length
- keys = [...keys.slice(0,index), key, ...keys.slice(index,keys.length)]
- this.pathKeys.set(model,keys)
- }
-
- addDescKey({model, time, index, dur, desc/* , replace */ }={}){
- /* if(replace){
- this.removeKey(model,'desc',index)
- } */
- let keys = this.descKeys.get(model)
- if(!keys){
- keys = []
- }
- let key = {
- time, //startTime
- dur,
- desc,
- }
- if(index == void 0)index = keys.length
- keys = [...keys.slice(0,index), key, ...keys.slice(index,keys.length)]
- this.descKeys.set(model,keys)
- }
-
-
-
-
-
- save(){//for test, 注意保证每个模型名字不同
- let data = {poseKeys:{}, clipKeys:{}}
- for(let [model, keys] of this.clipKeys){
- data.clipKeys[model.name] = keys.map(key=>{
- return {
- actionIndex: model.actions.indexOf(key.action),
- time: key.time, dur:key.dur, weight:key.weight, speed:key.speed
- }
- })
- }
-
- for(let [model, keys] of this.poseKeys){
- data.poseKeys[model.name] = keys.map(key=>{
- return {
- qua: key.qua.toArray(), pos: key.pos.toArray(), scale:key.scale.toArray(),
- time: key.time,
- }
- })
- }
- console.log(JSON.stringify(data))
- return data
- }
-
-
- buildFromData(data){
- if(typeof data == 'string'){
- data = JSON.parse(data)
- }
- for(let name in data.poseKeys){
- let model = viewer.objs.children.find(e=>e.name == name)
- if(!model){
- console.warn('没找到pose模型',name)
- continue
- }
- let keys = data.poseKeys[name].map(e=>{
- return {
- qua: new THREE.Quaternion().fromArray(e.qua),
- pos: new THREE.Vector3().fromArray(e.pos),
- scale: new THREE.Vector3().fromArray(e.scale),
- time: e.time
- }
- })
- this.poseKeys.set(model,keys)
- }
-
- for(let name in data.clipKeys){
- let model = viewer.objs.children.find(e=>e.name == name)
- if(!model){
- console.warn('没找到clip模型',name)
- continue
- }
- let keys = data.clipKeys[name].map(e=>{
- return {
- action: model.actions[e.actionIndex],
- time: e.time, dur: e.dur, weight:e.weight, speed:e.speed
- }
- })
- this.clipKeys.set(model,keys)
- }
- }
-
-
- ifContainsModel(model){//动画帧里是否包含它
- return [this.poseKeys, this.pathKeys, this.clipKeys].some((e)=>{
- return e.has(model)
- })
- }
-
-
- }
- /*
- function executeCrossFade( startAction, endAction, duration ) {
- // Not only the start action, but also the end action must get a weight of 1 before fading
- // (concerning the start action this is already guaranteed in this place)
- if ( endAction ) {
- setWeight( endAction, 1 );
- endAction.time = 0;
- if ( startAction ) {
- // Crossfade with warping
- startAction.crossFadeTo( endAction, duration, true );
- } else {
- // Fade in
- endAction.fadeIn( duration );
- }
- } else {
- // Fade out
- startAction.fadeOut( duration );
- }
- }
- function setWeight( action, weight ) {
- action.enabled = true;
- window.ani1 || action.setEffectiveTimeScale( 1 );
- action.setEffectiveWeight( weight );
- } */
-
- /* autoActionSpeed(action){ //要获取这段时间走过的路程很难,还是延期吧. 而且一段动作要对应多个速度不同的位移,是不可能的。
- return dis / dur
- } */
- /*
- 动作自动计算步伐 幅度(weight)或 速度
- timeScale * modelStepSizeRatio * weight = dis / dur
- 速度 每个模型的步长系数 幅度
-
-
-
- 如果人的bone attach 物品,物品就被add到人身上,需要click出物品
- //测试:为人加物体, 需要先选中物品
- let obj = viewer.modules.MergeEditor.selected
- viewer.objs.children.find(e=>e.name == 'Man.glb').skeletonHelper.bones[34].attach(obj); //左手骨和物品绑定
- //viewer.objs.children.find(e=>e.name == 'Soldier.glb').skeletonHelper.bones[9].attach(obj); //右手骨和物品绑定
- obj.updateMatrixWorld()
- obj.dispatchEvent({type:'position_changed',byControl:true })
- obj.dispatchEvent({type:'rotation_changed',byControl:true })
-
-
- 物体带动骨骼自动做动作 setAniIK
- */
|