feat: add skybox renderer: test it by dragging an image window into window, fix waypoint block pos
This commit is contained in:
parent
8827aab981
commit
60fc5ef315
5 changed files with 124 additions and 1 deletions
|
|
@ -47,6 +47,9 @@ const getBackendMethods = (worldRenderer: WorldRendererThree) => {
|
|||
|
||||
addWaypoint: worldRenderer.waypoints.addWaypoint.bind(worldRenderer.waypoints),
|
||||
removeWaypoint: worldRenderer.waypoints.removeWaypoint.bind(worldRenderer.waypoints),
|
||||
|
||||
// New method for updating skybox
|
||||
setSkyboxImage: worldRenderer.skyboxRenderer.setSkyboxImage.bind(worldRenderer.skyboxRenderer)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
77
renderer/viewer/three/skyboxRenderer.ts
Normal file
77
renderer/viewer/three/skyboxRenderer.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import * as THREE from 'three'
|
||||
|
||||
export class SkyboxRenderer {
|
||||
private texture: THREE.Texture | null = null
|
||||
private mesh: THREE.Mesh<THREE.SphereGeometry, THREE.MeshBasicMaterial> | null = null
|
||||
|
||||
constructor (private readonly scene: THREE.Scene, public initialImage: string | null) {}
|
||||
|
||||
async init () {
|
||||
if (this.initialImage) {
|
||||
await this.setSkyboxImage(this.initialImage)
|
||||
}
|
||||
}
|
||||
|
||||
async setSkyboxImage (imageUrl: string) {
|
||||
// Dispose old textures if they exist
|
||||
if (this.texture) {
|
||||
this.texture.dispose()
|
||||
}
|
||||
|
||||
// Load the equirectangular texture
|
||||
const textureLoader = new THREE.TextureLoader()
|
||||
this.texture = await new Promise((resolve) => {
|
||||
textureLoader.load(
|
||||
imageUrl,
|
||||
(texture) => {
|
||||
texture.mapping = THREE.EquirectangularReflectionMapping
|
||||
texture.encoding = THREE.sRGBEncoding
|
||||
// Keep pixelated look
|
||||
texture.minFilter = THREE.NearestFilter
|
||||
texture.magFilter = THREE.NearestFilter
|
||||
texture.needsUpdate = true
|
||||
resolve(texture)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
// Create or update the skybox
|
||||
if (this.mesh) {
|
||||
// Just update the texture on the existing material
|
||||
this.mesh.material.map = this.texture
|
||||
this.mesh.material.needsUpdate = true
|
||||
} else {
|
||||
// Create a large sphere geometry for the skybox
|
||||
const geometry = new THREE.SphereGeometry(500, 60, 40)
|
||||
// Flip the geometry inside out
|
||||
geometry.scale(-1, 1, 1)
|
||||
|
||||
// Create material using the loaded texture
|
||||
const material = new THREE.MeshBasicMaterial({
|
||||
map: this.texture,
|
||||
side: THREE.FrontSide // Changed to FrontSide since we're flipping the geometry
|
||||
})
|
||||
|
||||
// Create and add the skybox mesh
|
||||
this.mesh = new THREE.Mesh(geometry, material)
|
||||
this.scene.add(this.mesh)
|
||||
}
|
||||
}
|
||||
|
||||
update (cameraPosition: THREE.Vector3) {
|
||||
if (this.mesh) {
|
||||
this.mesh.position.copy(cameraPosition)
|
||||
}
|
||||
}
|
||||
|
||||
dispose () {
|
||||
if (this.texture) {
|
||||
this.texture.dispose()
|
||||
}
|
||||
if (this.mesh) {
|
||||
this.mesh.geometry.dispose()
|
||||
;(this.mesh.material as THREE.Material).dispose()
|
||||
this.scene.remove(this.mesh)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -85,7 +85,7 @@ export class WaypointsRenderer {
|
|||
this.waypointScene.add(sprite.group)
|
||||
|
||||
this.waypoints.set(id, {
|
||||
id, x, y, z, minDistance,
|
||||
id, x: x + 0.5, y: y + 0.5, z: z + 0.5, minDistance,
|
||||
color, label,
|
||||
sprite,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import { CameraShake } from './cameraShake'
|
|||
import { ThreeJsMedia } from './threeJsMedia'
|
||||
import { Fountain } from './threeJsParticles'
|
||||
import { WaypointsRenderer } from './waypoints'
|
||||
import { SkyboxRenderer } from './skyboxRenderer'
|
||||
|
||||
type SectionKey = string
|
||||
|
||||
|
|
@ -71,6 +72,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
}
|
||||
fountains: Fountain[] = []
|
||||
DEBUG_RAYCAST = false
|
||||
skyboxRenderer: SkyboxRenderer
|
||||
|
||||
private currentPosTween?: tweenJs.Tween<THREE.Vector3>
|
||||
private currentRotTween?: tweenJs.Tween<{ pitch: number, yaw: number }>
|
||||
|
|
@ -94,6 +96,10 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
this.holdingBlock = new HoldingBlock(this)
|
||||
this.holdingBlockLeft = new HoldingBlock(this, true)
|
||||
|
||||
// Initialize skybox renderer
|
||||
this.skyboxRenderer = new SkyboxRenderer(this.scene, null)
|
||||
void this.skyboxRenderer.init()
|
||||
|
||||
this.addDebugOverlay()
|
||||
this.resetScene()
|
||||
void this.init()
|
||||
|
|
@ -708,6 +714,10 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
this.cursorBlock.render()
|
||||
this.updateSectionOffsets()
|
||||
|
||||
// Update skybox position to follow camera
|
||||
const cameraPos = this.getCameraPosition()
|
||||
this.skyboxRenderer.update(cameraPos)
|
||||
|
||||
const sizeOrFovChanged = sizeChanged || this.displayOptions.inWorldRenderingConfig.fov !== this.camera.fov
|
||||
if (sizeOrFovChanged) {
|
||||
const size = this.renderer.getSize(new THREE.Vector2())
|
||||
|
|
@ -947,6 +957,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
|
||||
destroy (): void {
|
||||
super.destroy()
|
||||
this.skyboxRenderer.dispose()
|
||||
}
|
||||
|
||||
shouldObjectVisible (object: THREE.Object3D) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import fs from 'fs'
|
|||
import * as nbt from 'prismarine-nbt'
|
||||
import RegionFile from 'prismarine-provider-anvil/src/region'
|
||||
import { versions } from 'minecraft-data'
|
||||
import { getThreeJsRendererMethods } from 'renderer/viewer/three/threeJsMethods'
|
||||
import { openWorldDirectory, openWorldZip } from './browserfs'
|
||||
import { isGameActive } from './globalState'
|
||||
import { showNotification } from './react/NotificationProvider'
|
||||
|
|
@ -12,6 +13,9 @@ const parseNbt = promisify(nbt.parse)
|
|||
const simplifyNbt = nbt.simplify
|
||||
window.nbt = nbt
|
||||
|
||||
// Supported image types for skybox
|
||||
const VALID_IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.webp']
|
||||
|
||||
// todo display drop zone
|
||||
for (const event of ['drag', 'dragstart', 'dragend', 'dragover', 'dragenter', 'dragleave', 'drop']) {
|
||||
window.addEventListener(event, (e: any) => {
|
||||
|
|
@ -45,6 +49,34 @@ window.addEventListener('drop', async e => {
|
|||
})
|
||||
|
||||
async function handleDroppedFile (file: File) {
|
||||
// Check for image files first when game is active
|
||||
if (isGameActive(false) && VALID_IMAGE_EXTENSIONS.some(ext => file.name.toLowerCase().endsWith(ext))) {
|
||||
try {
|
||||
// Convert image to base64
|
||||
const reader = new FileReader()
|
||||
const base64Promise = new Promise<string>((resolve, reject) => {
|
||||
reader.onload = () => resolve(reader.result as string)
|
||||
reader.onerror = reject
|
||||
})
|
||||
reader.readAsDataURL(file)
|
||||
const base64Image = await base64Promise
|
||||
|
||||
// Get ThreeJS backend methods and update skybox
|
||||
const setSkyboxImage = getThreeJsRendererMethods()?.setSkyboxImage
|
||||
if (setSkyboxImage) {
|
||||
await setSkyboxImage(base64Image)
|
||||
showNotification('Skybox updated successfully')
|
||||
} else {
|
||||
showNotification('Cannot update skybox - renderer does not support it')
|
||||
}
|
||||
return
|
||||
} catch (err) {
|
||||
console.error('Failed to update skybox:', err)
|
||||
showNotification('Failed to update skybox', 'error')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (file.name.endsWith('.zip')) {
|
||||
void openWorldZip(file)
|
||||
return
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue