Skip to content

Commit 60fc5ef

Browse files
committed
feat: add skybox renderer: test it by dragging an image window into window, fix waypoint block pos
1 parent 8827aab commit 60fc5ef

File tree

5 files changed

+124
-1
lines changed

5 files changed

+124
-1
lines changed

renderer/viewer/three/graphicsBackend.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ const getBackendMethods = (worldRenderer: WorldRendererThree) => {
4747

4848
addWaypoint: worldRenderer.waypoints.addWaypoint.bind(worldRenderer.waypoints),
4949
removeWaypoint: worldRenderer.waypoints.removeWaypoint.bind(worldRenderer.waypoints),
50+
51+
// New method for updating skybox
52+
setSkyboxImage: worldRenderer.skyboxRenderer.setSkyboxImage.bind(worldRenderer.skyboxRenderer)
5053
}
5154
}
5255

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import * as THREE from 'three'
2+
3+
export class SkyboxRenderer {
4+
private texture: THREE.Texture | null = null
5+
private mesh: THREE.Mesh<THREE.SphereGeometry, THREE.MeshBasicMaterial> | null = null
6+
7+
constructor (private readonly scene: THREE.Scene, public initialImage: string | null) {}
8+
9+
async init () {
10+
if (this.initialImage) {
11+
await this.setSkyboxImage(this.initialImage)
12+
}
13+
}
14+
15+
async setSkyboxImage (imageUrl: string) {
16+
// Dispose old textures if they exist
17+
if (this.texture) {
18+
this.texture.dispose()
19+
}
20+
21+
// Load the equirectangular texture
22+
const textureLoader = new THREE.TextureLoader()
23+
this.texture = await new Promise((resolve) => {
24+
textureLoader.load(
25+
imageUrl,
26+
(texture) => {
27+
texture.mapping = THREE.EquirectangularReflectionMapping
28+
texture.encoding = THREE.sRGBEncoding
29+
// Keep pixelated look
30+
texture.minFilter = THREE.NearestFilter
31+
texture.magFilter = THREE.NearestFilter
32+
texture.needsUpdate = true
33+
resolve(texture)
34+
}
35+
)
36+
})
37+
38+
// Create or update the skybox
39+
if (this.mesh) {
40+
// Just update the texture on the existing material
41+
this.mesh.material.map = this.texture
42+
this.mesh.material.needsUpdate = true
43+
} else {
44+
// Create a large sphere geometry for the skybox
45+
const geometry = new THREE.SphereGeometry(500, 60, 40)
46+
// Flip the geometry inside out
47+
geometry.scale(-1, 1, 1)
48+
49+
// Create material using the loaded texture
50+
const material = new THREE.MeshBasicMaterial({
51+
map: this.texture,
52+
side: THREE.FrontSide // Changed to FrontSide since we're flipping the geometry
53+
})
54+
55+
// Create and add the skybox mesh
56+
this.mesh = new THREE.Mesh(geometry, material)
57+
this.scene.add(this.mesh)
58+
}
59+
}
60+
61+
update (cameraPosition: THREE.Vector3) {
62+
if (this.mesh) {
63+
this.mesh.position.copy(cameraPosition)
64+
}
65+
}
66+
67+
dispose () {
68+
if (this.texture) {
69+
this.texture.dispose()
70+
}
71+
if (this.mesh) {
72+
this.mesh.geometry.dispose()
73+
;(this.mesh.material as THREE.Material).dispose()
74+
this.scene.remove(this.mesh)
75+
}
76+
}
77+
}

