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
package/README.md ADDED
@@ -0,0 +1,297 @@
1
+ # Minecraft Renderer
2
+
3
+ ![Minecraft Renderer](./logo.webp)
4
+
5
+ A modular Minecraft world renderer with Three.js WebGL backend. Designed for performance testing, experimentation, and integration into Minecraft clients.
6
+
7
+ ## Architecture Overview
8
+
9
+ ```
10
+ ┌─────────────────────────────────────────────────────────────────┐
11
+ │ AppViewer │
12
+ │ - Manages graphics backend lifecycle │
13
+ │ - Handles world view and player state │
14
+ │ - Coordinates between data and rendering │
15
+ └─────────────────────────────────────────────────────────────────┘
16
+
17
+
18
+ ┌─────────────────────────────────────────────────────────────────┐
19
+ │ GraphicsBackend (Three.js) │
20
+ │ - WebGL rendering via Three.js │
21
+ │ - Scene, camera, and lighting management │
22
+ │ - Mesher worker coordination │
23
+ └─────────────────────────────────────────────────────────────────┘
24
+
25
+ ┌────────────────────┼────────────────────┐
26
+ ▼ ▼ ▼
27
+ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
28
+ │ DocumentRenderer │ │WorldGeometryHandler│ │ StarField │
29
+ │ - Render loop │ │ - Chunk meshes │ │ - Night sky │
30
+ │ - Canvas sizing │ │ - GPU memory │ │ - Twinkling │
31
+ │ - FPS tracking │ │ - Signs/banners │ │ effect │
32
+ └──────────────────┘ └──────────────────┘ └──────────────────┘
33
+ ```
34
+
35
+ ## Core Components
36
+
37
+ ### WorldView (formerly WorldDataEmitter)
38
+
39
+ Manages chunk loading/unloading and emits world events to the renderer.
40
+
41
+ ```typescript
42
+ import { WorldView } from 'minecraft-renderer'
43
+
44
+ // Create world view with a world provider
45
+ const worldView = new WorldView(worldProvider, renderDistance, startPosition)
46
+
47
+ // Initialize and start loading chunks
48
+ await worldView.init(playerPosition)
49
+
50
+ // Update position (loads/unloads chunks as needed)
51
+ await worldView.updatePosition(newPosition)
52
+
53
+ // Set block and emit update
54
+ worldView.setBlockStateId(position, stateId)
55
+ ```
56
+
57
+ ### AppViewer
58
+
59
+ Main application entry point for integrating the renderer.
60
+
61
+ ```typescript
62
+ import { AppViewer, createGraphicsBackend } from 'minecraft-renderer'
63
+
64
+ const viewer = new AppViewer({
65
+ config: {
66
+ sceneBackground: 'lightblue',
67
+ fpsLimit: 60
68
+ },
69
+ rendererConfig: {
70
+ enableLighting: true,
71
+ smoothLighting: true,
72
+ showChunkBorders: false
73
+ }
74
+ })
75
+
76
+ // Load backend
77
+ await viewer.loadBackend(createGraphicsBackend)
78
+
79
+ // Start rendering world
80
+ await viewer.startWorld(worldProvider, renderDistance)
81
+
82
+ // Update camera each frame
83
+ viewer.updateCamera(position, yaw, pitch)
84
+ ```
85
+
86
+ ## How Block Rendering Works
87
+
88
+ ### 1. Chunk Data Flow
89
+
90
+ ```
91
+ World Provider → WorldView → GraphicsBackend → Mesher Workers → Three.js Scene
92
+ ```
93
+
94
+ 1. **World Provider**: Provides chunk column data (prismarine-chunk format)
95
+ 2. **WorldView**: Emits `loadChunk` events with serialized chunk data
96
+ 3. **GraphicsBackend**: Receives events and dispatches to mesher workers
97
+ 4. **Mesher Workers**: Generate geometry (positions, normals, UVs, colors)
98
+ 5. **Three.js Scene**: Creates BufferGeometry meshes from worker output
99
+
100
+ ### 2. Mesher Worker Communication
101
+
102
+ Workers receive:
103
+ - Block data (chunk JSON with block state IDs)
104
+ - Block models and textures atlas
105
+ - Lighting configuration
106
+
107
+ Workers produce:
108
+ - Float32Array of vertex positions (x, y, z per vertex)
109
+ - Float32Array of normals
110
+ - Float32Array of colors (vertex colors for lighting)
111
+ - Float32Array of UVs (texture coordinates)
112
+ - Uint16/32Array of indices
113
+
114
+ ### 3. Geometry Structure
115
+
116
+ Each block face is a quad with 4 vertices and 6 indices:
117
+
118
+ ```typescript
119
+ interface MesherGeometryOutput {
120
+ positions: Float32Array // [x1,y1,z1, x2,y2,z2, ...]
121
+ normals: Float32Array // [nx,ny,nz, ...]
122
+ colors: Float32Array // [r,g,b, r,g,b, ...] (0-1 range, lighting)
123
+ uvs: Float32Array // [u1,v1, u2,v2, ...] (texture atlas coords)
124
+ indices: Uint32Array // [0,1,2, 2,3,0, ...] (triangles)
125
+ sx, sy, sz: number // Section position offset
126
+ blocksCount: number // Number of non-air blocks
127
+ signs: Record<string, SignData>
128
+ banners: Record<string, BannerData>
129
+ heads: Record<string, HeadData>
130
+ }
131
+ ```
132
+
133
+ ### 4. Block Model Resolution
134
+
135
+ 1. Block state ID → Block state properties
136
+ 2. Block state properties → Blockstate JSON
137
+ 3. Blockstate JSON → Model variants
138
+ 4. Model JSON → Faces with texture references
139
+ 5. Texture references → Atlas UV coordinates
140
+
141
+ ### 5. Lighting Calculation
142
+
143
+ Lighting uses both block light and sky light:
144
+
145
+ ```typescript
146
+ // Light level 0-15 for both block and sky light
147
+ const blockLight = chunk.getBlockLight(pos)
148
+ const skyLight = chunk.getSkyLight(pos)
149
+
150
+ // Combined light level
151
+ const light = Math.max(blockLight, skyLight * skyLightMultiplier)
152
+
153
+ // Light level to color multiplier
154
+ const brightness = lightLevelToBrightness[light]
155
+ // Applied as vertex color: [brightness, brightness, brightness]
156
+ ```
157
+
158
+ ### 6. Ambient Occlusion
159
+
160
+ Smooth lighting uses ambient occlusion based on neighboring blocks:
161
+
162
+ ```typescript
163
+ // For each vertex, check 3 neighboring blocks
164
+ // AO value = (side1 + side2 + corner) / 3
165
+ // Applied as vertex color darkening
166
+ ```
167
+
168
+ ## Configuration
169
+
170
+ ### WorldRendererConfig
171
+
172
+ ```typescript
173
+ interface WorldRendererConfig {
174
+ // Performance
175
+ mesherWorkers: number // Number of worker threads (default: 4)
176
+ addChunksBatchWaitTime: number // Batch delay for chunk loading (ms)
177
+ _experimentalSmoothChunkLoading: boolean
178
+
179
+ // Rendering
180
+ enableLighting: boolean // Enable block/sky lighting
181
+ smoothLighting: boolean // Enable ambient occlusion
182
+ dayCycle: boolean // Enable time-based sky changes
183
+ starfield: boolean // Enable star field at night
184
+ fov: number // Camera field of view
185
+
186
+ // Debug
187
+ showChunkBorders: boolean // Show chunk boundary helpers
188
+ enableDebugOverlay: boolean // Show advanced stats
189
+ clipWorldBelowY: number | undefined // Don't render below Y level
190
+ }
191
+ ```
192
+
193
+ ## Memory Management
194
+
195
+ The renderer implements several memory optimizations:
196
+
197
+ 1. **CPU Array Disposal**: After GPU upload, CPU-side typed arrays are nulled
198
+ 2. **Texture Caching**: Signs and banners share textures via reference counting
199
+ 3. **Section Tracking**: Memory usage is tracked per section for debugging
200
+
201
+ ```typescript
202
+ // Get memory usage
203
+ const { bytes, readable } = worldGeometryHandler.getMemoryUsageReadable()
204
+ console.log(`GPU Memory: ${readable}`) // e.g., "45.32 MB"
205
+ ```
206
+
207
+ ## Performance Tips
208
+
209
+ 1. **Mesher Workers**: Increase `mesherWorkers` on multi-core systems
210
+ 2. **Smooth Loading**: Enable `_experimentalSmoothChunkLoading` to prevent frame drops
211
+ 3. **Clip World**: Use `clipWorldBelowY` to reduce geometry for surface views
212
+ 4. **Disable Lighting**: Set `enableLighting: false` for faster meshing
213
+
214
+ ## Development
215
+
216
+ ```bash
217
+ # Install dependencies
218
+ pnpm install
219
+
220
+ # Run playground
221
+ pnpm dev
222
+
223
+ # Build library
224
+ pnpm build
225
+
226
+ # Type check
227
+ pnpm typecheck
228
+ ```
229
+
230
+ ## File Structure
231
+
232
+ ```
233
+ src/
234
+ ├── index.ts # Main exports
235
+ ├── types.ts # TypeScript types
236
+ ├── config.ts # Default configurations
237
+ ├── appViewer.ts # Main application viewer
238
+ ├── worldView.ts # Chunk loading/events (WorldDataEmitter)
239
+ ├── playerState.ts # Player state management
240
+ ├── three/ # Three.js backend
241
+ │ ├── index.ts # Backend exports
242
+ │ ├── graphicsBackend.ts # Main backend entry
243
+ │ ├── documentRenderer.ts # Render loop management
244
+ │ ├── worldGeometryHandler.ts # Chunk geometry
245
+ │ └── starField.ts # Night sky effect
246
+ └── playground/ # Development environment
247
+ ├── playground.ts # Main playground entry
248
+ └── playground.html # HTML template
249
+ ```
250
+
251
+ ## Integration Example
252
+
253
+ ```typescript
254
+ import { AppViewer, createGraphicsBackend, WorldView } from 'minecraft-renderer'
255
+ import ChunkLoader from 'prismarine-chunk'
256
+ import WorldLoader from 'prismarine-world'
257
+
258
+ // Setup world (using prismarine-world)
259
+ const World = WorldLoader('1.20.4')
260
+ const Chunk = ChunkLoader('1.20.4')
261
+ const world = new World().sync
262
+
263
+ // Create viewer
264
+ const viewer = new AppViewer()
265
+
266
+ // Provide resources (textures, models)
267
+ viewer.resourcesManager = {
268
+ currentConfig: { version: '1.20.4' },
269
+ currentResources: {
270
+ blocksAtlasImage: atlasImage,
271
+ blocksAtlasJson: atlasJson,
272
+ blockstatesModels: modelsData,
273
+ allReady: true
274
+ },
275
+ on: () => {}
276
+ }
277
+
278
+ // Load backend
279
+ await viewer.loadBackend(createGraphicsBackend)
280
+
281
+ // Start world
282
+ await viewer.startWorld(world, 4) // 4 chunk render distance
283
+
284
+ // Initialize world view
285
+ await viewer.worldView!.init(new Vec3(0, 64, 0))
286
+
287
+ // Game loop
288
+ function gameLoop() {
289
+ viewer.updateCamera(playerPosition, playerYaw, playerPitch)
290
+ requestAnimationFrame(gameLoop)
291
+ }
292
+ gameLoop()
293
+ ```
294
+
295
+ ## License
296
+
297
+ MIT
@@ -0,0 +1,83 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Minecraft Renderer Playground</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ html, body {
15
+ width: 100%;
16
+ height: 100%;
17
+ overflow: hidden;
18
+ background: #1a1a1a;
19
+ }
20
+
21
+ #viewer-canvas {
22
+ position: fixed;
23
+ top: 0;
24
+ left: 0;
25
+ width: 100%;
26
+ height: 100%;
27
+ }
28
+
29
+ .lil-gui {
30
+ position: fixed !important;
31
+ top: 10px;
32
+ right: 10px;
33
+ z-index: 1000;
34
+ }
35
+
36
+ .loading-overlay {
37
+ position: fixed;
38
+ top: 0;
39
+ left: 0;
40
+ width: 100%;
41
+ height: 100%;
42
+ background: #1a1a1a;
43
+ display: flex;
44
+ flex-direction: column;
45
+ justify-content: center;
46
+ align-items: center;
47
+ z-index: 10000;
48
+ color: #fff;
49
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
50
+ }
51
+
52
+ .loading-overlay.hidden {
53
+ display: none;
54
+ }
55
+
56
+ .loading-spinner {
57
+ width: 50px;
58
+ height: 50px;
59
+ border: 3px solid #333;
60
+ border-top-color: #4ade80;
61
+ border-radius: 50%;
62
+ animation: spin 1s linear infinite;
63
+ }
64
+
65
+ @keyframes spin {
66
+ to { transform: rotate(360deg); }
67
+ }
68
+
69
+ .loading-text {
70
+ margin-top: 16px;
71
+ font-size: 14px;
72
+ color: #888;
73
+ }
74
+ </style>
75
+ <script defer src="./static/js/lib-polyfill.98986ac5.js"></script><script defer src="./static/js/lib-react.5c9129e0.js"></script><script defer src="./static/js/365.f05233ab.js"></script><script defer src="./static/js/index.092ec5be.js"></script></head>
76
+ <body>
77
+ <div id="react-root"></div>
78
+ <div class="loading-overlay" id="loading">
79
+ <div class="loading-spinner"></div>
80
+ <div class="loading-text">Loading Minecraft Renderer...</div>
81
+ </div>
82
+ </body>
83
+ </html>