spoint 0.1.0 → 0.1.11

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.
Files changed (204) hide show
  1. package/README.md +134 -209
  2. package/SKILL.md +95 -0
  3. package/apps/environment/index.js +200 -1
  4. package/apps/environment/models/decorative/.gitkeep +0 -0
  5. package/apps/environment/models/hazards/.gitkeep +0 -0
  6. package/apps/environment/models/interactive/.gitkeep +0 -0
  7. package/apps/environment/models/structures/.gitkeep +0 -0
  8. package/apps/environment/smartObjects.js +114 -0
  9. package/apps/interactable/index.js +155 -0
  10. package/apps/physics-crate/index.js +15 -9
  11. package/apps/power-crate/index.js +18 -12
  12. package/apps/tps-game/$GDUPI.vrm +0 -0
  13. package/apps/tps-game/Cleetus.vrm +0 -0
  14. package/apps/tps-game/index.js +185 -27
  15. package/apps/world/index.js +68 -22
  16. package/bin/create-app.js +337 -0
  17. package/client/ARControls.js +301 -0
  18. package/client/LoadingManager.js +117 -0
  19. package/client/MobileControls.js +1122 -0
  20. package/client/anim-lib.glb +0 -0
  21. package/client/animation.js +306 -0
  22. package/client/app.js +1341 -65
  23. package/client/camera.js +191 -33
  24. package/client/createLoadingScreen.js +69 -0
  25. package/client/editor/bridge.js +113 -0
  26. package/client/editor/css/main.css +794 -0
  27. package/client/editor/images/rotate.svg +4 -0
  28. package/client/editor/images/scale.svg +4 -0
  29. package/client/editor/images/translate.svg +4 -0
  30. package/client/editor/index.html +103 -0
  31. package/client/editor/js/Command.js +41 -0
  32. package/client/editor/js/Config.js +81 -0
  33. package/client/editor/js/Editor.js +785 -0
  34. package/client/editor/js/EditorControls.js +438 -0
  35. package/client/editor/js/History.js +321 -0
  36. package/client/editor/js/Loader.js +987 -0
  37. package/client/editor/js/LoaderUtils.js +90 -0
  38. package/client/editor/js/Menubar.Add.js +510 -0
  39. package/client/editor/js/Menubar.Edit.js +145 -0
  40. package/client/editor/js/Menubar.File.js +466 -0
  41. package/client/editor/js/Menubar.Help.js +73 -0
  42. package/client/editor/js/Menubar.Status.js +51 -0
  43. package/client/editor/js/Menubar.View.js +183 -0
  44. package/client/editor/js/Menubar.js +27 -0
  45. package/client/editor/js/Player.js +53 -0
  46. package/client/editor/js/Resizer.js +58 -0
  47. package/client/editor/js/Script.js +503 -0
  48. package/client/editor/js/Selector.js +102 -0
  49. package/client/editor/js/Sidebar.Geometry.BoxGeometry.js +121 -0
  50. package/client/editor/js/Sidebar.Geometry.BufferGeometry.js +115 -0
  51. package/client/editor/js/Sidebar.Geometry.CapsuleGeometry.js +97 -0
  52. package/client/editor/js/Sidebar.Geometry.CircleGeometry.js +97 -0
  53. package/client/editor/js/Sidebar.Geometry.CylinderGeometry.js +121 -0
  54. package/client/editor/js/Sidebar.Geometry.DodecahedronGeometry.js +73 -0
  55. package/client/editor/js/Sidebar.Geometry.ExtrudeGeometry.js +196 -0
  56. package/client/editor/js/Sidebar.Geometry.IcosahedronGeometry.js +73 -0
  57. package/client/editor/js/Sidebar.Geometry.LatheGeometry.js +98 -0
  58. package/client/editor/js/Sidebar.Geometry.Modifiers.js +73 -0
  59. package/client/editor/js/Sidebar.Geometry.OctahedronGeometry.js +74 -0
  60. package/client/editor/js/Sidebar.Geometry.PlaneGeometry.js +97 -0
  61. package/client/editor/js/Sidebar.Geometry.RingGeometry.js +121 -0
  62. package/client/editor/js/Sidebar.Geometry.ShapeGeometry.js +76 -0
  63. package/client/editor/js/Sidebar.Geometry.SphereGeometry.js +133 -0
  64. package/client/editor/js/Sidebar.Geometry.TetrahedronGeometry.js +74 -0
  65. package/client/editor/js/Sidebar.Geometry.TorusGeometry.js +109 -0
  66. package/client/editor/js/Sidebar.Geometry.TorusKnotGeometry.js +121 -0
  67. package/client/editor/js/Sidebar.Geometry.TubeGeometry.js +135 -0
  68. package/client/editor/js/Sidebar.Geometry.js +332 -0
  69. package/client/editor/js/Sidebar.Material.BooleanProperty.js +60 -0
  70. package/client/editor/js/Sidebar.Material.ColorProperty.js +87 -0
  71. package/client/editor/js/Sidebar.Material.ConstantProperty.js +62 -0
  72. package/client/editor/js/Sidebar.Material.MapProperty.js +249 -0
  73. package/client/editor/js/Sidebar.Material.NumberProperty.js +60 -0
  74. package/client/editor/js/Sidebar.Material.Program.js +73 -0
  75. package/client/editor/js/Sidebar.Material.RangeValueProperty.js +63 -0
  76. package/client/editor/js/Sidebar.Material.js +751 -0
  77. package/client/editor/js/Sidebar.Object.Animation.js +102 -0
  78. package/client/editor/js/Sidebar.Object.js +898 -0
  79. package/client/editor/js/Sidebar.Project.App.js +165 -0
  80. package/client/editor/js/Sidebar.Project.Image.js +225 -0
  81. package/client/editor/js/Sidebar.Project.Materials.js +82 -0
  82. package/client/editor/js/Sidebar.Project.Renderer.js +144 -0
  83. package/client/editor/js/Sidebar.Project.Video.js +242 -0
  84. package/client/editor/js/Sidebar.Project.js +31 -0
  85. package/client/editor/js/Sidebar.Properties.js +73 -0
  86. package/client/editor/js/Sidebar.Scene.js +585 -0
  87. package/client/editor/js/Sidebar.Script.js +129 -0
  88. package/client/editor/js/Sidebar.Settings.History.js +146 -0
  89. package/client/editor/js/Sidebar.Settings.Shortcuts.js +175 -0
  90. package/client/editor/js/Sidebar.Settings.js +60 -0
  91. package/client/editor/js/Sidebar.js +41 -0
  92. package/client/editor/js/Storage.js +98 -0
  93. package/client/editor/js/Strings.js +2028 -0
  94. package/client/editor/js/Toolbar.js +84 -0
  95. package/client/editor/js/Viewport.Controls.js +92 -0
  96. package/client/editor/js/Viewport.Info.js +136 -0
  97. package/client/editor/js/Viewport.Pathtracer.js +91 -0
  98. package/client/editor/js/Viewport.ViewHelper.js +39 -0
  99. package/client/editor/js/Viewport.XR.js +222 -0
  100. package/client/editor/js/Viewport.js +900 -0
  101. package/client/editor/js/commands/AddObjectCommand.js +68 -0
  102. package/client/editor/js/commands/AddScriptCommand.js +75 -0
  103. package/client/editor/js/commands/Commands.js +23 -0
  104. package/client/editor/js/commands/MoveObjectCommand.js +111 -0
  105. package/client/editor/js/commands/MultiCmdsCommand.js +85 -0
  106. package/client/editor/js/commands/RemoveObjectCommand.js +88 -0
  107. package/client/editor/js/commands/RemoveScriptCommand.js +81 -0
  108. package/client/editor/js/commands/SetColorCommand.js +73 -0
  109. package/client/editor/js/commands/SetGeometryCommand.js +87 -0
  110. package/client/editor/js/commands/SetGeometryValueCommand.js +70 -0
  111. package/client/editor/js/commands/SetMaterialColorCommand.js +86 -0
  112. package/client/editor/js/commands/SetMaterialCommand.js +79 -0
  113. package/client/editor/js/commands/SetMaterialMapCommand.js +143 -0
  114. package/client/editor/js/commands/SetMaterialRangeCommand.js +91 -0
  115. package/client/editor/js/commands/SetMaterialValueCommand.js +90 -0
  116. package/client/editor/js/commands/SetMaterialVectorCommand.js +79 -0
  117. package/client/editor/js/commands/SetPositionCommand.js +84 -0
  118. package/client/editor/js/commands/SetRotationCommand.js +84 -0
  119. package/client/editor/js/commands/SetScaleCommand.js +84 -0
  120. package/client/editor/js/commands/SetSceneCommand.js +103 -0
  121. package/client/editor/js/commands/SetScriptValueCommand.js +80 -0
  122. package/client/editor/js/commands/SetShadowValueCommand.js +73 -0
  123. package/client/editor/js/commands/SetUuidCommand.js +70 -0
  124. package/client/editor/js/commands/SetValueCommand.js +75 -0
  125. package/client/editor/js/libs/acorn/acorn.js +3236 -0
  126. package/client/editor/js/libs/acorn/acorn_loose.js +1299 -0
  127. package/client/editor/js/libs/acorn/walk.js +344 -0
  128. package/client/editor/js/libs/app/index.html +57 -0
  129. package/client/editor/js/libs/app.js +251 -0
  130. package/client/editor/js/libs/codemirror/addon/dialog.css +32 -0
  131. package/client/editor/js/libs/codemirror/addon/dialog.js +163 -0
  132. package/client/editor/js/libs/codemirror/addon/show-hint.css +36 -0
  133. package/client/editor/js/libs/codemirror/addon/show-hint.js +529 -0
  134. package/client/editor/js/libs/codemirror/addon/tern.css +87 -0
  135. package/client/editor/js/libs/codemirror/addon/tern.js +750 -0
  136. package/client/editor/js/libs/codemirror/codemirror.css +344 -0
  137. package/client/editor/js/libs/codemirror/codemirror.js +9849 -0
  138. package/client/editor/js/libs/codemirror/mode/glsl.js +233 -0
  139. package/client/editor/js/libs/codemirror/mode/javascript.js +959 -0
  140. package/client/editor/js/libs/codemirror/theme/monokai.css +41 -0
  141. package/client/editor/js/libs/esprima.js +6401 -0
  142. package/client/editor/js/libs/jsonlint.js +453 -0
  143. package/client/editor/js/libs/signals.min.js +14 -0
  144. package/client/editor/js/libs/tern-threejs/threejs.js +5031 -0
  145. package/client/editor/js/libs/ternjs/comment.js +87 -0
  146. package/client/editor/js/libs/ternjs/def.js +588 -0
  147. package/client/editor/js/libs/ternjs/doc_comment.js +401 -0
  148. package/client/editor/js/libs/ternjs/infer.js +1635 -0
  149. package/client/editor/js/libs/ternjs/polyfill.js +80 -0
  150. package/client/editor/js/libs/ternjs/signal.js +26 -0
  151. package/client/editor/js/libs/ternjs/tern.js +993 -0
  152. package/client/editor/js/libs/ui.js +1346 -0
  153. package/client/editor/js/libs/ui.three.js +855 -0
  154. package/client/facial-animation.js +455 -0
  155. package/client/index.html +7 -4
  156. package/client/loading.css +147 -0
  157. package/client/loading.html +25 -0
  158. package/client/style.css +251 -0
  159. package/package.json +7 -3
  160. package/server.js +9 -1
  161. package/src/apps/AppContext.js +1 -1
  162. package/src/apps/AppLoader.js +50 -37
  163. package/src/apps/AppRuntime.js +32 -8
  164. package/src/client/InputHandler.js +233 -0
  165. package/src/client/JitterBuffer.js +207 -0
  166. package/src/client/KalmanFilter.js +125 -0
  167. package/src/client/MessageHandler.js +101 -0
  168. package/src/client/PhysicsNetworkClient.js +141 -68
  169. package/src/client/ReconnectManager.js +62 -0
  170. package/src/client/SmoothInterpolation.js +127 -0
  171. package/src/client/SnapshotProcessor.js +144 -0
  172. package/src/connection/ConnectionManager.js +21 -3
  173. package/src/connection/SessionStore.js +13 -3
  174. package/src/index.client.js +4 -6
  175. package/src/netcode/EventLog.js +29 -15
  176. package/src/netcode/LagCompensator.js +25 -26
  177. package/src/netcode/NetworkState.js +4 -1
  178. package/src/netcode/PhysicsIntegration.js +20 -6
  179. package/src/netcode/PlayerManager.js +10 -2
  180. package/src/netcode/SnapshotEncoder.js +66 -19
  181. package/src/netcode/TickSystem.js +13 -4
  182. package/src/physics/World.js +66 -13
  183. package/src/protocol/msgpack.js +90 -63
  184. package/src/sdk/ReloadHandlers.js +12 -2
  185. package/src/sdk/ReloadManager.js +5 -0
  186. package/src/sdk/ServerHandlers.js +50 -11
  187. package/src/sdk/StaticHandler.js +22 -6
  188. package/src/sdk/TickHandler.js +101 -34
  189. package/src/sdk/scaffold.js +31 -0
  190. package/src/sdk/server.js +59 -33
  191. package/src/shared/movement.js +2 -1
  192. package/src/spatial/Octree.js +5 -0
  193. package/apps/interactive-door/index.js +0 -33
  194. package/apps/patrol-npc/index.js +0 -37
  195. package/src/connection/QualityMonitor.js +0 -46
  196. package/src/debug/StateInspector.js +0 -42
  197. package/src/index.js +0 -1
  198. package/src/index.server.js +0 -27
  199. package/src/protocol/Codec.js +0 -60
  200. package/src/protocol/SequenceTracker.js +0 -71
  201. package/src/sdk/ClientMessageHandler.js +0 -80
  202. package/src/sdk/client.js +0 -122
  203. package/world/kaira.glb +0 -0
  204. package/world/schwust.glb +0 -0
