minecraft-renderer 0.1.97 → 0.1.98

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.97",
3
+ "version": "0.1.98",
4
4
  "description": "The most Modular Minecraft world renderer with Three.js WebGL backend",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -145,11 +145,11 @@ function shortestYawRadians(fromYawRad: number, toYawRad: number): number {
145
145
  return norm > Math.PI ? norm - TAU_YAW : norm
146
146
  }
147
147
 
148
- /** Voice chat presence for a player, drawn as a small mic glyph before their nametag. */
148
+ /** Voice chat presence for a player, drawn as a small speaker glyph before their nametag. */
149
149
  export type PlayerVoiceState = 'speaking' | 'muted' | 'idle'
150
150
 
151
151
  /**
152
- * Draws a small mic glyph centered at the origin of the current transform.
152
+ * Draws a small speaker glyph centered at the origin of the current transform.
153
153
  * `size` is the glyph's bounding box (both width and height).
154
154
  */
155
155
  function drawVoiceStateIcon(ctx: OffscreenCanvasRenderingContext2D, state: PlayerVoiceState, cx: number, cy: number, size: number) {
@@ -157,46 +157,42 @@ function drawVoiceStateIcon(ctx: OffscreenCanvasRenderingContext2D, state: Playe
157
157
 
158
158
  ctx.save()
159
159
  ctx.translate(cx, cy)
160
-
161
- if (state === 'speaking') {
162
- ctx.beginPath()
163
- ctx.arc(0, 0, size * 0.62, 0, Math.PI * 2)
164
- ctx.strokeStyle = 'rgba(61, 220, 71, 0.55)'
165
- ctx.lineWidth = Math.max(1, size * 0.08)
166
- ctx.stroke()
167
- }
168
-
169
160
  ctx.fillStyle = color
170
161
  ctx.strokeStyle = color
171
- ctx.lineWidth = Math.max(1, size * 0.12)
162
+ ctx.lineWidth = Math.max(1.5, size * 0.09)
163
+ ctx.lineCap = 'round'
164
+
165
+ // Speaker silhouette: a small box with a cone opening to the right
166
+ const boxWidth = size * 0.22
167
+ const boxHeight = size * 0.34
168
+ const coneWidth = size * 0.24
169
+ const left = -size * 0.44
172
170
 
173
- // Mic body: a rounded capsule
174
- const bodyWidth = size * 0.34
175
- const bodyHeight = size * 0.55
176
- const bodyRadius = bodyWidth / 2
177
171
  ctx.beginPath()
178
- ctx.moveTo(-bodyRadius, -bodyHeight / 2 + bodyRadius)
179
- ctx.arcTo(-bodyRadius, -bodyHeight / 2, 0, -bodyHeight / 2, bodyRadius)
180
- ctx.arcTo(bodyRadius, -bodyHeight / 2, bodyRadius, -bodyHeight / 2 + bodyRadius, bodyRadius)
181
- ctx.lineTo(bodyRadius, bodyHeight / 2 - bodyRadius)
182
- ctx.arcTo(bodyRadius, bodyHeight / 2, 0, bodyHeight / 2, bodyRadius)
183
- ctx.arcTo(-bodyRadius, bodyHeight / 2, -bodyRadius, bodyHeight / 2 - bodyRadius, bodyRadius)
172
+ ctx.moveTo(left, -boxHeight / 2)
173
+ ctx.lineTo(left + boxWidth, -boxHeight / 2)
174
+ ctx.lineTo(left + boxWidth + coneWidth, -size * 0.5)
175
+ ctx.lineTo(left + boxWidth + coneWidth, size * 0.5)
176
+ ctx.lineTo(left + boxWidth, boxHeight / 2)
177
+ ctx.lineTo(left, boxHeight / 2)
184
178
  ctx.closePath()
185
179
  ctx.fill()
186
180
 
187
- // Stand: arc + base line
188
- ctx.beginPath()
189
- ctx.arc(0, bodyHeight / 2 - bodyRadius / 2, size * 0.32, Math.PI * 0.15, Math.PI * 0.85, false)
190
- ctx.stroke()
191
- ctx.beginPath()
192
- ctx.moveTo(0, size * 0.5)
193
- ctx.lineTo(0, size * 0.62)
194
- ctx.stroke()
181
+ const coneTip = left + boxWidth + coneWidth
182
+
183
+ if (state === 'speaking') {
184
+ ctx.beginPath()
185
+ ctx.arc(coneTip + size * 0.06, 0, size * 0.16, -Math.PI * 0.32, Math.PI * 0.32)
186
+ ctx.stroke()
187
+ ctx.beginPath()
188
+ ctx.arc(coneTip + size * 0.06, 0, size * 0.28, -Math.PI * 0.28, Math.PI * 0.28)
189
+ ctx.stroke()
190
+ }
195
191
 
196
192
  if (state === 'muted') {
197
193
  ctx.beginPath()
198
- ctx.moveTo(-size * 0.42, -size * 0.42)
199
- ctx.lineTo(size * 0.42, size * 0.42)
194
+ ctx.moveTo(-size * 0.46, -size * 0.46)
195
+ ctx.lineTo(size * 0.46, size * 0.46)
200
196
  ctx.stroke()
201
197
  }
202
198
 
@@ -227,25 +223,22 @@ function getUsernameTexture(
227
223
 
228
224
  const height = (fontSize + padding) * plainLines.length
229
225
 
230
- // Render the text on its own canvas first so the existing centered-text
231
- // layout in renderComponent() is unaffected by the icon we add alongside it.
226
+ // Render the text onto its own transparent canvas so the existing
227
+ // centered-text layout in renderComponent() is unaffected by compositing it
228
+ // alongside the icon. The background plate is painted exactly once, on the
229
+ // final canvas below — painting it here too would double-blend the alpha
230
+ // under the text relative to the icon area, producing a visible seam.
232
231
  const textCanvas = createCanvas(textWidth, height)
233
232
  const textCtx = textCanvas.getContext('2d')
234
233
  if (!textCtx) throw new Error('Could not get 2d context')
235
234
 
236
- textCtx.fillStyle = nameTagBackgroundColor
237
- textCtx.fillRect(0, 0, textCanvas.width, textCanvas.height)
238
235
  textCtx.globalAlpha = nameTagTextOpacity / 255
239
-
240
236
  const textRendered = renderComponent(username, PrismarineChat, textCanvas, fontSize, 'white', -padding + fontSize)
241
237
  if (!textRendered) return undefined
242
-
243
238
  textCtx.globalAlpha = 1
244
239
 
245
- if (!voiceState) return textCanvas
246
-
247
- const iconSize = fontSize * 0.55
248
- const iconAreaWidth = iconSize + padding * 2
240
+ const iconSize = fontSize * 0.85
241
+ const iconAreaWidth = voiceState ? iconSize + padding * 2.5 : 0
249
242
 
250
243
  const canvas = createCanvas(iconAreaWidth + textWidth, height)
251
244
  const ctx = canvas.getContext('2d')
@@ -253,7 +246,9 @@ function getUsernameTexture(
253
246
 
254
247
  ctx.fillStyle = nameTagBackgroundColor
255
248
  ctx.fillRect(0, 0, canvas.width, canvas.height)
256
- drawVoiceStateIcon(ctx, voiceState, iconAreaWidth / 2, canvas.height / 2, iconSize)
249
+ if (voiceState) {
250
+ drawVoiceStateIcon(ctx, voiceState, iconAreaWidth / 2, canvas.height / 2, iconSize)
251
+ }
257
252
  ctx.drawImage(textCanvas, iconAreaWidth, 0)
258
253
 
259
254
  return canvas