minecraft-renderer 0.1.0

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 (187) hide show
  1. package/README.md +297 -0
  2. package/dist/index.html +83 -0
  3. package/dist/static/image/arrow.6f27b59f.png +0 -0
  4. package/dist/static/image/blocksAtlasLatest.7850afa3.png +0 -0
  5. package/dist/static/image/blocksAtlasLegacy.5c76823d.png +0 -0
  6. package/dist/static/image/itemsAtlasLatest.36036f95.png +0 -0
  7. package/dist/static/image/itemsAtlasLegacy.dcb1b58d.png +0 -0
  8. package/dist/static/image/tipped_arrow.6f27b59f.png +0 -0
  9. package/dist/static/js/365.f05233ab.js +8462 -0
  10. package/dist/static/js/365.f05233ab.js.LICENSE.txt +52 -0
  11. package/dist/static/js/async/738.efa27644.js +1 -0
  12. package/dist/static/js/index.092ec5be.js +56 -0
  13. package/dist/static/js/lib-polyfill.98986ac5.js +1 -0
  14. package/dist/static/js/lib-react.5c9129e0.js +2 -0
  15. package/dist/static/js/lib-react.5c9129e0.js.LICENSE.txt +39 -0
  16. package/package.json +104 -0
  17. package/src/assets/destroy_stage_0.png +0 -0
  18. package/src/assets/destroy_stage_1.png +0 -0
  19. package/src/assets/destroy_stage_2.png +0 -0
  20. package/src/assets/destroy_stage_3.png +0 -0
  21. package/src/assets/destroy_stage_4.png +0 -0
  22. package/src/assets/destroy_stage_5.png +0 -0
  23. package/src/assets/destroy_stage_6.png +0 -0
  24. package/src/assets/destroy_stage_7.png +0 -0
  25. package/src/assets/destroy_stage_8.png +0 -0
  26. package/src/assets/destroy_stage_9.png +0 -0
  27. package/src/examples/README.md +146 -0
  28. package/src/examples/appViewerExample.ts +205 -0
  29. package/src/examples/initialMenuStart.ts +161 -0
  30. package/src/graphicsBackend/appViewer.ts +297 -0
  31. package/src/graphicsBackend/config.ts +119 -0
  32. package/src/graphicsBackend/index.ts +10 -0
  33. package/src/graphicsBackend/playerState.ts +61 -0
  34. package/src/graphicsBackend/types.ts +143 -0
  35. package/src/index.ts +97 -0
  36. package/src/lib/DebugGui.ts +190 -0
  37. package/src/lib/animationController.ts +85 -0
  38. package/src/lib/buildSharedConfig.mjs +1 -0
  39. package/src/lib/cameraBobbing.ts +94 -0
  40. package/src/lib/canvas2DOverlay.example.ts +361 -0
  41. package/src/lib/canvas2DOverlay.quickstart.ts +242 -0
  42. package/src/lib/canvas2DOverlay.ts +381 -0
  43. package/src/lib/cleanupDecorator.ts +29 -0
  44. package/src/lib/createPlayerObject.ts +55 -0
  45. package/src/lib/frameTimingCollector.ts +164 -0
  46. package/src/lib/guiRenderer.ts +283 -0
  47. package/src/lib/items.ts +140 -0
  48. package/src/lib/mesherlogReader.ts +131 -0
  49. package/src/lib/moreBlockDataGenerated.json +714 -0
  50. package/src/lib/preflatMap.json +1741 -0
  51. package/src/lib/simpleUtils.ts +40 -0
  52. package/src/lib/smoothSwitcher.ts +168 -0
  53. package/src/lib/spiral.ts +29 -0
  54. package/src/lib/ui/newStats.ts +120 -0
  55. package/src/lib/utils/proxy.ts +23 -0
  56. package/src/lib/utils/skins.ts +63 -0
  57. package/src/lib/utils.ts +76 -0
  58. package/src/lib/workerProxy.ts +342 -0
  59. package/src/lib/worldrendererCommon.ts +1088 -0
  60. package/src/mesher/mesher.ts +253 -0
  61. package/src/mesher/models.ts +769 -0
  62. package/src/mesher/modelsGeometryCommon.ts +142 -0
  63. package/src/mesher/shared.ts +80 -0
  64. package/src/mesher/standaloneRenderer.ts +270 -0
  65. package/src/mesher/test/a.ts +3 -0
  66. package/src/mesher/test/mesherTester.ts +76 -0
  67. package/src/mesher/test/playground.ts +19 -0
  68. package/src/mesher/test/test-perf.ts +74 -0
  69. package/src/mesher/test/tests.test.ts +56 -0
  70. package/src/mesher/world.ts +294 -0
  71. package/src/mesher/worldConstants.ts +1 -0
  72. package/src/modules/index.ts +11 -0
  73. package/src/modules/starfield.ts +313 -0
  74. package/src/modules/types.ts +110 -0
  75. package/src/playerState/playerState.ts +78 -0
  76. package/src/playerState/types.ts +36 -0
  77. package/src/playground/allEntitiesDebug.ts +170 -0
  78. package/src/playground/baseScene.ts +587 -0
  79. package/src/playground/mobileControls.tsx +268 -0
  80. package/src/playground/playground.html +83 -0
  81. package/src/playground/playground.ts +11 -0
  82. package/src/playground/playgroundUi.tsx +140 -0
  83. package/src/playground/reactUtils.ts +71 -0
  84. package/src/playground/scenes/allEntities.ts +13 -0
  85. package/src/playground/scenes/entities.ts +37 -0
  86. package/src/playground/scenes/floorRandom.ts +33 -0
  87. package/src/playground/scenes/frequentUpdates.ts +148 -0
  88. package/src/playground/scenes/geometryExport.ts +142 -0
  89. package/src/playground/scenes/index.ts +12 -0
  90. package/src/playground/scenes/lightingStarfield.ts +40 -0
  91. package/src/playground/scenes/main.ts +313 -0
  92. package/src/playground/scenes/railsCobweb.ts +14 -0
  93. package/src/playground/scenes/rotationIssue.ts +7 -0
  94. package/src/playground/scenes/slabsOptimization.ts +16 -0
  95. package/src/playground/scenes/transparencyIssue.ts +11 -0
  96. package/src/playground/shared.ts +79 -0
  97. package/src/resourcesManager/index.ts +5 -0
  98. package/src/resourcesManager/resourcesManager.ts +314 -0
  99. package/src/shims/minecraftData.ts +41 -0
  100. package/src/sign-renderer/index.html +21 -0
  101. package/src/sign-renderer/index.ts +216 -0
  102. package/src/sign-renderer/noop.js +1 -0
  103. package/src/sign-renderer/playground.ts +38 -0
  104. package/src/sign-renderer/tests.test.ts +69 -0
  105. package/src/sign-renderer/vite.config.ts +10 -0
  106. package/src/three/appShared.ts +75 -0
  107. package/src/three/bannerRenderer.ts +275 -0
  108. package/src/three/cameraShake.ts +120 -0
  109. package/src/three/cinimaticScript.ts +350 -0
  110. package/src/three/documentRenderer.ts +491 -0
  111. package/src/three/entities.ts +1580 -0
  112. package/src/three/entity/EntityMesh.ts +707 -0
  113. package/src/three/entity/animations.js +171 -0
  114. package/src/three/entity/armorModels.json +204 -0
  115. package/src/three/entity/armorModels.ts +36 -0
  116. package/src/three/entity/entities.json +6230 -0
  117. package/src/three/entity/exportedModels.js +38 -0
  118. package/src/three/entity/externalTextures.json +1 -0
  119. package/src/three/entity/models/allay.obj +325 -0
  120. package/src/three/entity/models/arrow.obj +60 -0
  121. package/src/three/entity/models/axolotl.obj +509 -0
  122. package/src/three/entity/models/blaze.obj +601 -0
  123. package/src/three/entity/models/boat.obj +417 -0
  124. package/src/three/entity/models/camel.obj +1061 -0
  125. package/src/three/entity/models/cat.obj +509 -0
  126. package/src/three/entity/models/chicken.obj +371 -0
  127. package/src/three/entity/models/cod.obj +371 -0
  128. package/src/three/entity/models/creeper.obj +279 -0
  129. package/src/three/entity/models/dolphin.obj +371 -0
  130. package/src/three/entity/models/ender_dragon.obj +2993 -0
  131. package/src/three/entity/models/enderman.obj +325 -0
  132. package/src/three/entity/models/endermite.obj +187 -0
  133. package/src/three/entity/models/fox.obj +463 -0
  134. package/src/three/entity/models/frog.obj +739 -0
  135. package/src/three/entity/models/ghast.obj +463 -0
  136. package/src/three/entity/models/goat.obj +601 -0
  137. package/src/three/entity/models/guardian.obj +1015 -0
  138. package/src/three/entity/models/horse.obj +1061 -0
  139. package/src/three/entity/models/llama.obj +509 -0
  140. package/src/three/entity/models/minecart.obj +233 -0
  141. package/src/three/entity/models/parrot.obj +509 -0
  142. package/src/three/entity/models/piglin.obj +739 -0
  143. package/src/three/entity/models/pillager.obj +371 -0
  144. package/src/three/entity/models/rabbit.obj +555 -0
  145. package/src/three/entity/models/sheep.obj +555 -0
  146. package/src/three/entity/models/shulker.obj +141 -0
  147. package/src/three/entity/models/sniffer.obj +693 -0
  148. package/src/three/entity/models/spider.obj +509 -0
  149. package/src/three/entity/models/tadpole.obj +95 -0
  150. package/src/three/entity/models/turtle.obj +371 -0
  151. package/src/three/entity/models/vex.obj +325 -0
  152. package/src/three/entity/models/villager.obj +509 -0
  153. package/src/three/entity/models/warden.obj +463 -0
  154. package/src/three/entity/models/witch.obj +647 -0
  155. package/src/three/entity/models/wolf.obj +509 -0
  156. package/src/three/entity/models/zombie_villager.obj +463 -0
  157. package/src/three/entity/objModels.js +1 -0
  158. package/src/three/fireworks.ts +661 -0
  159. package/src/three/fireworksRenderer.ts +434 -0
  160. package/src/three/globals.d.ts +7 -0
  161. package/src/three/graphicsBackend.ts +274 -0
  162. package/src/three/graphicsBackendOffThread.ts +107 -0
  163. package/src/three/hand.ts +89 -0
  164. package/src/three/holdingBlock.ts +926 -0
  165. package/src/three/index.ts +20 -0
  166. package/src/three/itemMesh.ts +427 -0
  167. package/src/three/modules.d.ts +14 -0
  168. package/src/three/panorama.ts +308 -0
  169. package/src/three/panoramaShared.ts +1 -0
  170. package/src/three/renderSlot.ts +82 -0
  171. package/src/three/skyboxRenderer.ts +406 -0
  172. package/src/three/starField.ts +13 -0
  173. package/src/three/threeJsMedia.ts +731 -0
  174. package/src/three/threeJsMethods.ts +15 -0
  175. package/src/three/threeJsParticles.ts +160 -0
  176. package/src/three/threeJsSound.ts +95 -0
  177. package/src/three/threeJsUtils.ts +90 -0
  178. package/src/three/waypointSprite.ts +435 -0
  179. package/src/three/waypoints.ts +163 -0
  180. package/src/three/world/cursorBlock.ts +172 -0
  181. package/src/three/world/vr.ts +257 -0
  182. package/src/three/worldGeometryExport.ts +259 -0
  183. package/src/three/worldGeometryHandler.ts +279 -0
  184. package/src/three/worldRendererThree.ts +1381 -0
  185. package/src/worldView/index.ts +6 -0
  186. package/src/worldView/types.ts +66 -0
  187. package/src/worldView/worldView.ts +424 -0
