spoint 0.1.41 → 0.1.43
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/SKILL.md +22 -0
- package/client/animation.js +14 -5
- package/client/app.js +17 -6
- package/client/index.html +1 -1
- package/client/vendor/three-mesh-bvh.module.js +8373 -0
- package/package.json +1 -1
- package/src/apps/AppContext.js +18 -0
- package/src/physics/World.js +7 -0
package/SKILL.md
CHANGED
|
@@ -239,6 +239,14 @@ ctx.physics.addCapsuleCollider(radius, fullHeight)
|
|
|
239
239
|
ctx.physics.addTrimeshCollider()
|
|
240
240
|
// Builds static mesh from entity.model path. Static only.
|
|
241
241
|
|
|
242
|
+
ctx.physics.addConvexCollider(points)
|
|
243
|
+
// points: flat Float32Array or Array of [x,y,z,x,y,z,...] vertex positions
|
|
244
|
+
// Builds a ConvexHullShape from the provided point cloud. Supports all motion types.
|
|
245
|
+
|
|
246
|
+
ctx.physics.addConvexFromModel(meshIndex = 0)
|
|
247
|
+
// Extracts vertex positions from entity.model GLB and builds ConvexHullShape.
|
|
248
|
+
// Simpler and faster than trimesh for dynamic objects like vehicles/crates.
|
|
249
|
+
|
|
242
250
|
ctx.physics.addForce([fx, fy, fz]) // Impulse: velocity += force / mass
|
|
243
251
|
ctx.physics.setVelocity([vx, vy, vz]) // Set velocity directly
|
|
244
252
|
```
|
|
@@ -544,6 +552,12 @@ ctx.physics.addCapsuleCollider(radius, fullHeight)
|
|
|
544
552
|
ctx.physics.addTrimeshCollider()
|
|
545
553
|
// Static trimesh from entity.model. Only for static bodies.
|
|
546
554
|
|
|
555
|
+
ctx.physics.addConvexCollider(points)
|
|
556
|
+
// points: flat array [x,y,z,x,y,z,...]. Supports all motion types (dynamic/kinematic/static).
|
|
557
|
+
|
|
558
|
+
ctx.physics.addConvexFromModel(meshIndex = 0)
|
|
559
|
+
// Extracts vertices from entity.model GLB and builds ConvexHullShape. Good for dynamic vehicles/crates.
|
|
560
|
+
|
|
547
561
|
ctx.physics.addForce([fx, fy, fz]) // velocity += force / mass
|
|
548
562
|
ctx.physics.setVelocity([vx, vy, vz])
|
|
549
563
|
```
|
|
@@ -1068,6 +1082,14 @@ The engine manually applies `gravity[1] * dt` to Y velocity. This is already han
|
|
|
1068
1082
|
|
|
1069
1083
|
`addTrimeshCollider()` creates a static mesh. No dynamic or kinematic trimesh support.
|
|
1070
1084
|
|
|
1085
|
+
### Convex hull for dynamic objects
|
|
1086
|
+
|
|
1087
|
+
Use `addConvexCollider(points)` or `addConvexFromModel()` for dynamic/kinematic bodies that need shape-accurate physics (vehicles, crates). Convex hulls support all motion types unlike trimesh. `addConvexFromModel()` reads vertices from the entity's GLB at setup time - call it after setting `entity.model`.
|
|
1088
|
+
|
|
1089
|
+
### Animation library is cached globally
|
|
1090
|
+
|
|
1091
|
+
`loadAnimationLibrary()` loads `/anim-lib.glb` only once and caches the result. All subsequent calls return the cached result immediately. The library is also pre-fetched in parallel with the VRM download during initialization.
|
|
1092
|
+
|
|
1071
1093
|
### Tick drops under load
|
|
1072
1094
|
|
|
1073
1095
|
TickSystem processes max 4 ticks per loop. If the server falls more than 4 ticks behind (31ms at 128 TPS), those ticks are dropped silently.
|
package/client/animation.js
CHANGED
|
@@ -123,12 +123,21 @@ function normalizeClips(gltf, vrmVersion, vrmHumanoid) {
|
|
|
123
123
|
return clips
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
let _animLibCache = null
|
|
127
|
+
let _animLibPromise = null
|
|
128
|
+
|
|
126
129
|
export async function loadAnimationLibrary(vrmVersion, vrmHumanoid) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
if (_animLibCache) return _animLibCache
|
|
131
|
+
if (_animLibPromise) return _animLibPromise
|
|
132
|
+
_animLibPromise = (async () => {
|
|
133
|
+
const loader = new GLTFLoader()
|
|
134
|
+
const gltf = await loader.loadAsync('/anim-lib.glb')
|
|
135
|
+
const normalizedClips = normalizeClips(gltf, vrmVersion || '1', vrmHumanoid)
|
|
136
|
+
console.log(`[anim] Loaded animation library (${normalizedClips.size} clips):`, [...normalizedClips.keys()])
|
|
137
|
+
_animLibCache = { normalizedClips }
|
|
138
|
+
return _animLibCache
|
|
139
|
+
})()
|
|
140
|
+
return _animLibPromise
|
|
132
141
|
}
|
|
133
142
|
|
|
134
143
|
export function createPlayerAnimator(vrm, allClips, vrmVersion, animConfig = {}) {
|
package/client/app.js
CHANGED
|
@@ -713,16 +713,16 @@ function detectVrmVersion(buffer) {
|
|
|
713
713
|
|
|
714
714
|
function initAssets(playerModelUrl) {
|
|
715
715
|
loadingMgr.setStage('DOWNLOAD')
|
|
716
|
-
|
|
716
|
+
const animLibPromise = loadAnimationLibrary('1', null)
|
|
717
|
+
assetsReady = loadingMgr.fetchWithProgress(playerModelUrl).then(async b => {
|
|
717
718
|
vrmBuffer = b
|
|
718
719
|
loadingMgr.setStage('PROCESS')
|
|
719
|
-
|
|
720
|
-
}).then(result => {
|
|
721
|
-
animAssets = result
|
|
720
|
+
animAssets = await animLibPromise
|
|
722
721
|
assetsLoaded = true
|
|
723
722
|
checkAllLoaded()
|
|
724
723
|
}).catch(err => {
|
|
725
724
|
console.warn('[assets] player model unavailable:', err.message)
|
|
725
|
+
animLibPromise.then(r => { animAssets = r }).catch(() => {})
|
|
726
726
|
assetsLoaded = true
|
|
727
727
|
checkAllLoaded()
|
|
728
728
|
})
|
|
@@ -840,7 +840,16 @@ function evaluateAppModule(code) {
|
|
|
840
840
|
try {
|
|
841
841
|
let stripped = code.replace(/^import\s+.*$/gm, '')
|
|
842
842
|
stripped = stripped.replace(/const\s+__dirname\s*=.*import\.meta\.url.*$/gm, 'const __dirname = "/"')
|
|
843
|
-
|
|
843
|
+
stripped = stripped.replace(/export\s+/g, '')
|
|
844
|
+
const exportDefaultIdx = stripped.search(/\bdefault\s*[\{(]/)
|
|
845
|
+
let wrapped
|
|
846
|
+
if (exportDefaultIdx !== -1) {
|
|
847
|
+
const before = stripped.slice(0, exportDefaultIdx)
|
|
848
|
+
const after = stripped.slice(exportDefaultIdx + 'default'.length).trimStart()
|
|
849
|
+
wrapped = before + '\nreturn ' + after + '\n//# sourceURL=app-module.js'
|
|
850
|
+
} else {
|
|
851
|
+
wrapped = stripped.replace(/\bdefault\s*/, 'return ') + '\n//# sourceURL=app-module.js'
|
|
852
|
+
}
|
|
844
853
|
const join = (...parts) => parts.filter(Boolean).join('/')
|
|
845
854
|
const readdirSync = () => []
|
|
846
855
|
const statSync = () => ({ isDirectory: () => false })
|
|
@@ -969,6 +978,7 @@ function loadEntityModel(entityId, entityState) {
|
|
|
969
978
|
fitShadowFrustum()
|
|
970
979
|
pendingLoads.delete(entityId)
|
|
971
980
|
if (!environmentLoaded) { environmentLoaded = true; checkAllLoaded() }
|
|
981
|
+
if (loadingScreenHidden) renderer.compileAsync(scene, camera).catch(() => renderer.compile(scene, camera))
|
|
972
982
|
}, (progress) => { if (progress.total > 0) console.log('[gltf]', url, Math.round(progress.loaded / progress.total * 100) + '%') }, (err) => { console.error('[gltf]', url, err); pendingLoads.delete(entityId) })
|
|
973
983
|
}
|
|
974
984
|
|
|
@@ -1117,7 +1127,8 @@ function checkAllLoaded() {
|
|
|
1117
1127
|
loadingMgr.setStage('INIT')
|
|
1118
1128
|
loadingMgr.complete()
|
|
1119
1129
|
loadingScreenHidden = true
|
|
1120
|
-
|
|
1130
|
+
loadingScreen.hide()
|
|
1131
|
+
warmupShaders().catch(() => {})
|
|
1121
1132
|
}
|
|
1122
1133
|
|
|
1123
1134
|
function initInputHandler() {
|
package/client/index.html
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"three": "https://esm.sh/three@0.171.0",
|
|
14
14
|
"three/addons/": "https://esm.sh/three@0.171.0/examples/jsm/",
|
|
15
15
|
"@pixiv/three-vrm": "https://esm.sh/@pixiv/three-vrm@3?external=three",
|
|
16
|
-
"three-mesh-bvh": "
|
|
16
|
+
"three-mesh-bvh": "/vendor/three-mesh-bvh.module.js",
|
|
17
17
|
"webjsx": "https://esm.sh/webjsx@0.0.73",
|
|
18
18
|
"webjsx/jsx-runtime": "https://esm.sh/webjsx@0.0.73/jsx-runtime"
|
|
19
19
|
}
|