Skip to content

Commit 62820bc

Browse files
authored
Waypoint visual scaling factor (#492)
1 parent 5ca730e commit 62820bc

2 files changed

Lines changed: 55 additions & 21 deletions

File tree

renderer/viewer/three/waypointSprite.ts

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ export const WAYPOINT_CONFIG = {
1919
pixelSize: 50,
2020
paddingPx: 50,
2121
},
22+
// Default visual scale factor (can be overridden globally or per-waypoint)
23+
DEFAULT_VISUAL_SCALE: 1,
24+
// Default opacity (can be overridden globally or per-waypoint)
25+
DEFAULT_OPACITY: 1,
2226
}
2327

2428
export type WaypointSprite = {
@@ -51,14 +55,31 @@ export function createWaypointSprite (options: {
5155
// Y offset in world units used by updateScaleWorld only (screen-pixel API ignores this)
5256
labelYOffset?: number,
5357
metadata?: any,
58+
visualScale?: number,
59+
opacity?: number,
5460
}): WaypointSprite {
5561
const color = options.color ?? 0xFF_00_00
5662
const depthTest = options.depthTest ?? false
5763
const labelYOffset = options.labelYOffset ?? 1.5
5864

65+
// Get visual scale from options, metadata, server metadata, or default
66+
// Priority: options.visualScale > metadata.visualScale > window.serverMetadata?.waypointVisualScale > DEFAULT
67+
const visualScale = options.visualScale
68+
?? options.metadata?.visualScale
69+
?? (typeof window === 'undefined' ? undefined : (window as any).serverMetadata?.waypointVisualScale)
70+
?? WAYPOINT_CONFIG.DEFAULT_VISUAL_SCALE
71+
72+
// Get opacity from options, metadata, server metadata, or default
73+
// Priority: options.opacity > metadata.opacity > window.serverMetadata?.waypointOpacity > DEFAULT
74+
const opacity = options.opacity
75+
?? options.metadata?.opacity
76+
?? (typeof window === 'undefined' ? undefined : (window as any).serverMetadata?.waypointOpacity)
77+
?? WAYPOINT_CONFIG.DEFAULT_OPACITY
78+
5979
// Build combined sprite
60-
const sprite = createCombinedSprite(color, options.label ?? '', '0m', depthTest)
80+
const sprite = createCombinedSprite(color, options.label ?? '', '0m', depthTest, visualScale)
6181
sprite.renderOrder = 10
82+
sprite.material.opacity = opacity
6283
let currentLabel = options.label ?? ''
6384

6485
// Performance optimization: cache distance text to avoid unnecessary updates
@@ -79,7 +100,7 @@ export function createWaypointSprite (options: {
79100
group.position.set(x, y, z)
80101

81102
function setColor (newColor: number) {
82-
const canvas = drawCombinedCanvas(newColor, currentLabel, '0m')
103+
const canvas = drawCombinedCanvas(newColor, currentLabel, '0m', visualScale)
83104
const texture = new THREE.CanvasTexture(canvas)
84105
const mat = sprite.material
85106
mat.map?.dispose()
@@ -89,7 +110,7 @@ export function createWaypointSprite (options: {
89110

90111
function setLabel (newLabel?: string) {
91112
currentLabel = newLabel ?? ''
92-
const canvas = drawCombinedCanvas(color, currentLabel, '0m')
113+
const canvas = drawCombinedCanvas(color, currentLabel, '0m', visualScale)
93114
const texture = new THREE.CanvasTexture(canvas)
94115
const mat = sprite.material
95116
mat.map?.dispose()
@@ -104,7 +125,7 @@ export function createWaypointSprite (options: {
104125
}
105126
lastDistanceText = distanceText
106127

107-
const canvas = drawCombinedCanvas(color, label, distanceText)
128+
const canvas = drawCombinedCanvas(color, label, distanceText, visualScale)
108129
const texture = new THREE.CanvasTexture(canvas)
109130
const mat = sprite.material
110131
mat.map?.dispose()
@@ -129,8 +150,8 @@ export function createWaypointSprite (options: {
129150
) {
130151
const vFovRad = cameraFov * Math.PI / 180
131152
const worldUnitsPerScreenHeightAtDist = Math.tan(vFovRad / 2) * 2 * distance
132-
// Use configured target screen size
133-
const scale = worldUnitsPerScreenHeightAtDist * (WAYPOINT_CONFIG.TARGET_SCREEN_PX / viewportHeightPx)
153+
// Use configured target screen size with visual scale multiplier
154+
const scale = worldUnitsPerScreenHeightAtDist * (WAYPOINT_CONFIG.TARGET_SCREEN_PX * visualScale / viewportHeightPx)
134155
sprite.scale.set(scale, scale, 1)
135156
}
136157

@@ -159,7 +180,7 @@ export function createWaypointSprite (options: {
159180
ctx.fill()
160181

161182
const texture = new THREE.CanvasTexture(canvas)
162-
const material = new THREE.SpriteMaterial({ map: texture, transparent: true, depthTest: false, depthWrite: false })
183+
const material = new THREE.SpriteMaterial({ map: texture, transparent: true, depthTest: false, depthWrite: false, opacity })
163184
arrowSprite = new THREE.Sprite(material)
164185
arrowSprite.renderOrder = 12
165186
arrowSprite.visible = false
@@ -278,9 +299,9 @@ export function createWaypointSprite (options: {
278299
const angle = Math.atan2(ry, rx)
279300
arrowSprite.material.rotation = angle - Math.PI / 2
280301

281-
// Constant pixel size for arrow (use fixed placement distance)
302+
// Constant pixel size for arrow (use fixed placement distance) with visual scale
282303
const worldUnitsPerScreenHeightAtDist = Math.tan(vFovRad / 2) * 2 * placeDist
283-
const sPx = worldUnitsPerScreenHeightAtDist * (WAYPOINT_CONFIG.ARROW.pixelSize / viewportHeightPx)
304+
const sPx = worldUnitsPerScreenHeightAtDist * (WAYPOINT_CONFIG.ARROW.pixelSize * visualScale / viewportHeightPx)
284305
arrowSprite.scale.set(sPx, sPx, 1)
285306
return false
286307
}
@@ -343,7 +364,7 @@ export function createWaypointSprite (options: {
343364
}
344365

345366
// Internal helpers
346-
function drawCombinedCanvas (color: number, id: string, distance: string): HTMLCanvasElement {
367+
function drawCombinedCanvas (color: number, id: string, distance: string, visualScale = 1): HTMLCanvasElement {
347368
const scale = WAYPOINT_CONFIG.CANVAS_SCALE * (globalThis.devicePixelRatio || 1)
348369
const size = WAYPOINT_CONFIG.CANVAS_SIZE * scale
349370
const canvas = document.createElement('canvas')
@@ -354,11 +375,11 @@ function drawCombinedCanvas (color: number, id: string, distance: string): HTMLC
354375
// Clear canvas
355376
ctx.clearRect(0, 0, size, size)
356377

357-
// Draw dot
378+
// Draw dot with visual scale applied
358379
const centerX = size / 2
359380
const dotY = Math.round(size * WAYPOINT_CONFIG.LAYOUT.DOT_Y)
360-
const radius = Math.round(size * 0.05) // Dot takes up ~12% of canvas height
361-
const borderWidth = Math.max(2, Math.round(4 * scale))
381+
const radius = Math.round(size * 0.05 * visualScale) // Dot takes up ~5% of canvas height, scaled
382+
const borderWidth = Math.max(2, Math.round(4 * scale * visualScale))
362383

363384
// Outer border (black)
364385
ctx.beginPath()
@@ -376,21 +397,21 @@ function drawCombinedCanvas (color: number, id: string, distance: string): HTMLC
376397
ctx.textAlign = 'center'
377398
ctx.textBaseline = 'middle'
378399

379-
// Title
380-
const nameFontPx = Math.round(size * 0.08) // ~8% of canvas height
381-
const distanceFontPx = Math.round(size * 0.06) // ~6% of canvas height
400+
// Title with visual scale applied
401+
const nameFontPx = Math.round(size * 0.08 * visualScale) // ~8% of canvas height, scaled
402+
const distanceFontPx = Math.round(size * 0.06 * visualScale) // ~6% of canvas height, scaled
382403
ctx.font = `bold ${nameFontPx}px mojangles`
383-
ctx.lineWidth = Math.max(2, Math.round(3 * scale))
404+
ctx.lineWidth = Math.max(2, Math.round(3 * scale * visualScale))
384405
const nameY = Math.round(size * WAYPOINT_CONFIG.LAYOUT.NAME_Y)
385406

386407
ctx.strokeStyle = 'black'
387408
ctx.strokeText(id, centerX, nameY)
388409
ctx.fillStyle = 'white'
389410
ctx.fillText(id, centerX, nameY)
390411

391-
// Distance
412+
// Distance with visual scale applied
392413
ctx.font = `bold ${distanceFontPx}px mojangles`
393-
ctx.lineWidth = Math.max(2, Math.round(2 * scale))
414+
ctx.lineWidth = Math.max(2, Math.round(2 * scale * visualScale))
394415
const distanceY = Math.round(size * WAYPOINT_CONFIG.LAYOUT.DISTANCE_Y)
395416

396417
ctx.strokeStyle = 'black'
@@ -401,8 +422,8 @@ function drawCombinedCanvas (color: number, id: string, distance: string): HTMLC
401422
return canvas
402423
}
403424

404-
function createCombinedSprite (color: number, id: string, distance: string, depthTest: boolean): THREE.Sprite {
405-
const canvas = drawCombinedCanvas(color, id, distance)
425+
function createCombinedSprite (color: number, id: string, distance: string, depthTest: boolean, visualScale = 1): THREE.Sprite {
426+
const canvas = drawCombinedCanvas(color, id, distance, visualScale)
406427
const texture = new THREE.CanvasTexture(canvas)
407428
texture.anisotropy = 1
408429
texture.magFilter = THREE.LinearFilter

src/customChannels.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,26 @@ const registerConnectMetadataChannel = () => {
6363
]
6464

6565
bot._client.registerChannel(CHANNEL_NAME, packetStructure, true)
66+
67+
// Send client metadata to server
6668
bot._client.writeChannel(CHANNEL_NAME, {
6769
metadata: JSON.stringify({
6870
version: process.env.RELEASE_TAG,
6971
build: process.env.BUILD_VERSION,
7072
...window.serverMetadataConnect,
7173
})
7274
})
75+
76+
// Listen for server metadata
77+
bot._client.on(CHANNEL_NAME as any, (data) => {
78+
try {
79+
const metadata = JSON.parse(data.metadata)
80+
window.serverMetadata = metadata
81+
console.debug('Received server metadata:', metadata)
82+
} catch (error) {
83+
console.warn('Failed to parse server metadata:', error)
84+
}
85+
})
7386
}
7487

7588
const registerBlockInteractionsCustomizationChannel = () => {

0 commit comments

Comments
 (0)