@@ -0,0 +1,381 @@
1
+ /**
2
+ * Canvas 2D Overlay for Three.js WebGL Canvas
3
+ *
4
+ * Provides methods to draw 2D graphics on top of Three.js rendering
5
+ * Works in both main thread and Web Worker contexts (OffscreenCanvas)
6
+ */
7
+
8
+ import * as THREE from 'three'
9
+
10
+ export class Canvas2DOverlay {
11
+ private gl: WebGLRenderingContext | WebGL2RenderingContext
12
+ private overlayScene: THREE.Scene
13
+ private overlayCamera: THREE.OrthographicCamera
14
+ private overlayObjects: THREE.Mesh[] = []
15
+
16
+ constructor(
17
+ private renderer: THREE.WebGLRenderer,
18
+ private canvas: HTMLCanvasElement | OffscreenCanvas
19
+ ) {
20
+ this.gl = this.renderer.getContext()
21
+
22
+ // Setup orthographic camera for 2D overlay
23
+ this.overlayCamera = new THREE.OrthographicCamera(
24
+ 0, // left
25
+ this.canvas.width, // right
26
+ this.canvas.height, // top
27
+ 0, // bottom
28
+ 0.1, // near
29
+ 1000 // far
30
+ )
31
+ this.overlayCamera.position.z = 10
32
+
33
+ this.overlayScene = new THREE.Scene()
34
+ }
35
+
36
+ /**
37
+ * Update camera when canvas size changes
38
+ */
39
+ updateSize(width: number, height: number) {
40
+ this.overlayCamera.left = 0
41
+ this.overlayCamera.right = width
42
+ this.overlayCamera.top = height
43
+ this.overlayCamera.bottom = 0
44
+ this.overlayCamera.updateProjectionMatrix()
45
+ }
46
+
47
+ /**
48
+ * Clear all overlay objects
49
+ */
50
+ clear() {
51
+ for (const obj of this.overlayObjects) {
52
+ obj.geometry.dispose()
53
+ if (obj.material instanceof THREE.Material) {
54
+ obj.material.dispose()
55
+ }
56
+ this.overlayScene.remove(obj)
57
+ }
58
+ this.overlayObjects = []
59
+ }
60
+
61
+ /**
62
+ * Draw a filled rectangle (2D box)
63
+ * @param x X position (pixels from left)
64
+ * @param y Y position (pixels from top)
65
+ * @param width Width in pixels
66
+ * @param height Height in pixels
67
+ * @param color Color (hex or CSS color)
68
+ * @param opacity Opacity (0-1)
69
+ */
70
+ drawRect(x: number, y: number, width: number, height: number, color: number | string = 0x000000, opacity = 1) {
71
+ const geometry = new THREE.PlaneGeometry(width, height)
72
+ const material = new THREE.MeshBasicMaterial({
73
+ color: typeof color === 'string' ? new THREE.Color(color) : color,
74
+ transparent: opacity < 1,
75
+ opacity,
76
+ depthTest: false, // Always render on top
77
+ depthWrite: false
78
+ })
79
+
80
+ const mesh = new THREE.Mesh(geometry, material)
81
+
82
+ // Position: origin is top-left for pixel coordinates
83
+ mesh.position.set(
84
+ x + width / 2, // Center X
85
+ y + height / 2, // Center Y (from top)
86
+ 0
87
+ )
88
+
89
+ this.overlayScene.add(mesh)
90
+ this.overlayObjects.push(mesh)
91
+
92
+ return mesh
93
+ }
94
+
95
+ /**
96
+ * Draw a rectangle outline (border only)
97
+ */
98
+ drawRectOutline(x: number, y: number, width: number, height: number, color: number | string = 0x000000, lineWidth = 1, opacity = 1) {
99
+ const points = [
100
+ new THREE.Vector3(x, y, 0),
101
+ new THREE.Vector3(x + width, y, 0),
102
+ new THREE.Vector3(x + width, y + height, 0),
103
+ new THREE.Vector3(x, y + height, 0),
104
+ new THREE.Vector3(x, y, 0)
105
+ ]
106
+
107
+ const geometry = new THREE.BufferGeometry().setFromPoints(points)
108
+ const material = new THREE.LineBasicMaterial({
109
+ color: typeof color === 'string' ? new THREE.Color(color) : color,
110
+ transparent: opacity < 1,
111
+ opacity,
112
+ linewidth: lineWidth // Note: linewidth may not work on all platforms
113
+ })
114
+
115
+ const line = new THREE.Line(geometry, material)
116
+ this.overlayScene.add(line)
117
+ this.overlayObjects.push(line as any)
118
+
119
+ return line
120
+ }
121
+
122
+ /**
123
+ * Draw text using canvas texture (works in worker with OffscreenCanvas)
124
+ */
125
+ drawText(text: string, x: number, y: number, options: {
126
+ fontSize?: number
127
+ fontFamily?: string
128
+ color?: string
129
+ backgroundColor?: string
130
+ padding?: number
131
+ opacity?: number
132
+ } = {}) {
133
+ const {
134
+ fontSize = 16,
135
+ fontFamily = 'Arial',
136
+ color = '#ffffff',
137
+ backgroundColor = '#000000',
138
+ padding = 4,
139
+ opacity = 1
140
+ } = options
141
+
142
+ // Create a temporary canvas for text rendering
143
+ // OffscreenCanvas works in workers!
144
+ const textCanvas = new OffscreenCanvas(512, 128)
145
+ const ctx = textCanvas.getContext('2d')!
146
+
147
+ ctx.font = `${fontSize}px ${fontFamily}`
148
+ const metrics = ctx.measureText(text)
149
+ const textWidth = metrics.width
150
+ const textHeight = fontSize
151
+
152
+ // Resize canvas to fit text
153
+ textCanvas.width = Math.ceil(textWidth + padding * 2)
154
+ textCanvas.height = Math.ceil(textHeight + padding * 2)
155
+
156
+ // Redraw with proper size
157
+ ctx.font = `${fontSize}px ${fontFamily}`
158
+ ctx.fillStyle = backgroundColor
159
+ ctx.fillRect(0, 0, textCanvas.width, textCanvas.height)
160
+
161
+ ctx.fillStyle = color
162
+ ctx.textBaseline = 'top'
163
+ ctx.fillText(text, padding, padding)
164
+
165
+ // Create texture from canvas
166
+ const texture = new THREE.CanvasTexture(textCanvas as any)
167
+ texture.needsUpdate = true
168
+
169
+ const material = new THREE.MeshBasicMaterial({
170
+ map: texture,
171
+ transparent: true,
172
+ opacity,
173
+ depthTest: false,
174
+ depthWrite: false
175
+ })
176
+
177
+ const geometry = new THREE.PlaneGeometry(textCanvas.width, textCanvas.height)
178
+ const mesh = new THREE.Mesh(geometry, material)
179
+
180
+ mesh.position.set(
181
+ x + textCanvas.width / 2,
182
+ y + textCanvas.height / 2,
183
+ 0
184
+ )
185
+
186
+ this.overlayScene.add(mesh)
187
+ this.overlayObjects.push(mesh)
188
+
189
+ return mesh
190
+ }
191
+
192
+ /**
193
+ * Draw a circle
194
+ */
195
+ drawCircle(x: number, y: number, radius: number, color: number | string = 0x000000, opacity = 1) {
196
+ const geometry = new THREE.CircleGeometry(radius, 32)
197
+ const material = new THREE.MeshBasicMaterial({
198
+ color: typeof color === 'string' ? new THREE.Color(color) : color,
199
+ transparent: opacity < 1,
200
+ opacity,
201
+ depthTest: false,
202
+ depthWrite: false
203
+ })
204
+
205
+ const mesh = new THREE.Mesh(geometry, material)
206
+ mesh.position.set(x, y, 0)
207
+
208
+ this.overlayScene.add(mesh)
209
+ this.overlayObjects.push(mesh)
210
+
211
+ return mesh
212
+ }
213
+
214
+ /**
215
+ * Render the overlay on top of Three.js scene
216
+ * Call this after rendering your main 3D scene
217
+ */
218
+ render() {
219
+ // Disable depth test so overlay renders on top
220
+ const oldAutoClear = this.renderer.autoClear
221
+ this.renderer.autoClear = false
222
+
223
+ this.renderer.render(this.overlayScene, this.overlayCamera)
224
+
225
+ this.renderer.autoClear = oldAutoClear
226
+ }
227
+
228
+ /**
229
+ * Dispose of all resources
230
+ */
231
+ dispose() {
232
+ this.clear()
233
+ this.overlayScene.clear()
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Alternative: Direct WebGL 2D Drawing
239
+ * For more performance or if you need more control
240
+ */
241
+ export class WebGLDirect2DOverlay {
242
+ private gl: WebGLRenderingContext | WebGL2RenderingContext
243
+ private program: WebGLProgram
244
+ private positionBuffer: WebGLBuffer
245
+ private colorBuffer: WebGLBuffer
246
+
247
+ constructor(
248
+ private renderer: THREE.WebGLRenderer,
249
+ private canvas: HTMLCanvasElement | OffscreenCanvas
250
+ ) {
251
+ this.gl = this.renderer.getContext()
252
+
253
+ // Create shader program for 2D rendering
254
+ const vertexShader = this.createShader(this.gl.VERTEX_SHADER, `
255
+ attribute vec2 position;
256
+ attribute vec4 color;
257
+ varying vec4 vColor;
258
+ uniform vec2 resolution;
259
+
260
+ void main() {
261
+ // Convert pixel coordinates to clip space (-1 to 1)
262
+ vec2 clipSpace = (position / resolution) * 2.0 - 1.0;
263
+ gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
264
+ vColor = color;
265
+ }
266
+ `)
267
+
268
+ const fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, `
269
+ precision mediump float;
270
+ varying vec4 vColor;
271
+
272
+ void main() {
273
+ gl_FragColor = vColor;
274
+ }
275
+ `)
276
+
277
+ this.program = this.createProgram(vertexShader, fragmentShader)
278
+
279
+ this.positionBuffer = this.gl.createBuffer()!
280
+ this.colorBuffer = this.gl.createBuffer()!
281
+ }
282
+
283
+ private createShader(type: number, source: string): WebGLShader {
284
+ const shader = this.gl.createShader(type)!
285
+ this.gl.shaderSource(shader, source)
286
+ this.gl.compileShader(shader)
287
+
288
+ if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
289
+ console.error('Shader compilation error:', this.gl.getShaderInfoLog(shader))
290
+ this.gl.deleteShader(shader)
291
+ throw new Error('Shader compilation failed')
292
+ }
293
+
294
+ return shader
295
+ }
296
+
297
+ private createProgram(vertexShader: WebGLShader, fragmentShader: WebGLShader): WebGLProgram {
298
+ const program = this.gl.createProgram()!
299
+ this.gl.attachShader(program, vertexShader)
300
+ this.gl.attachShader(program, fragmentShader)
301
+ this.gl.linkProgram(program)
302
+
303
+ if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {
304
+ console.error('Program linking error:', this.gl.getProgramInfoLog(program))
305
+ throw new Error('Program linking failed')
306
+ }
307
+
308
+ return program
309
+ }
310
+
311
+ /**
312
+ * Draw a rectangle using raw WebGL
313
+ */
314
+ drawRect(x: number, y: number, width: number, height: number, r: number, g: number, b: number, a = 1) {
315
+ const x1 = x
316
+ const y1 = y
317
+ const x2 = x + width
318
+ const y2 = y + height
319
+
320
+ const positions = new Float32Array([
321
+ x1, y1,
322
+ x2, y1,
323
+ x1, y2,
324
+ x1, y2,
325
+ x2, y1,
326
+ x2, y2
327
+ ])
328
+
329
+ const colors = new Float32Array([
330
+ r, g, b, a,
331
+ r, g, b, a,
332
+ r, g, b, a,
333
+ r, g, b, a,
334
+ r, g, b, a,
335
+ r, g, b, a
336
+ ])
337
+
338
+ this.gl.useProgram(this.program)
339
+
340
+ // Set resolution uniform
341
+ const resolutionLocation = this.gl.getUniformLocation(this.program, 'resolution')
342
+ this.gl.uniform2f(resolutionLocation, this.canvas.width, this.canvas.height)
343
+
344
+ // Setup position attribute
345
+ const positionLocation = this.gl.getAttribLocation(this.program, 'position')
346
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer)
347
+ this.gl.bufferData(this.gl.ARRAY_BUFFER, positions, this.gl.STATIC_DRAW)
348
+ this.gl.enableVertexAttribArray(positionLocation)
349
+ this.gl.vertexAttribPointer(positionLocation, 2, this.gl.FLOAT, false, 0, 0)
350
+
351
+ // Setup color attribute
352
+ const colorLocation = this.gl.getAttribLocation(this.program, 'color')
353
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.colorBuffer)
354
+ this.gl.bufferData(this.gl.ARRAY_BUFFER, colors, this.gl.STATIC_DRAW)
355
+ this.gl.enableVertexAttribArray(colorLocation)
356
+ this.gl.vertexAttribPointer(colorLocation, 4, this.gl.FLOAT, false, 0, 0)
357
+
358
+ // Enable blending for transparency
359
+ this.gl.enable(this.gl.BLEND)
360
+ this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA)
361
+
362
+ // Disable depth test to render on top
363
+ this.gl.disable(this.gl.DEPTH_TEST)
364
+
365
+ this.gl.drawArrays(this.gl.TRIANGLES, 0, 6)
366
+
367
+ // Restore state
368
+ this.gl.enable(this.gl.DEPTH_TEST)
369
+ }
370
+
371
+ dispose() {
372
+ this.gl.deleteBuffer(this.positionBuffer)
373
+ this.gl.deleteBuffer(this.colorBuffer)
374
+ this.gl.deleteProgram(this.program)
375
+ }
376
+ }
377
+
378
+
379
+
380
+
381
+
@@ -0,0 +1,29 @@
1
+ export function buildCleanupDecorator (cleanupMethod: string) {
2
+ return function () {
3
+ return function (_target: { snapshotInitialValues }, propertyKey: string) {
4
+ const target = _target as any
5
+ // Store the initial value of the property
6
+ if (!target._snapshotMethodPatched) {
7
+ target.snapshotInitialValues = function () {
8
+ this._initialValues = {}
9
+ for (const key of target._toCleanup) {
10
+ this._initialValues[key] = this[key]
11
+ }
12
+ }
13
+ target._snapshotMethodPatched = true
14
+ }
15
+ (target._toCleanup ??= []).push(propertyKey)
16
+ if (!target._cleanupPatched) {
17
+ const originalMethod = target[cleanupMethod]
18
+ target[cleanupMethod] = function () {
19
+ for (const key of target._toCleanup) {
20
+ this[key] = this._initialValues[key]
21
+ }
22
+ // eslint-disable-next-line prefer-rest-params
23
+ Reflect.apply(originalMethod, this, arguments)
24
+ }
25
+ }
26
+ target._cleanupPatched = true
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,55 @@
1
+ import { PlayerObject, PlayerAnimation } from 'skinview3d'
2
+ import * as THREE from 'three'
3
+ import { WalkingGeneralSwing } from '../three/entity/animations'
4
+ import { loadSkinImage, stevePngUrl } from './utils/skins'
5
+
6
+ export type PlayerObjectType = PlayerObject & {
7
+ animation?: PlayerAnimation
8
+ realPlayerUuid: string
9
+ realUsername: string
10
+ }
11
+
12
+ export function createPlayerObject (options: {
13
+ username?: string
14
+ uuid?: string
15
+ scale?: number
16
+ }): {
17
+ playerObject: PlayerObjectType
18
+ wrapper: THREE.Group
19
+ } {
20
+ const wrapper = new THREE.Group()
21
+ const playerObject = new PlayerObject() as PlayerObjectType
22
+
23
+ playerObject.realPlayerUuid = options.uuid ?? ''
24
+ playerObject.realUsername = options.username ?? ''
25
+ playerObject.position.set(0, 16, 0)
26
+
27
+ // fix issues with starfield
28
+ playerObject.traverse((obj) => {
29
+ if (obj instanceof THREE.Mesh && obj.material instanceof THREE.MeshStandardMaterial) {
30
+ obj.material.transparent = true
31
+ }
32
+ })
33
+
34
+ wrapper.add(playerObject as any)
35
+ const scale = options.scale ?? (1 / 16)
36
+ wrapper.scale.set(scale, scale, scale)
37
+ wrapper.rotation.set(0, Math.PI, 0)
38
+
39
+ // Set up animation
40
+ playerObject.animation = new WalkingGeneralSwing()
41
+ ;(playerObject.animation as WalkingGeneralSwing).isMoving = false
42
+ playerObject.animation.update(playerObject, 0)
43
+
44
+ return { playerObject, wrapper }
45
+ }
46
+
47
+ export const applySkinToPlayerObject = async (playerObject: PlayerObjectType, skinUrl: string) => {
48
+ return loadSkinImage(skinUrl || stevePngUrl).then(({ canvas }) => {
49
+ const skinTexture = new THREE.CanvasTexture(canvas)
50
+ skinTexture.magFilter = THREE.NearestFilter
51
+ skinTexture.minFilter = THREE.NearestFilter
52
+ skinTexture.needsUpdate = true
53
+ playerObject.skin.map = skinTexture as any
54
+ }).catch(console.error)
55
+ }
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Frame Timing Collector (Worker-side)
3
+ *
4
+ * Collects frame timing events and sends them to main thread via nonReactiveState
5
+ * This runs in the worker context and updates the shared state
6
+ */
7
+
8
+ import { NonReactiveState } from '@/graphicsBackend'
9
+
10
+
11
+ export interface FrameTimingEvent {
12
+ type: 'frameStart' | 'frameEnd' | 'cameraUpdate' | 'frameDisplay'
13
+ timestamp: number
14
+ duration?: number
15
+ }
16
+
17
+ export class FrameTimingCollector {
18
+ private events: FrameTimingEvent[] = []
19
+ private frozenEvents: FrameTimingEvent[] = []
20
+ private lastSecondEvents: FrameTimingEvent[] = []
21
+
22
+ private currentFrameStartTime: number | null = null
23
+ private lastInteractionTime = 0
24
+ private lastSecondUpdateTime = 0
25
+
26
+ private readonly timeWindowMs = 5000 // Show last 5 seconds
27
+ private readonly lastSecondWindowMs = 1000 // Show last 1 second
28
+ private readonly maxEvents = 500 // Limit events to prevent memory issues
29
+ private readonly lastSecondUpdateInterval = 1000 // Update once per second
30
+
31
+ constructor(
32
+ private nonReactiveState: NonReactiveState
33
+ ) {
34
+ // Initialize timeline if not exists
35
+ if (!this.nonReactiveState.renderer) {
36
+ (this.nonReactiveState as any).renderer = { timeline: { live: [], frozen: [], lastSecond: [] } }
37
+ }
38
+ if (!this.nonReactiveState.renderer.timeline) {
39
+ this.nonReactiveState.renderer.timeline = { live: [], frozen: [], lastSecond: [] }
40
+ }
41
+
42
+ // Start interval to update last second timeline
43
+ setInterval(() => {
44
+ this.updateLastSecondTimeline()
45
+ }, this.lastSecondUpdateInterval)
46
+ }
47
+
48
+ markFrameStart() {
49
+ this.currentFrameStartTime = performance.now()
50
+ this.addEvent({
51
+ type: 'frameStart',
52
+ timestamp: this.currentFrameStartTime
53
+ })
54
+ }
55
+
56
+ markFrameEnd() {
57
+ if (!this.currentFrameStartTime) return
58
+
59
+ const now = performance.now()
60
+ const duration = now - this.currentFrameStartTime
61
+
62
+ this.addEvent({
63
+ type: 'frameEnd',
64
+ timestamp: now,
65
+ duration
66
+ })
67
+
68
+ this.currentFrameStartTime = null
69
+ }
70
+
71
+ markCameraUpdate(posIsFalsey: boolean) {
72
+ if (!posIsFalsey) return
73
+
74
+ const now = performance.now()
75
+ this.lastInteractionTime = now
76
+
77
+ this.addEvent({
78
+ type: 'cameraUpdate',
79
+ timestamp: now
80
+ })
81
+
82
+ // Update frozen timeline when interaction happens
83
+ this.frozenEvents = [...this.events]
84
+ this.trimEvents(this.frozenEvents)
85
+ this.syncFrozenEvents()
86
+ }
87
+
88
+ markFrameDisplay() {
89
+ this.addEvent({
90
+ type: 'frameDisplay',
91
+ timestamp: performance.now()
92
+ })
93
+ }
94
+
95
+ private addEvent(event: FrameTimingEvent) {
96
+ this.events.push(event)
97
+ this.lastSecondEvents.push(event)
98
+ this.trimEvents(this.events)
99
+ this.trimLastSecondEvents()
100
+ this.syncLiveEvents()
101
+ // Note: lastSecond syncs separately via interval
102
+ }
103
+
104
+ private trimEvents(events: FrameTimingEvent[]) {
105
+ const now = performance.now()
106
+ const cutoff = now - this.timeWindowMs
107
+
108
+ // Remove old events
109
+ while (events.length > 0 && events[0].timestamp < cutoff) {
110
+ events.shift()
111
+ }
112
+
113
+ // Limit total events to prevent memory issues
114
+ if (events.length > this.maxEvents) {
115
+ events.splice(0, events.length - this.maxEvents)
116
+ }
117
+ }
118
+
119
+ private trimLastSecondEvents() {
120
+ const now = performance.now()
121
+ const cutoff = now - this.lastSecondWindowMs
122
+
123
+ // Remove old events
124
+ while (this.lastSecondEvents.length > 0 && this.lastSecondEvents[0].timestamp < cutoff) {
125
+ this.lastSecondEvents.shift()
126
+ }
127
+ }
128
+
129
+ private updateLastSecondTimeline() {
130
+ const now = performance.now()
131
+
132
+ // Only update if at least 1 second has passed
133
+ if (now - this.lastSecondUpdateTime < this.lastSecondUpdateInterval) {
134
+ return
135
+ }
136
+
137
+ this.lastSecondUpdateTime = now
138
+ this.trimLastSecondEvents()
139
+ this.syncLastSecondEvents()
140
+ }
141
+
142
+ private syncLiveEvents() {
143
+ // Update nonReactiveState with current events
144
+ // This will sync to main thread via workerProxy
145
+ this.nonReactiveState.renderer.timeline.live = [...this.events]
146
+ }
147
+
148
+ private syncFrozenEvents() {
149
+ this.nonReactiveState.renderer.timeline.frozen = [...this.frozenEvents]
150
+ }
151
+
152
+ private syncLastSecondEvents() {
153
+ this.nonReactiveState.renderer.timeline.lastSecond = [...this.lastSecondEvents]
154
+ }
155
+
156
+ clear() {
157
+ this.events = []
158
+ this.frozenEvents = []
159
+ this.lastSecondEvents = []
160
+ this.syncLiveEvents()
161
+ this.syncFrozenEvents()
162
+ this.syncLastSecondEvents()
163
+ }
164
+ }