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
package/src/three/entities.ts
CHANGED
|
@@ -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
|
|
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
|
|
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.
|
|
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(
|
|
179
|
-
ctx.
|
|
180
|
-
ctx.
|
|
181
|
-
ctx.lineTo(
|
|
182
|
-
ctx.
|
|
183
|
-
ctx.
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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.
|
|
199
|
-
ctx.lineTo(size * 0.
|
|
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
|
|
231
|
-
// layout in renderComponent() is unaffected by
|
|
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
|
-
|
|
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
|
-
|
|
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
|