package/client/camera.js CHANGED
@@ -7,14 +7,61 @@ const camDesired = new THREE.Vector3()
7
7
  const camLookTarget = new THREE.Vector3()
8
8
  const aimRaycaster = new THREE.Raycaster()
9
9
  const aimDir = new THREE.Vector3()
10
- const shoulderOffset = 0.35
11
- const headHeight = 0.4
12
- const camFollowSpeed = 12.0
13
- const camSnapSpeed = 30.0
14
- const zoomStages = [0, 1.5, 3, 5, 8]
10
+ let shoulderOffset = 0.35
11
+ let headHeight = 0.4
12
+ let camFollowSpeed = 12.0
13
+ let camSnapSpeed = 30.0
14
+ let zoomStages = [0, 1.5, 3, 5, 8]
15
+ let mouseSensitivity = 0.002
16
+ let pitchMin = -1.4, pitchMax = 1.4
17
+
18
+ function isDescendant(obj, ancestor) {
19
+ let cur = obj
20
+ while (cur) {
21
+ if (cur === ancestor) return true
22
+ cur = cur.parent
23
+ }
24
+ return false
25
+ }
26
+
27
+ const _boneWorldPos = new THREE.Vector3()
28
+ const _boneForward = new THREE.Vector3()
15
29
 