renderer/viewer/three/waypoints.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export class WaypointsRenderer {
8585
this.waypointScene.add(sprite.group)
8686

8787
this.waypoints.set(id, {
88-
id, x, y, z, minDistance,
88+
id, x: x + 0.5, y: y + 0.5, z: z + 0.5, minDistance,
8989
color, label,
9090
sprite,
9191
})

renderer/viewer/three/worldrendererThree.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { CameraShake } from './cameraShake'
2424
import { ThreeJsMedia } from './threeJsMedia'
2525
import { Fountain } from './threeJsParticles'
2626
import { WaypointsRenderer } from './waypoints'
27+
import { SkyboxRenderer } from './skyboxRenderer'
2728

2829
type SectionKey = string
2930

@@ -71,6 +72,7 @@ export class WorldRendererThree extends WorldRendererCommon {
7172
}
7273
fountains: Fountain[] = []
7374
DEBUG_RAYCAST = false
75+
skyboxRenderer: SkyboxRenderer
7476

7577
private currentPosTween?: tweenJs.Tween<THREE.Vector3>
7678
private currentRotTween?: tweenJs.Tween<{ pitch: number, yaw: number }>
@@ -94,6 +96,10 @@ export class WorldRendererThree extends WorldRendererCommon {
9496
this.holdingBlock = new HoldingBlock(this)
9597
this.holdingBlockLeft = new HoldingBlock(this, true)
9698

99+
// Initialize skybox renderer
100+
this.skyboxRenderer = new SkyboxRenderer(this.scene, null)
101+
void this.skyboxRenderer.init()
102+
97103
this.addDebugOverlay()
98104
this.resetScene()
99105
void this.init()
@@ -708,6 +714,10 @@ export class WorldRendererThree extends WorldRendererCommon {
708714
this.cursorBlock.render()
709715
this.updateSectionOffsets()
710716

717+
// Update skybox position to follow camera
718+
const cameraPos = this.getCameraPosition()
719+
this.skyboxRenderer.update(cameraPos)
720+
711721
const sizeOrFovChanged = sizeChanged || this.displayOptions.inWorldRenderingConfig.fov !== this.camera.fov
712722
if (sizeOrFovChanged) {
713723
const size = this.renderer.getSize(new THREE.Vector2())
@@ -947,6 +957,7 @@ export class WorldRendererThree extends WorldRendererCommon {
947957

948958
destroy (): void {
949959
super.destroy()
960+
this.skyboxRenderer.dispose()
950961
}
951962

952963
shouldObjectVisible (object: THREE.Object3D) {

src/dragndrop.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import fs from 'fs'
33
import * as nbt from 'prismarine-nbt'
44
import RegionFile from 'prismarine-provider-anvil/src/region'
55
import { versions } from 'minecraft-data'
6+
import { getThreeJsRendererMethods } from 'renderer/viewer/three/threeJsMethods'
67
import { openWorldDirectory, openWorldZip } from './browserfs'
78
import { isGameActive } from './globalState'
89
import { showNotification } from './react/NotificationProvider'
@@ -12,6 +13,9 @@ const parseNbt = promisify(nbt.parse)
1213
const simplifyNbt = nbt.simplify
1314
window.nbt = nbt
1415

16+
// Supported image types for skybox
17+
const VALID_IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.webp']
18+
1519
// todo display drop zone
1620
for (const event of ['drag', 'dragstart', 'dragend', 'dragover', 'dragenter', 'dragleave', 'drop']) {
1721
window.addEventListener(event, (e: any) => {
@@ -45,6 +49,34 @@ window.addEventListener('drop', async e => {
4549
})
4650

4751
async function handleDroppedFile (file: File) {
52+
// Check for image files first when game is active
53+
if (isGameActive(false) && VALID_IMAGE_EXTENSIONS.some(ext => file.name.toLowerCase().endsWith(ext))) {
54+
try {
55+
// Convert image to base64
56+
const reader = new FileReader()
57+
const base64Promise = new Promise<string>((resolve, reject) => {
58+
reader.onload = () => resolve(reader.result as string)
59+
reader.onerror = reject
60+
})
61+
reader.readAsDataURL(file)
62+
const base64Image = await base64Promise
63+
64+
// Get ThreeJS backend methods and update skybox
65+
const setSkyboxImage = getThreeJsRendererMethods()?.setSkyboxImage
66+
if (setSkyboxImage) {
67+
await setSkyboxImage(base64Image)
68+
showNotification('Skybox updated successfully')
69+
} else {
70+
showNotification('Cannot update skybox - renderer does not support it')
71+
}
72+
return
73+
} catch (err) {
74+
console.error('Failed to update skybox:', err)
75+
showNotification('Failed to update skybox', 'error')
76+
return
77+
}
78+
}
79+
4880
if (file.name.endsWith('.zip')) {
4981
void openWorldZip(file)
5082
return

0 commit comments

Comments
 (0)