minecraft-renderer 0.1.31 → 0.1.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minecraft-renderer",
3
- "version": "0.1.31",
3
+ "version": "0.1.32",
4
4
  "description": "The most Modular Minecraft world renderer with Three.js WebGL backend",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -45,6 +45,7 @@ export const defaultWorldRendererConfig = {
45
45
  // Camera visual related settings
46
46
  showHand: false,
47
47
  viewBobbing: false,
48
+ handRenderer: 'vanilla' as 'vanilla' | 'legacy',
48
49
  renderEars: true,
49
50
  highlightBlockColor: 'blue' as 'blue' | 'classic' | 'auto' | undefined,
50
51
 
@@ -288,7 +288,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
288
288
  if (initial) {
289
289
  callback(this.playerStateReactive[key])
290
290
  }
291
- subscribeKey(this.playerStateReactive, key, callback)
291
+ return subscribeKey(this.playerStateReactive, key, callback)
292
292
  }
293
293
 
294
294
  onReactiveConfigUpdated<T extends keyof typeof this.worldRendererConfig>(key: T, callback: (value: typeof this.worldRendererConfig[T]) => void) {
package/src/three/hand.ts CHANGED
@@ -21,70 +21,121 @@ export const getMyHand = async (image?: string, userName?: string) => {
21
21
 
22
22
  newMap.magFilter = THREE.NearestFilter
23
23
  newMap.minFilter = THREE.NearestFilter
24
- // right arm
25
- const box = new THREE.BoxGeometry()
26
- const material = new THREE.MeshStandardMaterial()
24
+
27
25
  const slim = false
28
- const mesh = new THREE.Mesh(box, material)
29
- mesh.scale.x = slim ? 3 : 4
30
- mesh.scale.y = 12
31
- mesh.scale.z = 4
32
- setSkinUVs(box, 40, 16, slim ? 3 : 4, 12, 4)
26
+ const pixelWidth = slim ? 3 : 4
27
+
28
+ // Exact replica of vanilla's Cube: addBox(-3, -2, -2, 4, 12, 4) at texOffs(40, 16)
29
+ const box = createVanillaCubeGeometry(
30
+ 40, 16,
31
+ slim ? -2 : -3, -2, -2,
32
+ pixelWidth, 12, 4,
33
+ 64, 64
34
+ )
35
+
36
+ const material = new THREE.MeshStandardMaterial()
33
37
  material.map = newMap
34
38
  material.needsUpdate = true
39
+
40
+ const mesh = new THREE.Mesh(box, material)
41
+
35
42
  const group = new THREE.Group()
36
43
  group.add(mesh)
37
- group.scale.set(0.1, 0.1, 0.1)
38
- mesh.rotation.z = Math.PI
39
44
  return group
40
45
  }
41
46
 
42
- function setUVs (
43
- box: THREE.BoxGeometry,
44
- u: number,
45
- v: number,
46
- width: number,
47
- height: number,
48
- depth: number,
49
- textureWidth: number,
50
- textureHeight: number
51
- ): void {
52
- const toFaceVertices = (x1: number, y1: number, x2: number, y2: number) => [
53
- new THREE.Vector2(x1 / textureWidth, 1 - y2 / textureHeight),
54
- new THREE.Vector2(x2 / textureWidth, 1 - y2 / textureHeight),
55
- new THREE.Vector2(x2 / textureWidth, 1 - y1 / textureHeight),
56
- new THREE.Vector2(x1 / textureWidth, 1 - y1 / textureHeight),
47
+ /**
48
+ * Creates a BufferGeometry replicating vanilla Minecraft's ModelPart.Cube exactly.
49
+ * Vertices, face winding, normals, and UV mapping match the decompiled Java source.
50
+ * Position coordinates are in pixels, divided by 16 for block units.
51
+ */
52
+ function createVanillaCubeGeometry (
53
+ texU: number, texV: number,
54
+ originX: number, originY: number, originZ: number,
55
+ sizeX: number, sizeY: number, sizeZ: number,
56
+ texWidth: number, texHeight: number,
57
+ mirror = false
58
+ ): THREE.BufferGeometry {
59
+ let minX = originX / 16
60
+ let minY = originY / 16
61
+ let minZ = originZ / 16
62
+ let maxX = (originX + sizeX) / 16
63
+ let maxY = (originY + sizeY) / 16
64
+ let maxZ = (originZ + sizeZ) / 16
65
+
66
+ if (mirror) {
67
+ [minX, maxX] = [maxX, minX]
68
+ }
69
+
70
+ // 8 corner vertices matching vanilla's Cube constructor
71
+ const V = [
72
+ [minX, minY, minZ], // 0
73
+ [maxX, minY, minZ], // 1
74
+ [maxX, maxY, minZ], // 2
75
+ [minX, maxY, minZ], // 3
76
+ [minX, minY, maxZ], // 4
77
+ [maxX, minY, maxZ], // 5
78
+ [maxX, maxY, maxZ], // 6
79
+ [minX, maxY, maxZ], // 7
57
80
  ]
58
81
 
59
- const top = toFaceVertices(u + depth, v, u + width + depth, v + depth)
60
- const bottom = toFaceVertices(u + width + depth, v, u + width * 2 + depth, v + depth)
61
- const left = toFaceVertices(u, v + depth, u + depth, v + depth + height)
62
- const front = toFaceVertices(u + depth, v + depth, u + width + depth, v + depth + height)
63
- const right = toFaceVertices(u + width + depth, v + depth, u + width + depth * 2, v + height + depth)
64
- const back = toFaceVertices(u + width + depth * 2, v + depth, u + width * 2 + depth * 2, v + height + depth)
65
-
66
- const uvAttr = box.attributes.uv as THREE.BufferAttribute
67
- const uvRight = [right[3], right[2], right[0], right[1]]
68
- const uvLeft = [left[3], left[2], left[0], left[1]]
69
- const uvTop = [top[3], top[2], top[0], top[1]]
70
- const uvBottom = [bottom[0], bottom[1], bottom[3], bottom[2]]
71
- const uvFront = [front[3], front[2], front[0], front[1]]
72
- const uvBack = [back[3], back[2], back[0], back[1]]
73
-
74
- // Create a new array to hold the modified UV data
75
- const newUVData = [] as number[]
76
-
77
- // Iterate over the arrays and copy the data to uvData
78
- for (const uvArray of [uvRight, uvLeft, uvTop, uvBottom, uvFront, uvBack]) {
79
- for (const uv of uvArray) {
80
- newUVData.push(uv.x, uv.y)
82
+ // UV grid (pixel coords)
83
+ const u0 = texU
84
+ const u1 = texU + sizeZ
85
+ const u2 = texU + sizeZ + sizeX
86
+ const u3 = texU + sizeZ + sizeX + sizeX
87
+ const u4 = texU + sizeZ + sizeX + sizeZ
88
+ const u5 = texU + sizeZ + sizeX + sizeZ + sizeX
89
+ const v0 = texV
90
+ const v1 = texV + sizeZ
91
+ const v2 = texV + sizeZ + sizeY
92
+
93
+ // 6 faces: vanilla vertex order + UV rect + normal
94
+ const faces: { vi: number[]; uv: number[]; n: number[] }[] = [
95
+ { vi: [5, 4, 0, 1], uv: [u1, v0, u2, v1], n: [0, -1, 0] }, // DOWN
96
+ { vi: [2, 3, 7, 6], uv: [u2, v1, u3, v0], n: [0, 1, 0] }, // UP
97
+ { vi: [0, 4, 7, 3], uv: [u0, v1, u1, v2], n: [-1, 0, 0] }, // WEST
98
+ { vi: [1, 0, 3, 2], uv: [u1, v1, u2, v2], n: [0, 0, -1] }, // NORTH
99
+ { vi: [5, 1, 2, 6], uv: [u2, v1, u4, v2], n: [1, 0, 0] }, // EAST
100
+ { vi: [4, 5, 6, 7], uv: [u4, v1, u5, v2], n: [0, 0, 1] }, // SOUTH
101
+ ]
102
+
103
+ const positions: number[] = []
104
+ const uvs: number[] = []
105
+ const normals: number[] = []
106
+ const indices: number[] = []
107
+
108
+ for (let fi = 0; fi < faces.length; fi++) {
109
+ const face = faces[fi]
110
+ const base = fi * 4
111
+
112
+ const [uL, vT, uR, vB] = face.uv
113
+ // Vanilla vertex UV order: top-right, top-left, bottom-left, bottom-right
114
+ const fUV = [
115
+ [uR / texWidth, 1 - vT / texHeight],
116
+ [uL / texWidth, 1 - vT / texHeight],
117
+ [uL / texWidth, 1 - vB / texHeight],
118
+ [uR / texWidth, 1 - vB / texHeight],
119
+ ]
120
+
121
+ const order = mirror ? [3, 2, 1, 0] : [0, 1, 2, 3]
122
+ const nx = mirror ? -face.n[0] : face.n[0]
123
+
124
+ for (let i = 0; i < 4; i++) {
125
+ const vert = V[face.vi[order[i]]]
126
+ positions.push(vert[0], vert[1], vert[2])
127
+ uvs.push(fUV[i][0], fUV[i][1])
128
+ normals.push(nx, face.n[1], face.n[2])
81
129
  }
130
+
131
+ indices.push(base, base + 1, base + 2, base, base + 2, base + 3)
82
132
  }
83
133
 
84
- uvAttr.set(new Float32Array(newUVData))
85
- uvAttr.needsUpdate = true
86
- }
134
+ const geometry = new THREE.BufferGeometry()
135
+ geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3))
136
+ geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2))
137
+ geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3))
138
+ geometry.setIndex(indices)
87
139
 
