feat: make arrows colorful and metadata (#430)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
parent
636a7fdb54
commit
3b94889bed
3 changed files with 49 additions and 8 deletions
|
|
@ -16,7 +16,7 @@ export const WAYPOINT_CONFIG = {
|
||||||
CANVAS_SCALE: 2,
|
CANVAS_SCALE: 2,
|
||||||
ARROW: {
|
ARROW: {
|
||||||
enabledDefault: false,
|
enabledDefault: false,
|
||||||
pixelSize: 30,
|
pixelSize: 50,
|
||||||
paddingPx: 50,
|
paddingPx: 50,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -50,6 +50,7 @@ export function createWaypointSprite (options: {
|
||||||
depthTest?: boolean,
|
depthTest?: boolean,
|
||||||
// Y offset in world units used by updateScaleWorld only (screen-pixel API ignores this)
|
// Y offset in world units used by updateScaleWorld only (screen-pixel API ignores this)
|
||||||
labelYOffset?: number,
|
labelYOffset?: number,
|
||||||
|
metadata?: any,
|
||||||
}): WaypointSprite {
|
}): WaypointSprite {
|
||||||
const color = options.color ?? 0xFF_00_00
|
const color = options.color ?? 0xFF_00_00
|
||||||
const depthTest = options.depthTest ?? false
|
const depthTest = options.depthTest ?? false
|
||||||
|
|
@ -131,16 +132,22 @@ export function createWaypointSprite (options: {
|
||||||
canvas.height = size
|
canvas.height = size
|
||||||
const ctx = canvas.getContext('2d')!
|
const ctx = canvas.getContext('2d')!
|
||||||
ctx.clearRect(0, 0, size, size)
|
ctx.clearRect(0, 0, size, size)
|
||||||
|
|
||||||
|
// Draw arrow shape
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
ctx.moveTo(size * 0.2, size * 0.5)
|
ctx.moveTo(size * 0.15, size * 0.5)
|
||||||
ctx.lineTo(size * 0.8, size * 0.5)
|
ctx.lineTo(size * 0.85, size * 0.5)
|
||||||
ctx.lineTo(size * 0.5, size * 0.2)
|
ctx.lineTo(size * 0.5, size * 0.15)
|
||||||
ctx.closePath()
|
ctx.closePath()
|
||||||
ctx.lineWidth = 4
|
|
||||||
|
// Use waypoint color for arrow
|
||||||
|
const colorHex = `#${color.toString(16).padStart(6, '0')}`
|
||||||
|
ctx.lineWidth = 6
|
||||||
ctx.strokeStyle = 'black'
|
ctx.strokeStyle = 'black'
|
||||||
ctx.stroke()
|
ctx.stroke()
|
||||||
ctx.fillStyle = 'white'
|
ctx.fillStyle = colorHex
|
||||||
ctx.fill()
|
ctx.fill()
|
||||||
|
|
||||||
const texture = new THREE.CanvasTexture(canvas)
|
const texture = new THREE.CanvasTexture(canvas)
|
||||||
const material = new THREE.SpriteMaterial({ map: texture, transparent: true, depthTest: false, depthWrite: false })
|
const material = new THREE.SpriteMaterial({ map: texture, transparent: true, depthTest: false, depthWrite: false })
|
||||||
arrowSprite = new THREE.Sprite(material)
|
arrowSprite = new THREE.Sprite(material)
|
||||||
|
|
@ -169,6 +176,9 @@ export function createWaypointSprite (options: {
|
||||||
ensureArrow()
|
ensureArrow()
|
||||||
if (!arrowSprite) return true
|
if (!arrowSprite) return true
|
||||||
|
|
||||||
|
// Check if onlyLeftRight is enabled in metadata
|
||||||
|
const onlyLeftRight = options.metadata?.onlyLeftRight === true
|
||||||
|
|
||||||
// Build camera basis using camera.up to respect custom orientations
|
// Build camera basis using camera.up to respect custom orientations
|
||||||
const forward = new THREE.Vector3()
|
const forward = new THREE.Vector3()
|
||||||
camera.getWorldDirection(forward) // camera look direction
|
camera.getWorldDirection(forward) // camera look direction
|
||||||
|
|
@ -213,6 +223,20 @@ export function createWaypointSprite (options: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply onlyLeftRight logic - restrict arrows to left/right edges only
|
||||||
|
if (onlyLeftRight) {
|
||||||
|
// Force the arrow to appear only on left or right edges
|
||||||
|
if (Math.abs(rx) > Math.abs(ry)) {
|
||||||
|
// Horizontal direction is dominant, keep it
|
||||||
|
ry = 0
|
||||||
|
} else {
|
||||||
|
// Vertical direction is dominant, but we want only left/right
|
||||||
|
// So choose left or right based on the sign of rx
|
||||||
|
rx = rx >= 0 ? 1 : -1
|
||||||
|
ry = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Place on the rectangle border [-1,1]x[-1,1]
|
// Place on the rectangle border [-1,1]x[-1,1]
|
||||||
const s = Math.max(Math.abs(rx), Math.abs(ry)) || 1
|
const s = Math.max(Math.abs(rx), Math.abs(ry)) || 1
|
||||||
let ndcX = rx / s
|
let ndcX = rx / s
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ interface WaypointOptions {
|
||||||
color?: number
|
color?: number
|
||||||
label?: string
|
label?: string
|
||||||
minDistance?: number
|
minDistance?: number
|
||||||
|
metadata?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WaypointsRenderer {
|
export class WaypointsRenderer {
|
||||||
|
|
@ -71,13 +72,14 @@ export class WaypointsRenderer {
|
||||||
this.removeWaypoint(id)
|
this.removeWaypoint(id)
|
||||||
|
|
||||||
const color = options.color ?? 0xFF_00_00
|
const color = options.color ?? 0xFF_00_00
|
||||||
const { label } = options
|
const { label, metadata } = options
|
||||||
const minDistance = options.minDistance ?? 0
|
const minDistance = options.minDistance ?? 0
|
||||||
|
|
||||||
const sprite = createWaypointSprite({
|
const sprite = createWaypointSprite({
|
||||||
position: new THREE.Vector3(x, y, z),
|
position: new THREE.Vector3(x, y, z),
|
||||||
color,
|
color,
|
||||||
label: (label || id),
|
label: (label || id),
|
||||||
|
metadata,
|
||||||
})
|
})
|
||||||
sprite.enableOffscreenArrow(true)
|
sprite.enableOffscreenArrow(true)
|
||||||
sprite.setArrowParent(this.waypointScene)
|
sprite.setArrowParent(this.waypointScene)
|
||||||
|
|
|
||||||
|
|
@ -82,15 +82,30 @@ const registerWaypointChannels = () => {
|
||||||
{
|
{
|
||||||
name: 'color',
|
name: 'color',
|
||||||
type: 'i32'
|
type: 'i32'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'metadataJson',
|
||||||
|
type: ['pstring', { countType: 'i16' }]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
registerChannel('minecraft-web-client:waypoint-add', packetStructure, (data) => {
|
registerChannel('minecraft-web-client:waypoint-add', packetStructure, (data) => {
|
||||||
|
// Parse metadata if provided
|
||||||
|
let metadata: any = {}
|
||||||
|
if (data.metadataJson && data.metadataJson.trim() !== '') {
|
||||||
|
try {
|
||||||
|
metadata = JSON.parse(data.metadataJson)
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to parse waypoint metadataJson:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getThreeJsRendererMethods()?.addWaypoint(data.id, data.x, data.y, data.z, {
|
getThreeJsRendererMethods()?.addWaypoint(data.id, data.x, data.y, data.z, {
|
||||||
minDistance: data.minDistance,
|
minDistance: data.minDistance,
|
||||||
label: data.label || undefined,
|
label: data.label || undefined,
|
||||||
color: data.color || undefined
|
color: data.color || undefined,
|
||||||
|
metadata
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue