spoint 0.1.28 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/animation.js +7 -6
- package/client/app.js +35 -28
- package/package.json +1 -1
package/client/animation.js
CHANGED
|
@@ -46,15 +46,15 @@ function filterUpperBodyTracks(clip) {
|
|
|
46
46
|
return new THREE.AnimationClip(clip.name, clip.duration, filteredTracks)
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
function
|
|
50
|
-
// Get all bone/mesh names that exist in target
|
|
49
|
+
function buildValidBoneSet(targetObj) {
|
|
51
50
|
const validBones = new Set()
|
|
52
51
|
targetObj.traverse(child => {
|
|
53
|
-
if (child.isBone || child.isSkinnedMesh)
|
|
54
|
-
validBones.add(child.name)
|
|
55
|
-
}
|
|
52
|
+
if (child.isBone || child.isSkinnedMesh) validBones.add(child.name)
|
|
56
53
|
})
|
|
54
|
+
return validBones
|
|
55
|
+
}
|
|
57
56
|
|
|
57
|
+
function filterValidClipTracks(clip, validBones) {
|
|
58
58
|
const validTracks = clip.tracks.filter(track => {
|
|
59
59
|
const boneName = extractBoneName(track.name)
|
|
60
60
|
if (!validBones.has(boneName)) {
|
|
@@ -140,6 +140,7 @@ export function createPlayerAnimator(vrm, allClips, vrmVersion, animConfig = {})
|
|
|
140
140
|
const additiveActions = new Map()
|
|
141
141
|
|
|
142
142
|
const clips = allClips.normalizedClips || allClips.rawClips || allClips
|
|
143
|
+
const validBones = buildValidBoneSet(root)
|
|
143
144
|
|
|
144
145
|
for (const [name, clip] of clips) {
|
|
145
146
|
if (!STATES[name]) continue
|
|
@@ -149,7 +150,7 @@ export function createPlayerAnimator(vrm, allClips, vrmVersion, animConfig = {})
|
|
|
149
150
|
console.log(`[anim] ${name} tracks:`, clip.tracks.map(t => extractBoneName(t.name)))
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
let playClip = filterValidClipTracks(clip,
|
|
153
|
+
let playClip = filterValidClipTracks(clip, validBones)
|
|
153
154
|
|
|
154
155
|
if (cfg.upperBody) {
|
|
155
156
|
const upperBodyClip = filterUpperBodyTracks(playClip)
|
package/client/app.js
CHANGED
|
@@ -659,6 +659,7 @@ scene.add(ground)
|
|
|
659
659
|
|
|
660
660
|
const loadingManager = new THREE.LoadingManager()
|
|
661
661
|
loadingManager.onError = (url) => console.warn('[THREE] Failed to load:', url)
|
|
662
|
+
THREE.Cache.enabled = true
|
|
662
663
|
const gltfLoader = new GLTFLoader(loadingManager)
|
|
663
664
|
const dracoLoader = new DRACOLoader(loadingManager)
|
|
664
665
|
dracoLoader.setDecoderPath('/draco/')
|
|
@@ -771,13 +772,15 @@ function initVRMFeatures(id, vrm) {
|
|
|
771
772
|
playerExpressions.set(id, features)
|
|
772
773
|
}
|
|
773
774
|
|
|
775
|
+
const _lookTargetVec = new THREE.Vector3()
|
|
776
|
+
|
|
774
777
|
function updateVRMFeatures(id, dt, targetPosition) {
|
|
775
778
|
const features = playerExpressions.get(id)
|
|
776
779
|
if (!features) return
|
|
777
780
|
if (features.springBone) features.springBone.update(dt)
|
|
778
781
|
if (features.lookAt && targetPosition) {
|
|
779
|
-
|
|
780
|
-
features.lookAt.lookAt(
|
|
782
|
+
_lookTargetVec.set(targetPosition.x, targetPosition.y + 1.6, targetPosition.z)
|
|
783
|
+
features.lookAt.lookAt(_lookTargetVec)
|
|
781
784
|
}
|
|
782
785
|
if (features.expressions) {
|
|
783
786
|
features.blinkTimer += dt
|
|
@@ -891,18 +894,13 @@ function rebuildEntityHierarchy(entities) {
|
|
|
891
894
|
if (!mesh) continue
|
|
892
895
|
|
|
893
896
|
const parentId = entityParentMap.get(e.id)
|
|
894
|
-
const currentParent = mesh.parent
|
|
895
|
-
const currentParentId = Array.from(entityParentMap.entries()).find(([k, v]) => v === null || entityMeshes.get(k) === currentParent)?.[0]
|
|
897
|
+
const currentParent = mesh.parent !== scene ? mesh.parent : null
|
|
896
898
|
|
|
897
899
|
if (parentId === null) {
|
|
898
|
-
if (currentParent
|
|
899
|
-
scene.add(mesh)
|
|
900
|
-
}
|
|
900
|
+
if (currentParent) scene.add(mesh)
|
|
901
901
|
} else {
|
|
902
902
|
const parentMesh = entityMeshes.get(parentId)
|
|
903
|
-
if (parentMesh && parentMesh !== currentParent)
|
|
904
|
-
parentMesh.add(mesh)
|
|
905
|
-
}
|
|
903
|
+
if (parentMesh && parentMesh !== currentParent) parentMesh.add(mesh)
|
|
906
904
|
}
|
|
907
905
|
}
|
|
908
906
|
}
|
|
@@ -921,8 +919,8 @@ function loadEntityModel(entityId, entityState) {
|
|
|
921
919
|
} else {
|
|
922
920
|
group = buildEntityMesh(entityId, entityState.custom)
|
|
923
921
|
}
|
|
924
|
-
group.position.set(
|
|
925
|
-
|
|
922
|
+
const ep = entityState.position; group.position.set(ep[0], ep[1], ep[2])
|
|
923
|
+
const er = entityState.rotation; if (er) group.quaternion.set(er[0], er[1], er[2], er[3])
|
|
926
924
|
scene.add(group)
|
|
927
925
|
entityMeshes.set(entityId, group)
|
|
928
926
|
pendingLoads.delete(entityId)
|
|
@@ -933,13 +931,20 @@ function loadEntityModel(entityId, entityState) {
|
|
|
933
931
|
const url = entityState.model.startsWith('./') ? '/' + entityState.model.slice(2) : entityState.model
|
|
934
932
|
gltfLoader.load(url, (gltf) => {
|
|
935
933
|
const model = gltf.scene
|
|
936
|
-
model.position.set(
|
|
937
|
-
|
|
938
|
-
|
|
934
|
+
const mp = entityState.position; model.position.set(mp[0], mp[1], mp[2])
|
|
935
|
+
const mr = entityState.rotation; if (mr) model.quaternion.set(mr[0], mr[1], mr[2], mr[3])
|
|
936
|
+
const colliders = []
|
|
937
|
+
model.traverse(c => {
|
|
938
|
+
if (c.isMesh) {
|
|
939
|
+
c.castShadow = true
|
|
940
|
+
c.receiveShadow = true
|
|
941
|
+
if (!c.isSkinnedMesh) { c.matrixAutoUpdate = false; colliders.push(c) }
|
|
942
|
+
if (c.material) { c.material.shadowSide = THREE.DoubleSide; c.material.roughness = 1; c.material.metalness = 0; if (c.material.specularIntensity !== undefined) c.material.specularIntensity = 0 }
|
|
943
|
+
}
|
|
944
|
+
})
|
|
945
|
+
model.updateMatrixWorld(true)
|
|
939
946
|
scene.add(model)
|
|
940
947
|
entityMeshes.set(entityId, model)
|
|
941
|
-
const colliders = []
|
|
942
|
-
model.traverse(c => { if (c.isMesh && !c.isSkinnedMesh) colliders.push(c) })
|
|
943
948
|
cam.setEnvironment(colliders)
|
|
944
949
|
scene.remove(ground)
|
|
945
950
|
fitShadowFrustum()
|
|
@@ -978,15 +983,17 @@ const client = new PhysicsNetworkClient({
|
|
|
978
983
|
const mesh = playerMeshes.get(p.id)
|
|
979
984
|
const feetOff = mesh?.userData?.feetOffset ?? 1.3
|
|
980
985
|
const tx = p.position[0], ty = p.position[1] - feetOff, tz = p.position[2]
|
|
981
|
-
playerTargets.
|
|
986
|
+
const existingTarget = playerTargets.get(p.id)
|
|
987
|
+
if (existingTarget) { existingTarget.x = tx; existingTarget.y = ty; existingTarget.z = tz }
|
|
988
|
+
else playerTargets.set(p.id, { x: tx, y: ty, z: tz })
|
|
982
989
|
playerStates.set(p.id, p)
|
|
983
990
|
const dx = tx - mesh.position.x, dy = ty - mesh.position.y, dz = tz - mesh.position.z
|
|
984
991
|
if (!mesh.userData.initialized || dx * dx + dy * dy + dz * dz > 100) { mesh.position.set(tx, ty, tz); mesh.userData.initialized = true }
|
|
985
992
|
}
|
|
986
993
|
for (const e of smoothState.entities) {
|
|
987
994
|
const mesh = entityMeshes.get(e.id)
|
|
988
|
-
if (mesh && e.position) mesh.position.set(
|
|
989
|
-
if (mesh && e.rotation) mesh.quaternion.set(
|
|
995
|
+
if (mesh && e.position) mesh.position.set(e.position[0], e.position[1], e.position[2])
|
|
996
|
+
if (mesh && e.rotation) mesh.quaternion.set(e.rotation[0], e.rotation[1], e.rotation[2], e.rotation[3])
|
|
990
997
|
if (!entityMeshes.has(e.id)) loadEntityModel(e.id, e)
|
|
991
998
|
}
|
|
992
999
|
rebuildEntityHierarchy(smoothState.entities)
|
|
@@ -1380,21 +1387,21 @@ function animate(timestamp) {
|
|
|
1380
1387
|
const mesh = playerMeshes.get(id)
|
|
1381
1388
|
if (!mesh) continue
|
|
1382
1389
|
const vx = ps.velocity?.[0] || 0, vz = ps.velocity?.[2] || 0
|
|
1383
|
-
if (
|
|
1390
|
+
if (vx * vx + vz * vz > 0.25) mesh.userData.lastYaw = Math.atan2(vx, vz)
|
|
1384
1391
|
if (mesh.userData.lastYaw !== undefined) {
|
|
1385
1392
|
let diff = mesh.userData.lastYaw - mesh.rotation.y
|
|
1386
|
-
|
|
1387
|
-
while (diff < -Math.PI) diff += Math.PI * 2
|
|
1393
|
+
diff = diff - Math.PI * 2 * Math.round(diff / (Math.PI * 2))
|
|
1388
1394
|
mesh.rotation.y += diff * lerpFactor
|
|
1389
1395
|
}
|
|
1390
1396
|
const target = playerTargets.get(id)
|
|
1391
1397
|
updateVRMFeatures(id, frameDt, target)
|
|
1392
1398
|
if (id !== client.playerId && ps.lookPitch !== undefined) {
|
|
1393
|
-
const
|
|
1394
|
-
if (
|
|
1395
|
-
const
|
|
1396
|
-
if (
|
|
1399
|
+
const features = playerExpressions.get(id)
|
|
1400
|
+
if (features && !features._headBone) {
|
|
1401
|
+
const vrm = playerVrms.get(id)
|
|
1402
|
+
if (vrm?.humanoid) features._headBone = vrm.humanoid.getNormalizedBoneNode('head')
|
|
1397
1403
|
}
|
|
1404
|
+
if (features?._headBone) features._headBone.rotation.x = -(ps.lookPitch || 0) * 0.6
|
|
1398
1405
|
}
|
|
1399
1406
|
}
|
|
1400
1407
|
for (const [eid, mesh] of entityMeshes) {
|
|
@@ -1409,7 +1416,7 @@ function animate(timestamp) {
|
|
|
1409
1416
|
if (engineCtx.facial) engineCtx.facial.update(frameDt)
|
|
1410
1417
|
uiTimer += frameDt
|
|
1411
1418
|
if (latestState && uiTimer >= 0.25) { uiTimer = 0; renderAppUI(latestState) }
|
|
1412
|
-
const local =
|
|
1419
|
+
const local = playerStates.get(client.playerId)
|
|
1413
1420
|
const inVR = renderer.xr.isPresenting
|
|
1414
1421
|
if (!inVR || cam.getEditMode()) {
|
|
1415
1422
|
cam.update(local, playerMeshes.get(client.playerId), frameDt, latestInput)
|