88
- function setSkinUVs (box: THREE.BoxGeometry, u: number, v: number, width: number, height: number, depth: number): void {
89
- setUVs(box, u, v, width, height, depth, 64, 64)
140
+ return geometry
90
141
  }
@@ -0,0 +1,90 @@
1
+ //@ts-nocheck
2
+ import * as THREE from 'three'
3
+ import { loadSkinFromUsername, loadSkinImage } from '../lib/utils/skins'
4
+ import { steveTexture } from './entities'
5
+
6
+
7
+ export const getMyHand = async (image?: string, userName?: string) => {
8
+ let newMap: THREE.Texture
9
+ if (!image && !userName) {
10
+ newMap = await steveTexture
11
+ } else {
12
+ if (!image) {
13
+ image = await loadSkinFromUsername(userName!, 'skin')
14
+ }
15
+ if (!image) {
16
+ return
17
+ }
18
+ const { canvas } = await loadSkinImage(image)
19
+ newMap = new THREE.CanvasTexture(canvas)
20
+ }
21
+
22
+ newMap.magFilter = THREE.NearestFilter
23
+ newMap.minFilter = THREE.NearestFilter
24
+ // right arm
25
+ const box = new THREE.BoxGeometry()
26
+ const material = new THREE.MeshStandardMaterial()
27
+ const slim = false
28
+ const mesh = new THREE.Mesh(box, material)
29
+ mesh.scale.x = slim ? 3 : 4
30
+ mesh.scale.y = 12
31
+ mesh.scale.z = 4
32
+ setSkinUVs(box, 40, 16, slim ? 3 : 4, 12, 4)
33
+ material.map = newMap
34
+ material.needsUpdate = true
35
+ const group = new THREE.Group()
36
+ group.add(mesh)
37
+ group.scale.set(0.1, 0.1, 0.1)
38
+ mesh.rotation.z = Math.PI
39
+ return group
40
+ }
41
+
42
+ function setUVs (
43
+ box: THREE.BoxGeometry,
44
+ u: number,
45
+ v: number,
46
+ width: number,
47
+ height: number,
48
+ depth: number,
49
+ textureWidth: number,
50
+ textureHeight: number
51
+ ): void {
52
+ const toFaceVertices = (x1: number, y1: number, x2: number, y2: number) => [
53
+ new THREE.Vector2(x1 / textureWidth, 1 - y2 / textureHeight),
54
+ new THREE.Vector2(x2 / textureWidth, 1 - y2 / textureHeight),
55
+ new THREE.Vector2(x2 / textureWidth, 1 - y1 / textureHeight),
56
+ new THREE.Vector2(x1 / textureWidth, 1 - y1 / textureHeight),
57
+ ]
58
+
59
+ const top = toFaceVertices(u + depth, v, u + width + depth, v + depth)
60
+ const bottom = toFaceVertices(u + width + depth, v, u + width * 2 + depth, v + depth)
61
+ const left = toFaceVertices(u, v + depth, u + depth, v + depth + height)
62
+ const front = toFaceVertices(u + depth, v + depth, u + width + depth, v + depth + height)
63
+ const right = toFaceVertices(u + width + depth, v + depth, u + width + depth * 2, v + height + depth)
64
+ const back = toFaceVertices(u + width + depth * 2, v + depth, u + width * 2 + depth * 2, v + height + depth)
65
+
66
+ const uvAttr = box.attributes.uv as THREE.BufferAttribute
67
+ const uvRight = [right[3], right[2], right[0], right[1]]
68
+ const uvLeft = [left[3], left[2], left[0], left[1]]
69
+ const uvTop = [top[3], top[2], top[0], top[1]]
70
+ const uvBottom = [bottom[0], bottom[1], bottom[3], bottom[2]]
71
+ const uvFront = [front[3], front[2], front[0], front[1]]
72
+ const uvBack = [back[3], back[2], back[0], back[1]]
73
+
74
+ // Create a new array to hold the modified UV data
75
+ const newUVData = [] as number[]
76
+
77
+ // Iterate over the arrays and copy the data to uvData
78
+ for (const uvArray of [uvRight, uvLeft, uvTop, uvBottom, uvFront, uvBack]) {
79
+ for (const uv of uvArray) {
80
+ newUVData.push(uv.x, uv.y)
81
+ }
82
+ }
83
+
84
+ uvAttr.set(new Float32Array(newUVData))
85
+ uvAttr.needsUpdate = true
86
+ }
87
+
88
+ function setSkinUVs (box: THREE.BoxGeometry, u: number, v: number, width: number, height: number, depth: number): void {
89
+ setUVs(box, u, v, width, height, depth, 64, 64)
90
+ }