Skip to content

Commit c0f0bf4

Browse files
committed
imp: avatar rate checking amortization
1 parent 4b6eb30 commit c0f0bf4

File tree

4 files changed

+58
-20
lines changed

4 files changed

+58
-20
lines changed

src/core/World.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Settings } from './systems/Settings'
55
import { Collections } from './systems/Collections'
66
import { Apps } from './systems/Apps'
77
import { Anchors } from './systems/Anchors'
8+
import { Avatars } from './systems/Avatars'
89
import { Events } from './systems/Events'
910
import { Chat } from './systems/Chat'
1011
import { Blueprints } from './systems/Blueprints'
@@ -38,6 +39,7 @@ export class World extends EventEmitter {
3839
this.register('collections', Collections)
3940
this.register('apps', Apps)
4041
this.register('anchors', Anchors)
42+
this.register('avatars', Avatars)
4143
this.register('events', Events)
4244
this.register('scripts', Scripts)
4345
this.register('chat', Chat)

src/core/extras/createVRMFactory.js

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ const m1 = new THREE.Matrix4()
1313

1414
const FORWARD = new THREE.Vector3(0, 0, -1)
1515

16-
const DIST_CHECK_RATE = 2 // once every second
1716
const DIST_MIN_RATE = 1 / 5 // 5 times per second
1817
const DIST_MAX_RATE = 1 / 60 // 40 times per second
1918
const DIST_MIN = 5 // <= 5m = max rate
2019
const DIST_MAX = 60 // >= 60m = min rate
2120

21+
const MAX_GAZE_DISTANCE = 40
22+
2223
const material = new THREE.MeshBasicMaterial()
2324

2425
const AimAxis = {
@@ -285,35 +286,31 @@ export function createVRMFactory(glb, setupMaterial) {
285286

286287
let elapsed = 0
287288
let rate = 0
288-
let rateCheckedAt = 999
289289
let rateCheck = true
290+
let distance
291+
292+
const updateRate = () => {
293+
const vrmPos = v1.setFromMatrixPosition(vrm.scene.matrix)
294+
const camPos = v2.setFromMatrixPosition(hooks.camera.matrixWorld) // prettier-ignore
295+
distance = vrmPos.distanceTo(camPos)
296+
const clampedDistance = Math.max(distance - DIST_MIN, 0)
297+
const normalizedDistance = Math.min(clampedDistance / (DIST_MAX - DIST_MIN), 1) // prettier-ignore
298+
rate = DIST_MAX_RATE + normalizedDistance * (DIST_MIN_RATE - DIST_MAX_RATE) // prettier-ignore
299+
// console.log('distance', distance)
300+
// console.log('rate per second', 1 / rate)
301+
}
302+
290303
const update = delta => {
291304
elapsed += delta
292-
let should = true
293-
if (rateCheck) {
294-
// periodically calculate update rate based on distance to camera
295-
rateCheckedAt += delta
296-
if (rateCheckedAt >= DIST_CHECK_RATE) {
297-
const vrmPos = v1.setFromMatrixPosition(vrm.scene.matrix)
298-
const camPos = v2.setFromMatrixPosition(hooks.camera.matrixWorld) // prettier-ignore
299-
const distance = vrmPos.distanceTo(camPos)
300-
const clampedDistance = Math.max(distance - DIST_MIN, 0)
301-
const normalizedDistance = Math.min(clampedDistance / (DIST_MAX - DIST_MIN), 1) // prettier-ignore
302-
rate = DIST_MAX_RATE + normalizedDistance * (DIST_MIN_RATE - DIST_MAX_RATE) // prettier-ignore
303-
// console.log('distance', distance)
304-
// console.log('rate per second', 1 / rate)
305-
rateCheckedAt = 0
306-
}
307-
should = elapsed >= rate
308-
}
305+
const should = rateCheck ? elapsed >= rate : true
309306
if (should) {
310307
mixer.update(elapsed)
311308
skeleton.bones.forEach(bone => bone.updateMatrixWorld())
312309
skeleton.update = THREE.Skeleton.prototype.update
313310
if (!currentEmote) {
314311
updateLocomotion(delta)
315312
}
316-
if (loco.gazeDir && (currentEmote ? currentEmote.gaze : true)) {
313+
if (loco.gazeDir && distance < MAX_GAZE_DISTANCE && (currentEmote ? currentEmote.gaze : true)) {
317314
aimBone('neck', loco.gazeDir, delta, {
318315
minAngle: -30,
319316
maxAngle: 30,
@@ -619,6 +616,7 @@ export function createVRMFactory(glb, setupMaterial) {
619616
setEmote,
620617
setFirstPerson,
621618
update,
619+
updateRate,
622620
getBoneTransform,
623621
setLocomotion,
624622
setVisible(visible) {

src/core/nodes/Avatar.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export class Avatar extends Node {
4444
// this._disableRateCheck = null
4545
}
4646
this.ctx.world?.setHot(this.instance, true)
47+
this.ctx.world?.avatars.add(this.instance)
4748
this.onLoad?.()
4849
}
4950
}
@@ -62,6 +63,7 @@ export class Avatar extends Node {
6263
this.n++
6364
if (this.instance) {
6465
this.ctx.world?.setHot(this.instance, false)
66+
this.ctx.world?.avatars.remove(this.instance)
6567
this.instance.destroy()
6668
this.instance = null
6769
}

src/core/systems/Avatars.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { System } from './System'
2+
3+
/**
4+
* Avatars System
5+
*
6+
* - Runs rate checks one avatar per frame (amortization)
7+
*
8+
*/
9+
export class Avatars extends System {
10+
constructor(world) {
11+
super(world)
12+
this.avatars = []
13+
this.cursor = 0
14+
}
15+
16+
add(avatar) {
17+
this.avatars.push(avatar)
18+
}
19+
20+
remove(avatar) {
21+
const idx = this.avatars.indexOf(avatar)
22+
if (idx === -1) return
23+
this.avatars.splice(idx, 1)
24+
}
25+
26+
update() {
27+
if (!this.avatars.length) return
28+
const avatar = this.avatars[this.cursor % this.avatars.length]
29+
avatar.updateRate()
30+
this.cursor++
31+
}
32+
33+
destroy() {
34+
this.avatars = []
35+
}
36+
}

0 commit comments

Comments
 (0)