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,769 @@
1
+ import { Vec3 } from 'vec3'
2
+ import worldBlockProvider, { WorldBlockProvider } from 'mc-assets/dist/worldBlockProvider'
3
+ import legacyJson from '../lib/preflatMap.json'
4
+ import { BlockType } from '../../../playground/shared'
5
+ import { World, BlockModelPartsResolved, WorldBlock as Block, WorldBlock, worldColumnKey } from './world'
6
+ import { BlockElement, buildRotationMatrix, elemFaces, matmul3, matmulmat3, vecadd3, vecsub3 } from './modelsGeometryCommon'
7
+ import { INVISIBLE_BLOCKS } from './worldConstants'
8
+ import { MesherGeometryOutput, HighestBlockInfo } from './shared'
9
+
10
+
11
+ let blockProvider: WorldBlockProvider
12
+
13
+ const tints: any = {}
14
+ let needTiles = false
15
+
16
+ let tintsData
17
+ try {
18
+ tintsData = require('esbuild-data').tints
19
+ } catch (err) {
20
+ tintsData = require('minecraft-data/minecraft-data/data/pc/1.16.2/tints.json')
21
+ }
22
+ for (const key of Object.keys(tintsData)) {
23
+ tints[key] = prepareTints(tintsData[key])
24
+ }
25
+
26
+ type Tiles = {
27
+ [blockPos: string]: BlockType
28
+ }
29
+
30
+ function prepareTints (tints) {
31
+ const map = new Map()
32
+ const defaultValue = tintToGl(tints.default)
33
+ for (let { keys, color } of tints.data) {
34
+ color = tintToGl(color)
35
+ for (const key of keys) {
36
+ map.set(`${key}`, color)
37
+ }
38
+ }
39
+ return new Proxy(map, {
40
+ get (target, key) {
41
+ return target.has(key) ? target.get(key) : defaultValue
42
+ }
43
+ })
44
+ }
45
+
46
+ const calculatedBlocksEntries = Object.entries(legacyJson.clientCalculatedBlocks)
47
+ export function preflatBlockCalculation (block: Block, world: World, position: Vec3) {
48
+ const type = calculatedBlocksEntries.find(([name, blocks]) => blocks.includes(block.name))?.[0]
49
+ if (!type) return
50
+ switch (type) {
51
+ case 'directional': {
52
+ const isSolidConnection = !block.name.includes('redstone') && !block.name.includes('tripwire')
53
+ const neighbors = [
54
+ world.getBlock(position.offset(0, 0, 1)),
55
+ world.getBlock(position.offset(0, 0, -1)),
56
+ world.getBlock(position.offset(1, 0, 0)),
57
+ world.getBlock(position.offset(-1, 0, 0))
58
+ ]
59
+ // set needed props to true: east:'false',north:'false',south:'false',west:'false'
60
+ const props = {}
61
+ let changed = false
62
+ for (const [i, neighbor] of neighbors.entries()) {
63
+ const isConnectedToSolid = isSolidConnection ? (neighbor && !neighbor.transparent) : false
64
+ if (isConnectedToSolid || neighbor?.name === block.name) {
65
+ props[['south', 'north', 'east', 'west'][i]] = 'true'
66
+ changed = true
67
+ }
68
+ }
69
+ return changed ? props : undefined
70
+ }
71
+ // case 'gate_in_wall': {}
72
+ case 'block_snowy': {
73
+ const aboveIsSnow = world.getBlock(position.offset(0, 1, 0))?.name === 'snow'
74
+ if (aboveIsSnow) {
75
+ return {
76
+ snowy: `${aboveIsSnow}`
77
+ }
78
+ } else {
79
+ return
80
+ }
81
+ }
82
+ case 'door': {
83
+ // upper half matches lower in
84
+ const { half } = block.getProperties()
85
+ if (half === 'upper') {
86
+ // copy other properties
87
+ const lower = world.getBlock(position.offset(0, -1, 0))
88
+ if (lower?.name === block.name) {
89
+ return {
90
+ ...lower.getProperties(),
91
+ half: 'upper'
92
+ }
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }
98
+
99
+ function tintToGl (tint) {
100
+ const r = (tint >> 16) & 0xff
101
+ const g = (tint >> 8) & 0xff
102
+ const b = tint & 0xff
103
+ return [r / 255, g / 255, b / 255]
104
+ }
105
+
106
+ function getLiquidRenderHeight (world: World, block: WorldBlock | null, type: number, pos: Vec3, isWater: boolean, isRealWater: boolean) {
107
+ if ((isWater && !isRealWater) || (block && isBlockWaterlogged(block))) return 8 / 9
108
+ if (!block || block.type !== type) return 1 / 9
109
+ if (block.metadata === 0) { // source block
110
+ const blockAbove = world.getBlock(pos.offset(0, 1, 0))
111
+ if (blockAbove && blockAbove.type === type) return 1
112
+ return 8 / 9
113
+ }
114
+ return ((block.metadata >= 8 ? 8 : 7 - block.metadata) + 1) / 9
115
+ }
116
+
117
+
118
+ const isCube = (block: Block) => {
119
+ if (!block || block.transparent) return false
120
+ if (block.isCube) return true
121
+ if (!block.models?.length || block.models.length !== 1) return false
122
+ // all variants
123
+ return block.models[0].every(v => v.elements.every(e => {
124
+ return e.from[0] === 0 && e.from[1] === 0 && e.from[2] === 0 && e.to[0] === 16 && e.to[1] === 16 && e.to[2] === 16
125
+ }))
126
+ }
127
+
128
+ const getVec = (v: Vec3, dir: Vec3) => {
129
+ for (const coord of ['x', 'y', 'z']) {
130
+ if (Math.abs(dir[coord]) > 0) v[coord] = 0
131
+ }
132
+ return v.plus(dir)
133
+ }
134
+
135
+ function renderLiquid (world: World, cursor: Vec3, texture: any | undefined, type: number, biome: string, water: boolean, attr: MesherGeometryOutput, isRealWater: boolean) {
136
+ const heights: number[] = []
137
+ for (let z = -1; z <= 1; z++) {
138
+ for (let x = -1; x <= 1; x++) {
139
+ const pos = cursor.offset(x, 0, z)
140
+ heights.push(getLiquidRenderHeight(world, world.getBlock(pos), type, pos, water, isRealWater))
141
+ }
142
+ }
143
+ const cornerHeights = [
144
+ Math.max(Math.max(heights[0], heights[1]), Math.max(heights[3], heights[4])),
145
+ Math.max(Math.max(heights[1], heights[2]), Math.max(heights[4], heights[5])),
146
+ Math.max(Math.max(heights[3], heights[4]), Math.max(heights[6], heights[7])),
147
+ Math.max(Math.max(heights[4], heights[5]), Math.max(heights[7], heights[8]))
148
+ ]
149
+
150
+ // eslint-disable-next-line guard-for-in
151
+ for (const face in elemFaces) {
152
+ const { dir, corners, mask1, mask2 } = elemFaces[face]
153
+ const isUp = dir[1] === 1
154
+
155
+ const neighborPos = cursor.offset(...dir as [number, number, number])
156
+ const neighbor = world.getBlock(neighborPos)
157
+ if (!neighbor) continue
158
+ if (neighbor.type === type || (water && (neighbor.name === 'water' || isBlockWaterlogged(neighbor)))) continue
159
+ if (isCube(neighbor) && !isUp) continue
160
+
161
+ let tint = [1, 1, 1]
162
+ if (water) {
163
+ let m = 1 // Fake lighting to improve lisibility
164
+ if (Math.abs(dir[0]) > 0) m = 0.6
165
+ else if (Math.abs(dir[2]) > 0) m = 0.8
166
+ tint = tints.water[biome]
167
+ tint = [tint[0] * m, tint[1] * m, tint[2] * m]
168
+ }
169
+
170
+ if (needTiles) {
171
+ const tiles = attr.tiles as Tiles
172
+ tiles[`${cursor.x},${cursor.y},${cursor.z}`] ??= {
173
+ block: 'water',
174
+ faces: [],
175
+ }
176
+ tiles[`${cursor.x},${cursor.y},${cursor.z}`].faces.push({
177
+ face,
178
+ neighbor: `${neighborPos.x},${neighborPos.y},${neighborPos.z}`,
179
+ side: 0, // todo
180
+ textureIndex: 0,
181
+ // texture: eFace.texture.name,
182
+ })
183
+ }
184
+
185
+ const { u } = texture
186
+ const { v } = texture
187
+ const { su } = texture
188
+ const { sv } = texture
189
+
190
+ // Get base light value for the face
191
+ const baseLight = world.getLight(neighborPos, undefined, undefined, water ? 'water' : 'lava') / 15
192
+
193
+ for (const pos of corners) {
194
+ const height = cornerHeights[pos[2] * 2 + pos[0]]
195
+ const OFFSET = 0.0001
196
+ attr.t_positions!.push(
197
+ (pos[0] ? 1 - OFFSET : OFFSET) + (cursor.x & 15) - 8,
198
+ (pos[1] ? height - OFFSET : OFFSET) + (cursor.y & 15) - 8,
199
+ (pos[2] ? 1 - OFFSET : OFFSET) + (cursor.z & 15) - 8
200
+ )
201
+ attr.t_normals!.push(...dir)
202
+ attr.t_uvs!.push(pos[3] * su + u, pos[4] * sv * (pos[1] ? 1 : height) + v)
203
+
204
+ let cornerLightResult = baseLight
205
+ if (world.config.smoothLighting) {
206
+ const dx = pos[0] * 2 - 1
207
+ const dy = pos[1] * 2 - 1
208
+ const dz = pos[2] * 2 - 1
209
+ const cornerDir: [number, number, number] = [dx, dy, dz]
210
+ const side1Dir: [number, number, number] = [dx * mask1[0], dy * mask1[1], dz * mask1[2]]
211
+ const side2Dir: [number, number, number] = [dx * mask2[0], dy * mask2[1], dz * mask2[2]]
212
+
213
+ const dirVec = new Vec3(...dir as [number, number, number])
214
+
215
+ const side1LightDir = getVec(new Vec3(...side1Dir), dirVec)
216
+ const side1Light = world.getLight(cursor.plus(side1LightDir)) / 15
217
+ const side2DirLight = getVec(new Vec3(...side2Dir), dirVec)
218
+ const side2Light = world.getLight(cursor.plus(side2DirLight)) / 15
219
+ const cornerLightDir = getVec(new Vec3(...cornerDir), dirVec)
220
+ const cornerLight = world.getLight(cursor.plus(cornerLightDir)) / 15
221
+ // interpolate
222
+ const lights = [side1Light, side2Light, cornerLight, baseLight]
223
+ cornerLightResult = lights.reduce((acc, cur) => acc + cur, 0) / lights.length
224
+ }
225
+
226
+ // Apply light value to tint
227
+ attr.t_colors!.push(tint[0] * cornerLightResult, tint[1] * cornerLightResult, tint[2] * cornerLightResult)
228
+ }
229
+ }
230
+ }
231
+
232
+ const identicalCull = (currentElement: BlockElement, neighbor: Block, direction: Vec3) => {
233
+ const dirStr = `${direction.x},${direction.y},${direction.z}`
234
+ const lookForOppositeSide = {
235
+ '0,1,0': 'down',
236
+ '0,-1,0': 'up',
237
+ '1,0,0': 'east',
238
+ '-1,0,0': 'west',
239
+ '0,0,1': 'south',
240
+ '0,0,-1': 'north',
241
+ }[dirStr]!
242
+ const elemCompareForm = {
243
+ '0,1,0': (e: BlockElement) => `${e.from[0]},${e.from[2]}:${e.to[0]},${e.to[2]}`,
244
+ '0,-1,0': (e: BlockElement) => `${e.to[0]},${e.to[2]}:${e.from[0]},${e.from[2]}`,
245
+ '1,0,0': (e: BlockElement) => `${e.from[2]},${e.from[1]}:${e.to[2]},${e.to[1]}`,
246
+ '-1,0,0': (e: BlockElement) => `${e.to[2]},${e.to[1]}:${e.from[2]},${e.from[1]}`,
247
+ '0,0,1': (e: BlockElement) => `${e.from[1]},${e.from[2]}:${e.to[1]},${e.to[2]}`,
248
+ '0,0,-1': (e: BlockElement) => `${e.to[1]},${e.to[2]}:${e.from[1]},${e.from[2]}`,
249
+ }[dirStr]!
250
+ const elementEdgeValidator = {
251
+ '0,1,0': (e: BlockElement) => currentElement.from[1] === 0 && e.to[2] === 16,
252
+ '0,-1,0': (e: BlockElement) => currentElement.from[1] === 0 && e.to[2] === 16,
253
+ '1,0,0': (e: BlockElement) => currentElement.from[0] === 0 && e.to[1] === 16,
254
+ '-1,0,0': (e: BlockElement) => currentElement.from[0] === 0 && e.to[1] === 16,
255
+ '0,0,1': (e: BlockElement) => currentElement.from[2] === 0 && e.to[0] === 16,
256
+ '0,0,-1': (e: BlockElement) => currentElement.from[2] === 0 && e.to[0] === 16,
257
+ }[dirStr]!
258
+ const useVar = 0
259
+ const models = neighbor.models?.map(m => m[useVar] ?? m[0]) ?? []
260
+ // TODO we should support it! rewrite with optimizing general pipeline
261
+ if (models.some(m => m.x || m.y || m.z)) return
262
+ return models.every(model => {
263
+ return (model.elements ?? []).every(element => {
264
+ // todo check alfa on texture
265
+ return !!(element.faces[lookForOppositeSide]?.cullface && elemCompareForm(currentElement) === elemCompareForm(element) && elementEdgeValidator(element))
266
+ })
267
+ })
268
+ }
269
+
270
+ let needSectionRecomputeOnChange = false
271
+
272
+ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: boolean, attr: MesherGeometryOutput, globalMatrix: any, globalShift: any, block: Block, biome: string) {
273
+ const position = cursor
274
+ // const key = `${position.x},${position.y},${position.z}`
275
+ // if (!globalThis.allowedBlocks.includes(key)) return
276
+ const cullIfIdentical = block.name.includes('glass') || block.name.includes('ice')
277
+
278
+ // eslint-disable-next-line guard-for-in
279
+ for (const face in element.faces) {
280
+ const eFace = element.faces[face]
281
+ const { corners, mask1, mask2, side } = elemFaces[face]
282
+ const dir = matmul3(globalMatrix, elemFaces[face].dir)
283
+
284
+ if (eFace.cullface) {
285
+ const neighbor = world.getBlock(cursor.plus(new Vec3(...dir)), blockProvider, {})
286
+ if (neighbor) {
287
+ if (cullIfIdentical && neighbor.stateId === block.stateId) continue
288
+ if (!neighbor.transparent && (isCube(neighbor) || identicalCull(element, neighbor, new Vec3(...dir)))) continue
289
+ } else {
290
+ needSectionRecomputeOnChange = true
291
+ // continue
292
+ }
293
+ }
294
+
295
+ const minx = element.from[0]
296
+ const miny = element.from[1]
297
+ const minz = element.from[2]
298
+ const maxx = element.to[0]
299
+ const maxy = element.to[1]
300
+ const maxz = element.to[2]
301
+
302
+ const texture = eFace.texture as any
303
+ const { u } = texture
304
+ const { v } = texture
305
+ const { su } = texture
306
+ const { sv } = texture
307
+
308
+ const ndx = Math.floor(attr.positions.length / 3)
309
+
310
+ let tint = [1, 1, 1]
311
+ if (eFace.tintindex !== undefined) {
312
+ if (eFace.tintindex === 0) {
313
+ if (block.name === 'redstone_wire') {
314
+ tint = tints.redstone[`${block.getProperties().power}`]
315
+ } else if (block.name === 'birch_leaves' ||
316
+ block.name === 'spruce_leaves' ||
317
+ block.name === 'lily_pad') {
318
+ tint = tints.constant[block.name]
319
+ } else if (block.name.includes('leaves') || block.name === 'vine') {
320
+ tint = tints.foliage[biome]
321
+ } else {
322
+ tint = tints.grass[biome]
323
+ }
324
+ }
325
+ }
326
+
327
+ // UV rotation
328
+ let r = eFace.rotation || 0
329
+ if (face === 'down') {
330
+ r += 180
331
+ }
332
+ const uvcs = Math.cos(r * Math.PI / 180)
333
+ const uvsn = -Math.sin(r * Math.PI / 180)
334
+
335
+ let localMatrix = null as any
336
+ let localShift = null as any
337
+
338
+ if (element.rotation && !needTiles) {
339
+ // Rescale support for block model rotations
340
+ localMatrix = buildRotationMatrix(
341
+ element.rotation.axis,
342
+ element.rotation.angle
343
+ )
344
+
345
+ localShift = vecsub3(
346
+ element.rotation.origin,
347
+ matmul3(
348
+ localMatrix,
349
+ element.rotation.origin
350
+ )
351
+ )
352
+
353
+ // Apply rescale if specified
354
+ if (element.rotation.rescale) {
355
+ const FIT_TO_BLOCK_SCALE_MULTIPLIER = 2 - Math.sqrt(2)
356
+ const angleRad = element.rotation.angle * Math.PI / 180
357
+ const scale = Math.abs(Math.sin(angleRad)) * FIT_TO_BLOCK_SCALE_MULTIPLIER
358
+
359
+ // Get axis vector components (1 for the rotation axis, 0 for others)
360
+ const axisX = element.rotation.axis === 'x' ? 1 : 0
361
+ const axisY = element.rotation.axis === 'y' ? 1 : 0
362
+ const axisZ = element.rotation.axis === 'z' ? 1 : 0
363
+
364
+ // Create scale matrix: scale = (1 - axisComponent) * scaleFactor + 1
365
+ const scaleMatrix = [
366
+ [(1 - axisX) * scale + 1, 0, 0],
367
+ [0, (1 - axisY) * scale + 1, 0],
368
+ [0, 0, (1 - axisZ) * scale + 1]
369
+ ]
370
+
371
+ // Apply scaling to the transformation matrix
372
+ localMatrix = matmulmat3(localMatrix, scaleMatrix)
373
+
374
+ // Recalculate shift with the new matrix
375
+ localShift = vecsub3(
376
+ element.rotation.origin,
377
+ matmul3(
378
+ localMatrix,
379
+ element.rotation.origin
380
+ )
381
+ )
382
+ }
383
+ }
384
+
385
+ const aos: number[] = []
386
+ const neighborPos = position.plus(new Vec3(...dir))
387
+ // 10%
388
+ const baseLight = world.getLight(neighborPos, undefined, undefined, block.name) / 15
389
+ for (const pos of corners) {
390
+ let vertex = [
391
+ (pos[0] ? maxx : minx),
392
+ (pos[1] ? maxy : miny),
393
+ (pos[2] ? maxz : minz)
394
+ ]
395
+
396
+ if (!needTiles) { // 10%
397
+ vertex = vecadd3(matmul3(localMatrix, vertex), localShift)
398
+ vertex = vecadd3(matmul3(globalMatrix, vertex), globalShift)
399
+ vertex = vertex.map(v => v / 16)
400
+
401
+ attr.positions.push(
402
+ vertex[0] + (cursor.x & 15) - 8,
403
+ vertex[1] + (cursor.y & 15) - 8,
404
+ vertex[2] + (cursor.z & 15) - 8
405
+ )
406
+
407
+ attr.normals.push(...dir)
408
+
409
+ const baseu = (pos[3] - 0.5) * uvcs - (pos[4] - 0.5) * uvsn + 0.5
410
+ const basev = (pos[3] - 0.5) * uvsn + (pos[4] - 0.5) * uvcs + 0.5
411
+ attr.uvs.push(baseu * su + u, basev * sv + v)
412
+ }
413
+
414
+ let light = 1
415
+ const { smoothLighting } = world.config
416
+ // const smoothLighting = true
417
+ if (doAO) {
418
+ const dx = pos[0] * 2 - 1
419
+ const dy = pos[1] * 2 - 1
420
+ const dz = pos[2] * 2 - 1
421
+ const cornerDir = matmul3(globalMatrix, [dx, dy, dz])
422
+ const side1Dir = matmul3(globalMatrix, [dx * mask1[0], dy * mask1[1], dz * mask1[2]])
423
+ const side2Dir = matmul3(globalMatrix, [dx * mask2[0], dy * mask2[1], dz * mask2[2]])
424
+ const side1 = world.getBlock(cursor.offset(...side1Dir))
425
+ const side2 = world.getBlock(cursor.offset(...side2Dir))
426
+ const corner = world.getBlock(cursor.offset(...cornerDir))
427
+
428
+ let cornerLightResult = baseLight * 15
429
+
430
+ if (smoothLighting) {
431
+ const dirVec = new Vec3(...dir)
432
+ const getVec = (v: Vec3) => {
433
+ for (const coord of ['x', 'y', 'z']) {
434
+ if (Math.abs(dirVec[coord]) > 0) v[coord] = 0
435
+ }
436
+ return v.plus(dirVec)
437
+ }
438
+ const side1LightDir = getVec(new Vec3(...side1Dir))
439
+ const side1Light = world.getLight(cursor.plus(side1LightDir))
440
+ const side2DirLight = getVec(new Vec3(...side2Dir))
441
+ const side2Light = world.getLight(cursor.plus(side2DirLight))
442
+ const cornerLightDir = getVec(new Vec3(...cornerDir))
443
+ const cornerLight = world.getLight(cursor.plus(cornerLightDir))
444
+ // interpolate
445
+ const lights = [side1Light, side2Light, cornerLight, baseLight * 15]
446
+ cornerLightResult = lights.reduce((acc, cur) => acc + cur, 0) / lights.length
447
+ }
448
+
449
+ const side1Block = world.shouldMakeAo(side1) ? 1 : 0
450
+ const side2Block = world.shouldMakeAo(side2) ? 1 : 0
451
+ const cornerBlock = world.shouldMakeAo(corner) ? 1 : 0
452
+
453
+ // TODO: correctly interpolate ao light based on pos (evaluate once for each corner of the block)
454
+
455
+ const ao = (side1Block && side2Block) ? 0 : (3 - (side1Block + side2Block + cornerBlock))
456
+ // todo light should go upper on lower blocks
457
+ light = (ao + 1) / 4 * (cornerLightResult / 15)
458
+ aos.push(ao)
459
+ }
460
+
461
+ if (!needTiles) {
462
+ attr.colors.push(tint[0] * light, tint[1] * light, tint[2] * light)
463
+ }
464
+ }
465
+
466
+ const lightWithColor = [baseLight * tint[0], baseLight * tint[1], baseLight * tint[2]] as [number, number, number]
467
+
468
+ if (needTiles) {
469
+ const tiles = attr.tiles as Tiles
470
+ tiles[`${cursor.x},${cursor.y},${cursor.z}`] ??= {
471
+ block: block.name,
472
+ faces: [],
473
+ }
474
+ const needsOnlyOneFace = false
475
+ const isTilesEmpty = tiles[`${cursor.x},${cursor.y},${cursor.z}`].faces.length < 1
476
+ if (isTilesEmpty || !needsOnlyOneFace) {
477
+ tiles[`${cursor.x},${cursor.y},${cursor.z}`].faces.push({
478
+ face,
479
+ side,
480
+ textureIndex: eFace.texture.tileIndex,
481
+ neighbor: `${neighborPos.x},${neighborPos.y},${neighborPos.z}`,
482
+ light: baseLight,
483
+ tint: lightWithColor,
484
+ //@ts-expect-error debug prop
485
+ texture: eFace.texture.debugName || block.name,
486
+ } satisfies BlockType['faces'][number])
487
+ }
488
+ }
489
+
490
+ if (!needTiles) {
491
+ if (doAO && aos[0] + aos[3] >= aos[1] + aos[2]) {
492
+ attr.indices[attr.indicesCount++] = ndx
493
+ attr.indices[attr.indicesCount++] = ndx + 3
494
+ attr.indices[attr.indicesCount++] = ndx + 2
495
+ attr.indices[attr.indicesCount++] = ndx
496
+ attr.indices[attr.indicesCount++] = ndx + 1
497
+ attr.indices[attr.indicesCount++] = ndx + 3
498
+ } else {
499
+ attr.indices[attr.indicesCount++] = ndx
500
+ attr.indices[attr.indicesCount++] = ndx + 1
501
+ attr.indices[attr.indicesCount++] = ndx + 2
502
+ attr.indices[attr.indicesCount++] = ndx + 2
503
+ attr.indices[attr.indicesCount++] = ndx + 1
504
+ attr.indices[attr.indicesCount++] = ndx + 3
505
+ }
506
+ }
507
+ }
508
+ }
509
+
510
+ const ALWAYS_WATERLOGGED = new Set([
511
+ 'seagrass',
512
+ 'tall_seagrass',
513
+ 'kelp',
514
+ 'kelp_plant',
515
+ 'bubble_column'
516
+ ])
517
+ const isBlockWaterlogged = (block: Block) => {
518
+ return block.getProperties().waterlogged === true || block.getProperties().waterlogged === 'true' || ALWAYS_WATERLOGGED.has(block.name)
519
+ }
520
+
521
+ let unknownBlockModel: BlockModelPartsResolved
522
+ export function getSectionGeometry (sx: number, sy: number, sz: number, world: World) {
523
+ let delayedRender = [] as Array<() => void>
524
+
525
+ const attr: MesherGeometryOutput = {
526
+ sectionYNumber: (sy - world.config.worldMinY) >> 4,
527
+ chunkKey: worldColumnKey(sx, sz),
528
+ sectionStartY: sy,
529
+ sectionEndY: sy + 16,
530
+ sectionStartX: sx,
531
+ sectionEndX: sx + 16,
532
+ sectionStartZ: sz,
533
+ sectionEndZ: sz + 16,
534
+ sx: sx + 8,
535
+ sy: sy + 8,
536
+ sz: sz + 8,
537
+ positions: [],
538
+ normals: [],
539
+ colors: [],
540
+ uvs: [],
541
+ t_positions: [],
542
+ t_normals: [],
543
+ t_colors: [],
544
+ t_uvs: [],
545
+ indices: [],
546
+ indicesCount: 0, // Track current index position
547
+ using32Array: true,
548
+ tiles: {},
549
+ // todo this can be removed here
550
+ heads: {},
551
+ signs: {},
552
+ banners: {},
553
+ // isFull: true,
554
+ hadErrors: false,
555
+ blocksCount: 0
556
+ }
557
+
558
+ const cursor = new Vec3(0, 0, 0)
559
+ for (cursor.y = sy; cursor.y < sy + 16; cursor.y++) {
560
+ for (cursor.z = sz; cursor.z < sz + 16; cursor.z++) {
561
+ for (cursor.x = sx; cursor.x < sx + 16; cursor.x++) {
562
+ let block = world.getBlock(cursor, blockProvider, attr)!
563
+ if (INVISIBLE_BLOCKS.has(block.name)) continue
564
+ if ((block.name.includes('_sign') || block.name === 'sign') && !world.config.disableBlockEntityTextures) {
565
+ const key = `${cursor.x},${cursor.y},${cursor.z}`
566
+ const props: any = block.getProperties()
567
+ const facingRotationMap = {
568
+ 'north': 2,
569
+ 'south': 0,
570
+ 'west': 1,
571
+ 'east': 3
572
+ }
573
+ const isWall = block.name.endsWith('wall_sign') || block.name.endsWith('wall_hanging_sign')
574
+ const isHanging = block.name.endsWith('hanging_sign')
575
+ attr.signs[key] = {
576
+ isWall,
577
+ isHanging,
578
+ rotation: isWall ? facingRotationMap[props.facing] : +props.rotation
579
+ }
580
+ } else if (block.name === 'player_head' || block.name === 'player_wall_head') {
581
+ const key = `${cursor.x},${cursor.y},${cursor.z}`
582
+ const props: any = block.getProperties()
583
+ const facingRotationMap = {
584
+ 'north': 0,
585
+ 'south': 2,
586
+ 'west': 3,
587
+ 'east': 1
588
+ }
589
+ const isWall = block.name === 'player_wall_head'
590
+ attr.heads[key] = {
591
+ isWall,
592
+ rotation: isWall ? facingRotationMap[props.facing] : +props.rotation
593
+ }
594
+ } else if (block.name.includes('_banner') && !world.config.disableBlockEntityTextures) {
595
+ const key = `${cursor.x},${cursor.y},${cursor.z}`
596
+ const props: any = block.getProperties()
597
+ const facingRotationMap = {
598
+ 'north': 2,
599
+ 'south': 0,
600
+ 'west': 1,
601
+ 'east': 3
602
+ }
603
+ const isWall = block.name.endsWith('_wall_banner')
604
+ attr.banners[key] = {
605
+ isWall,
606
+ blockName: block.name, // Pass block name for base color extraction
607
+ rotation: isWall ? facingRotationMap[props.facing] : (props.rotation === undefined ? 0 : +props.rotation)
608
+ }
609
+ }
610
+ const biome = block.biome.name
611
+
612
+ if (world.preflat) { // 10% perf
613
+ const patchProperties = preflatBlockCalculation(block, world, cursor)
614
+ if (patchProperties) {
615
+ block._originalProperties ??= block._properties
616
+ block._properties = { ...block._originalProperties, ...patchProperties }
617
+ if (block.models && JSON.stringify(block._originalProperties) !== JSON.stringify(block._properties)) {
618
+ // recompute models
619
+ block.models = undefined
620
+ block = world.getBlock(cursor, blockProvider, attr)!
621
+ }
622
+ } else {
623
+ block._properties = block._originalProperties ?? block._properties
624
+ block._originalProperties = undefined
625
+ }
626
+ }
627
+
628
+ const isWaterlogged = isBlockWaterlogged(block)
629
+ if (block.name === 'water' || isWaterlogged) {
630
+ const pos = cursor.clone()
631
+ // eslint-disable-next-line @typescript-eslint/no-loop-func
632
+ delayedRender.push(() => {
633
+ renderLiquid(world, pos, blockProvider.getTextureInfo('water_still'), block.type, biome, true, attr, !isWaterlogged)
634
+ })
635
+ attr.blocksCount++
636
+ } else if (block.name === 'lava') {
637
+ renderLiquid(world, cursor, blockProvider.getTextureInfo('lava_still'), block.type, biome, false, attr, false)
638
+ attr.blocksCount++
639
+ }
640
+ if (block.name !== 'water' && block.name !== 'lava' && !INVISIBLE_BLOCKS.has(block.name)) {
641
+ // cache
642
+ let { models } = block
643
+
644
+ models ??= unknownBlockModel
645
+
646
+ const firstForceVar = world.config.debugModelVariant?.[0]
647
+ let part = 0
648
+ for (const modelVars of models ?? []) {
649
+ const pos = cursor.clone()
650
+ // const variantRuntime = mod(Math.floor(pos.x / 16) + Math.floor(pos.y / 16) + Math.floor(pos.z / 16), modelVars.length)
651
+ const variantRuntime = 0
652
+ const useVariant = world.config.debugModelVariant?.[part] ?? firstForceVar ?? variantRuntime
653
+ part++
654
+ const model = modelVars[useVariant] ?? modelVars[0]
655
+ if (!model) continue
656
+
657
+ // #region 10%
658
+ let globalMatrix = null as any
659
+ let globalShift = null as any
660
+ for (const axis of ['x', 'y', 'z'] as const) {
661
+ if (axis in model) {
662
+ globalMatrix = globalMatrix ?
663
+ matmulmat3(globalMatrix, buildRotationMatrix(axis, -(model[axis] ?? 0))) :
664
+ buildRotationMatrix(axis, -(model[axis] ?? 0))
665
+ }
666
+ }
667
+ if (globalMatrix) {
668
+ globalShift = [8, 8, 8]
669
+ globalShift = vecsub3(globalShift, matmul3(globalMatrix, globalShift))
670
+ }
671
+ // #endregion
672
+
673
+ for (const element of model.elements ?? []) {
674
+ const ao = model.ao ?? block.boundingBox !== 'empty'
675
+ if (block.transparent) {
676
+ const pos = cursor.clone()
677
+ delayedRender.push(() => {
678
+ renderElement(world, pos, element, ao, attr, globalMatrix, globalShift, block, biome)
679
+ })
680
+ } else {
681
+ // 60%
682
+ renderElement(world, cursor, element, ao, attr, globalMatrix, globalShift, block, biome)
683
+ }
684
+ }
685
+ }
686
+ if (part > 0) attr.blocksCount++
687
+ }
688
+ }
689
+ }
690
+ }
691
+
692
+ for (const render of delayedRender) {
693
+ render()
694
+ }
695
+ delayedRender = []
696
+
697
+ let ndx = attr.positions.length / 3
698
+ for (let i = 0; i < attr.t_positions!.length / 12; i++) {
699
+ attr.indices[attr.indicesCount++] = ndx
700
+ attr.indices[attr.indicesCount++] = ndx + 1
701
+ attr.indices[attr.indicesCount++] = ndx + 2
702
+ attr.indices[attr.indicesCount++] = ndx + 2
703
+ attr.indices[attr.indicesCount++] = ndx + 1
704
+ attr.indices[attr.indicesCount++] = ndx + 3
705
+ // back face
706
+ attr.indices[attr.indicesCount++] = ndx
707
+ attr.indices[attr.indicesCount++] = ndx + 2
708
+ attr.indices[attr.indicesCount++] = ndx + 1
709
+ attr.indices[attr.indicesCount++] = ndx + 2
710
+ attr.indices[attr.indicesCount++] = ndx + 3
711
+ attr.indices[attr.indicesCount++] = ndx + 1
712
+ ndx += 4
713
+ }
714
+
715
+ attr.positions.push(...attr.t_positions!)
716
+ attr.normals.push(...attr.t_normals!)
717
+ attr.colors.push(...attr.t_colors!)
718
+ attr.uvs.push(...attr.t_uvs!)
719
+
720
+ delete attr.t_positions
721
+ delete attr.t_normals
722
+ delete attr.t_colors
723
+ delete attr.t_uvs
724
+
725
+ attr.positions = new Float32Array(attr.positions) as any
726
+ attr.normals = new Float32Array(attr.normals) as any
727
+ attr.colors = new Float32Array(attr.colors) as any
728
+ attr.uvs = new Float32Array(attr.uvs) as any
729
+ attr.using32Array = arrayNeedsUint32(attr.indices)
730
+ if (attr.using32Array) {
731
+ attr.indices = new Uint32Array(attr.indices)
732
+ } else {
733
+ attr.indices = new Uint16Array(attr.indices)
734
+ }
735
+
736
+ if (needTiles) {
737
+ delete attr.positions
738
+ delete attr.normals
739
+ delete attr.colors
740
+ delete attr.uvs
741
+ }
742
+
743
+ return attr
744
+ }
745
+
746
+ // copied from three.js
747
+ function arrayNeedsUint32 (array) {
748
+
749
+ // assumes larger values usually on last
750
+
751
+ for (let i = array.length - 1; i >= 0; -- i) {
752
+
753
+ if (array[i] >= 65_535) return true // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565
754
+
755
+ }
756
+
757
+ return false
758
+
759
+ }
760
+
761
+ export const setBlockStatesData = (blockstatesModels, blocksAtlas: any, _needTiles = false, useUnknownBlockModel = true, version = 'latest') => {
762
+ blockProvider = worldBlockProvider(blockstatesModels, blocksAtlas, version)
763
+ globalThis.blockProvider = blockProvider
764
+ if (useUnknownBlockModel) {
765
+ unknownBlockModel = blockProvider.getAllResolvedModels0_1({ name: 'unknown', properties: {} })
766
+ }
767
+
768
+ needTiles = _needTiles
769
+ }