16
30
  export function createCameraController(camera, scene) {
17
31
  let yaw = 0, pitch = 0, zoomIndex = 2, camInitialized = false
32
+ let mode = 'tps'
33
+ let editMode = false
34
+ let editCamPos = new THREE.Vector3(0, 5, 10)
35
+ let editCamSpeed = 8
36
+ const envMeshes = []
37
+ let rayTimer = 0, cachedClipDist = 10, cachedAimPoint = null
38
+ let cameraBone = null
39
+ let headBone = null
40
+ let fpsForwardOffset = 0.7
41
+ let fpsHeadDownOffset = 0.2
42
+ camRaycaster.firstHitOnly = true
43
+ aimRaycaster.firstHitOnly = true
44
+
45
+ function setEnvironment(meshes) { envMeshes.length = 0; envMeshes.push(...meshes) }
46
+ function setCameraBone(bone) { cameraBone = bone }
47
+ function setHeadBone(bone) { headBone = bone }
48
+
49
+ function setMode(m) {
50
+ const prev = mode
51
+ mode = m
52
+ if (m === 'fps' && headBone) {
53
+ headBone.scale.set(0, 0, 0)
54
+ headBone.position.y -= fpsHeadDownOffset
55
+ }
56
+ if (prev === 'fps' && m !== 'fps' && headBone) {
57
+ headBone.scale.set(1, 1, 1)
58
+ headBone.position.y += fpsHeadDownOffset
59
+ }
60
+ }
61
+ function getMode() { return mode }
62
+
63
+ function setPosition(x, y, z) { camera.position.set(x, y, z) }
64
+ function setTarget(x, y, z) { camera.lookAt(x, y, z) }
18
65
 
19
66
  function restore(saved) {
20
67
  if (saved) { yaw = saved.yaw || 0; pitch = saved.pitch || 0; zoomIndex = saved.zoomIndex ?? 2 }
@@ -23,9 +70,9 @@ export function createCameraController(camera, scene) {
23
70
  function save() { return { yaw, pitch, zoomIndex } }
24
71
 
25
72
  function onMouseMove(e) {
26
- yaw -= e.movementX * 0.002
27
- pitch -= e.movementY * 0.002
28
- pitch = Math.max(-1.4, Math.min(1.4, pitch))
73
+ yaw -= e.movementX * mouseSensitivity
74
+ pitch -= e.movementY * mouseSensitivity
75
+ pitch = Math.max(pitchMin, Math.min(pitchMax, pitch))
29
76
  }
30
77
 
31
78
  function onWheel(e) {
@@ -51,19 +98,85 @@ export function createCameraController(camera, scene) {
51
98
  return len > 0.001 ? [dx / len, dy / len, dz / len] : [fwdX, fwdY, fwdZ]
52
99
  }
53
100
 
54
- function update(localPlayer, localMesh, frameDt) {
55
- if (!localPlayer) return
56
- const dist = zoomStages[zoomIndex]
101
+ function update(localPlayer, localMesh, frameDt, inputState) {
102
+ if (mode === 'custom' || mode === 'fixed') return
103
+ if (!localPlayer && !editMode) return
104
+
105
+ if (editMode && inputState) {
106
+ const sy = Math.sin(yaw), cy = Math.cos(yaw)
107
+ const fwdX = sy, fwdZ = cy
108
+ const rightX = -cy, rightZ = sy
109
+ const moveForward = (inputState.forward ? 1 : 0) - (inputState.backward ? 1 : 0)
110
+ const moveRight = (inputState.right ? 1 : 0) - (inputState.left ? 1 : 0)
111
+ const moveUp = (inputState.jump ? 1 : 0) - (inputState.crouch ? 1 : 0)
112
+ const speed = editCamSpeed * frameDt
113
+ editCamPos.x += (moveForward * fwdX + moveRight * rightX) * speed
114
+ editCamPos.y += moveUp * speed
115
+ editCamPos.z += (moveForward * fwdZ + moveRight * rightZ) * speed
116
+ camera.position.copy(editCamPos)
117
+ const sp = Math.sin(pitch), cp = Math.cos(pitch)
118
+ camera.lookAt(
119
+ camera.position.x + fwdX * 100,
120
+ camera.position.y + sp * 100,
121
+ camera.position.z + fwdZ * 100
122
+ )
123
+ return
124
+ }
125
+
126
+ const dist = mode === 'fps' ? 0 : zoomStages[zoomIndex]
57
127
  camTarget.set(localPlayer.position[0], localPlayer.position[1] + headHeight, localPlayer.position[2])
58
- if (localMesh) localMesh.visible = dist > 0.5
128
+ const punchLerp = 1 - Math.exp(-972 * frameDt)
129
+ punchYaw += (punchYawTarget - punchYaw) * punchLerp
130
+ punchPitch += (punchPitchTarget - punchPitch) * punchLerp
131
+ punchYawTarget *= 1 - Math.min(1, 18 * frameDt)
132
+ punchPitchTarget *= 1 - Math.min(1, 18 * frameDt)
133
+ yaw += punchYaw * frameDt
134
+ pitch = Math.max(pitchMin, Math.min(pitchMax, pitch + punchPitch * frameDt))
59
135
  const sy = Math.sin(yaw), cy = Math.cos(yaw)
60
136
  const sp = Math.sin(pitch), cp = Math.cos(pitch)
61
137
  const fwdX = sy * cp, fwdY = sp, fwdZ = cy * cp
62
138
  const rightX = -cy, rightZ = sy
63
139
  if (dist < 0.01) {
64
- camera.position.copy(camTarget)
65
- camera.lookAt(camTarget.x + fwdX, camTarget.y + fwdY, camTarget.z + fwdZ)
140
+ if (cameraBone && localMesh) {
141
+ localMesh.updateMatrixWorld(true)
142
+ cameraBone.getWorldPosition(_boneWorldPos)
143
+ _boneForward.set(fwdX, 0, fwdZ).normalize()
144
+ camera.position.copy(_boneWorldPos).addScaledVector(_boneForward, fpsForwardOffset)
145
+ camera.position.y += 0.35
146
+ } else {
147
+ camera.position.copy(camTarget)
148
+ }
149
+ if (headBone) headBone.scale.set(0, 0, 0)
150
+ const rayTargets = envMeshes.length ? envMeshes : scene.children
151
+ const wallDist = 0.35
152
+ const fpsRayOrigin = new THREE.Vector3().copy(camera.position)
153
+ const rayDirs = [
154
+ [fwdX, fwdY, fwdZ],
155
+ [rightX, 0, rightZ],
156
+ [-rightX, 0, -rightZ],
157
+ [0, 1, 0],
158
+ [0, -1, 0]
159
+ ]
160
+ for (const d of rayDirs) {
161
+ camDir.set(-d[0], -d[1], -d[2])
162
+ camRaycaster.set(fpsRayOrigin, camDir)
163
+ camRaycaster.far = wallDist
164
+ camRaycaster.near = 0
165
+ const hits = camRaycaster.intersectObjects(rayTargets, true)
166
+ for (const hit of hits) {
167
+ if (localMesh && isDescendant(hit.object, localMesh)) continue
168
+ const push = wallDist - hit.distance
169
+ if (push > 0) {
170
+ camera.position.x += d[0] * push
171
+ camera.position.y += d[1] * push
172
+ camera.position.z += d[2] * push
173
+ }
174
+ break
175
+ }
176
+ }
177
+ camera.lookAt(camera.position.x + fwdX, camera.position.y + fwdY, camera.position.z + fwdZ)
66
178
  } else {
179
+ if (headBone) headBone.scale.set(1, 1, 1)
67
180
  camDesired.set(
68
181
  camTarget.x - fwdX * dist + rightX * shoulderOffset,
69
182
  camTarget.y - fwdY * dist + 0.2,
@@ -71,16 +184,22 @@ export function createCameraController(camera, scene) {
71
184
  )
72
185
  camDir.subVectors(camDesired, camTarget).normalize()
73
186
  const fullDist = camTarget.distanceTo(camDesired)
74
- camRaycaster.set(camTarget, camDir)
75
- camRaycaster.far = fullDist
76
- camRaycaster.near = 0
77
- const hits = camRaycaster.intersectObjects(scene.children, true)
78
- let clippedDist = fullDist
79
- for (const hit of hits) {
80
- if (hit.object === localMesh || localMesh?.children?.includes(hit.object)) continue
81
- if (hit.distance < clippedDist) clippedDist = hit.distance - 0.2
187
+ rayTimer += frameDt
188
+ const doRaycast = rayTimer >= 0.05
189
+ if (doRaycast) {
190
+ rayTimer = 0
191
+ camRaycaster.set(camTarget, camDir)
192
+ camRaycaster.far = fullDist
193
+ camRaycaster.near = 0
194
+ const hits = camRaycaster.intersectObjects(envMeshes.length ? envMeshes : scene.children, true)
195
+ cachedClipDist = fullDist
196
+ for (const hit of hits) {
197
+ if (localMesh && isDescendant(hit.object, localMesh)) continue
198
+ if (hit.distance < cachedClipDist) cachedClipDist = hit.distance - 0.2
199
+ }
200
+ if (cachedClipDist < 0.3) cachedClipDist = 0.3
82
201
  }
83
- if (clippedDist < 0.3) clippedDist = 0.3
202
+ const clippedDist = Math.min(cachedClipDist, fullDist)
84
203
  camDesired.set(
85
204
  camTarget.x + camDir.x * clippedDist,
86
205
  camTarget.y + camDir.y * clippedDist,
@@ -93,16 +212,19 @@ export function createCameraController(camera, scene) {
93
212
  camera.position.lerp(camDesired, 1.0 - Math.exp(-speed * frameDt))
94
213
  }
95
214
  aimDir.set(fwdX, fwdY, fwdZ)
96
- aimRaycaster.set(camera.position, aimDir)
97
- aimRaycaster.far = 500
98
- aimRaycaster.near = 0.5
99
- const aimHits = aimRaycaster.intersectObjects(scene.children, true)
100
- let aimPoint = null
101
- for (const ah of aimHits) {
102
- if (ah.object === localMesh || localMesh?.children?.includes(ah.object)) continue
103
- aimPoint = ah.point; break
215
+ if (doRaycast) {
216
+ aimRaycaster.set(camera.position, aimDir)
217
+ aimRaycaster.far = 500
218
+ aimRaycaster.near = 0.5
219
+ const aimHits = aimRaycaster.intersectObjects(envMeshes.length ? envMeshes : scene.children, true)
220
+ cachedAimPoint = null
221
+ for (const ah of aimHits) {
222
+ if (localMesh && isDescendant(ah.object, localMesh)) continue
223
+ cachedAimPoint = ah.point; break
224
+ }
104
225
  }
105
- if (aimPoint) {
226
+ if (cachedAimPoint) {
227
+ const aimPoint = cachedAimPoint
106
228
  if (!camLookTarget.lengthSq()) camLookTarget.copy(aimPoint)
107
229
  camLookTarget.lerp(aimPoint, 1.0 - Math.exp(-camFollowSpeed * frameDt))
108
230
  } else {
@@ -112,5 +234,41 @@ export function createCameraController(camera, scene) {
112
234
  }
113
235
  }
114
236
 
115
- return { restore, save, onMouseMove, onWheel, getAimDirection, update, get yaw() { return yaw }, get pitch() { return pitch } }
237
+ function applyConfig(cfg) {
238
+ if (cfg.mode != null) mode = cfg.mode
239
+ if (cfg.shoulderOffset != null) shoulderOffset = cfg.shoulderOffset
240
+ if (cfg.headHeight != null) headHeight = cfg.headHeight
241
+ if (cfg.zoomStages) zoomStages = cfg.zoomStages
242
+ if (cfg.defaultZoomIndex != null) zoomIndex = cfg.defaultZoomIndex
243
+ if (cfg.followSpeed != null) camFollowSpeed = cfg.followSpeed
244
+ if (cfg.snapSpeed != null) camSnapSpeed = cfg.snapSpeed
245
+ if (cfg.mouseSensitivity != null) mouseSensitivity = cfg.mouseSensitivity
246
+ if (cfg.pitchRange) { pitchMin = cfg.pitchRange[0]; pitchMax = cfg.pitchRange[1] }
247
+ if (cfg.fov) { camera.fov = cfg.fov; camera.updateProjectionMatrix() }
248
+ }
249
+
250
+ let punchYawTarget = 0, punchPitchTarget = 0, punchYaw = 0, punchPitch = 0
251
+ function punch(intensity) {
252
+ punchYawTarget += (Math.random() - 0.5) * intensity * 0.9
253
+ punchPitchTarget += (Math.random() - 0.3) * intensity * 0.9
254
+ }
255
+
256
+ function setVRYaw(vrYaw) { yaw = vrYaw }
257
+ function getVRYaw() { return yaw }
258
+ function setVRPitch(vrPitch) { pitch = vrPitch }
259
+ function getVRPitch() { return pitch }
260
+ function adjustVRPitch(delta) {
261
+ pitch = Math.max(pitchMin, Math.min(pitchMax, pitch + delta))
262
+ }
263
+
264
+ function setEditMode(enabled) {
265
+ if (enabled && !editMode) {
266
+ editCamPos.copy(camera.position)
267
+ }
268
+ editMode = enabled
269
+ }
270
+ function getEditMode() { return editMode }
271
+ function getEditCameraPosition() { return editCamPos }
272
+
273
+ return { restore, save, onMouseMove, onWheel, getAimDirection, update, setEnvironment, setCameraBone, setHeadBone, applyConfig, setMode, getMode, setPosition, setTarget, punch, setVRYaw, getVRYaw, setVRPitch, getVRPitch, adjustVRPitch, setEditMode, getEditMode, getEditCameraPosition, get yaw() { return yaw }, get pitch() { return pitch }, get mode() { return mode } }
116
274
  }
@@ -0,0 +1,69 @@
1
+ export function createLoadingScreen(loadingManager) {
2
+ const overlay = document.createElement('div')
3
+ overlay.id = 'loading-overlay'
4
+ overlay.className = 'loading-overlay'
5
+ overlay.innerHTML = `
6
+ <div class="loading-container">
7
+ <div class="loading-header">
8
+ <h1>Spawnpoint</h1>
9
+ <p class="loading-stage-text">Connecting...</p>
10
+ </div>
11
+ <div class="loading-progress-wrapper">
12
+ <div class="loading-progress-bar">
13
+ <div class="loading-progress-fill"></div>
14
+ </div>
15
+ <div class="loading-percent">0%</div>
16
+ </div>
17
+ <div class="loading-details">
18
+ <span class="loading-current">0</span> / <span class="loading-total">0</span> bytes
19
+ </div>
20
+ <div class="loading-spinner">
21
+ <div class="spinner-dot"></div>
22
+ <div class="spinner-dot"></div>
23
+ <div class="spinner-dot"></div>
24
+ </div>
25
+ </div>
26
+ `
27
+ document.body.insertBefore(overlay, document.body.firstChild)
28
+
29
+ const progressFill = overlay.querySelector('.loading-progress-fill')
30
+ const percentText = overlay.querySelector('.loading-percent')
31
+ const stageText = overlay.querySelector('.loading-stage-text')
32
+ const currentBytes = overlay.querySelector('.loading-current')
33
+ const totalBytes = overlay.querySelector('.loading-total')
34
+
35
+ const updateUI = (detail) => {
36
+ const percent = detail.percent || 0
37
+ progressFill.style.width = percent + '%'
38
+ percentText.textContent = Math.round(percent) + '%'
39
+ stageText.textContent = detail.label || ''
40
+
41
+ if (detail.current !== undefined && detail.total !== undefined) {
42
+ currentBytes.textContent = formatBytes(detail.current)
43
+ totalBytes.textContent = formatBytes(detail.total)
44
+ }
45
+ }
46
+
47
+ const formatBytes = (bytes) => {
48
+ if (bytes === 0) return '0'
49
+ const k = 1024
50
+ const sizes = ['B', 'KB', 'MB']
51
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
52
+ return Math.round((bytes / Math.pow(k, i)) * 10) / 10 + sizes[i]
53
+ }
54
+
55
+ loadingManager.addEventListener('progress', (e) => updateUI(e.detail))
56
+ loadingManager.addEventListener('stagechange', (e) => updateUI(e.detail))
57
+
58
+ return {
59
+ element: overlay,
60
+ hide: async () => {
61
+ overlay.classList.add('fade-out')
62
+ await new Promise(resolve => setTimeout(resolve, 500))
63
+ overlay.remove()
64
+ },
65
+ dispose: () => {
66
+ overlay.remove()
67
+ }
68
+ }
69
+ }
@@ -0,0 +1,113 @@
1
+ import { PhysicsNetworkClient } from '/src/index.client.js'
2
+ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
3
+
4
+ export function connectEditorToGame(editor, THREE) {
5
+ const gltfLoader = new GLTFLoader()
6
+ const playerMeshes = new Map()
7
+ const entityMeshes = new Map()
8
+
9
+ const liveGroup = new THREE.Group()
10
+ liveGroup.name = 'Game Scene (Live)'
11
+ editor.addObject(liveGroup)
12
+
13
+ const playersGroup = new THREE.Group()
14
+ playersGroup.name = 'Players'
15
+ liveGroup.add(playersGroup)
16
+
17
+ const entitiesGroup = new THREE.Group()
18
+ entitiesGroup.name = 'Entities'
19
+ liveGroup.add(entitiesGroup)
20
+
21
+ editor.signals.sceneGraphChanged.dispatch()
22
+
23
+ const client = new PhysicsNetworkClient({
24
+ url: `${location.protocol === 'https:' ? 'wss:' : 'ws:'}//${location.host}/ws`,
25
+ predictionEnabled: false,
26
+ onStateUpdate(state) {
27
+ for (const p of state.players) {
28
+ let mesh = playerMeshes.get(p.id)
29
+ if (!mesh) {
30
+ mesh = new THREE.Group()
31
+ mesh.name = `Player_${p.id}`
32
+ const capsule = new THREE.Mesh(
33
+ new THREE.CapsuleGeometry(0.3, 1.0, 8, 16),
34
+ new THREE.MeshStandardMaterial({ color: 0x4488ff })
35
+ )
36
+ capsule.name = 'Capsule'
37
+ capsule.position.y = 0.8
38
+ mesh.add(capsule)
39
+ playersGroup.add(mesh)
40
+ playerMeshes.set(p.id, mesh)
41
+ editor.signals.sceneGraphChanged.dispatch()
42
+ }
43
+ mesh.position.set(p.position[0], p.position[1] - 1.3, p.position[2])
44
+ if (p.rotation) mesh.quaternion.set(p.rotation[0], p.rotation[1], p.rotation[2], p.rotation[3])
45
+ }
46
+ const activeIds = new Set(state.players.map(p => p.id))
47
+ for (const [id, mesh] of playerMeshes) {
48
+ if (!activeIds.has(id)) {
49
+ playersGroup.remove(mesh)
50
+ playerMeshes.delete(id)
51
+ editor.signals.sceneGraphChanged.dispatch()
52
+ }
53
+ }
54
+ editor.signals.objectChanged.dispatch(liveGroup)
55
+ },
56
+ onEntityAdded(id, entityState) {
57
+ if (entityState.model) {
58
+ const url = entityState.model.startsWith('./') ? '/' + entityState.model.slice(2) : entityState.model
59
+ gltfLoader.load(url, (gltf) => {
60
+ const model = gltf.scene
61
+ model.name = `Entity_${id}`
62
+ model.position.set(...entityState.position)
63
+ if (entityState.rotation) model.quaternion.set(...entityState.rotation)
64
+ entitiesGroup.add(model)
65
+ entityMeshes.set(id, model)
66
+ editor.signals.sceneGraphChanged.dispatch()
67
+ })
68
+ } else {
69
+ const c = entityState.custom || {}
70
+ const geo = c.mesh === 'sphere' ? new THREE.SphereGeometry(c.r || 0.5) :
71
+ c.mesh === 'cylinder' ? new THREE.CylinderGeometry(c.r || 0.4, c.r || 0.4, c.h || 0.1) :
72
+ new THREE.BoxGeometry(c.sx || 1, c.sy || 1, c.sz || 1)
73
+ const mat = new THREE.MeshStandardMaterial({ color: c.color ?? 0xff8800 })
74
+ const mesh = new THREE.Mesh(geo, mat)
75
+ mesh.name = `Entity_${id}`
76
+ mesh.position.set(...entityState.position)
77
+ if (entityState.rotation) mesh.quaternion.set(...entityState.rotation)
78
+ entitiesGroup.add(mesh)
79
+ entityMeshes.set(id, mesh)
80
+ editor.signals.sceneGraphChanged.dispatch()
81
+ }
82
+ },
83
+ onEntityRemoved(id) {
84
+ const mesh = entityMeshes.get(id)
85
+ if (mesh) {
86
+ entitiesGroup.remove(mesh)
87
+ entityMeshes.delete(id)
88
+ editor.signals.sceneGraphChanged.dispatch()
89
+ }
90
+ },
91
+ onWorldDef(wd) {
92
+ if (wd.scene) {
93
+ if (wd.scene.skyColor != null) editor.scene.background = new THREE.Color(wd.scene.skyColor)
94
+ if (wd.scene.fogColor != null) editor.scene.fog = new THREE.Fog(wd.scene.fogColor, wd.scene.fogNear ?? 80, wd.scene.fogFar ?? 200)
95
+ editor.signals.sceneBackgroundChanged.dispatch()
96
+ }
97
+ },
98
+ onPlayerLeft(id) {
99
+ const mesh = playerMeshes.get(id)
100
+ if (mesh) {
101
+ playersGroup.remove(mesh)
102
+ playerMeshes.delete(id)
103
+ editor.signals.sceneGraphChanged.dispatch()
104
+ }
105
+ },
106
+ onHotReload() {}
107
+ })
108
+
109
+ client.connect().then(() => console.log('[editor-bridge] Connected to game server'))
110
+
111
+ window.gameClient = client
112
+ window.liveGroup = liveGroup
113
+ }