a few minor but important world renderer fixes & impr

This commit is contained in:
Vitaly Turovsky 2024-04-17 05:34:20 +03:00
commit 9a8ff32f5a
10 changed files with 63 additions and 12 deletions

View file

@ -6,7 +6,28 @@ export default defineConfig({
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
setupNodeEvents (on, config) {
// https://medium.com/automation-with-donald/get-memory-consumption-of-web-app-with-cypress-84e2656e5a0f
on('before:browser:launch', (browser = {
name: "",
family: "chromium",
channel: "",
displayName: "",
version: "",
majorVersion: "",
path: "",
isHeaded: false,
isHeadless: false
}, launchOptions) => {
if (browser.family === 'chromium' && browser.name !== 'electron') {
// auto open devtools
launchOptions.args.push('--enable-precise-memory-info')
}
return launchOptions
})
return require('./cypress/plugins/index.js')(on, config)
},
baseUrl: 'http://localhost:8080',

View file

@ -38,6 +38,10 @@ function setSectionDirty (pos, value = true) {
}
}
const softCleanup = () => {
world.blockCache = {}
}
self.onmessage = ({ data }) => {
const globalVar: any = globalThis
@ -58,6 +62,7 @@ self.onmessage = ({ data }) => {
world.addColumn(data.x, data.z, data.chunk)
} else if (data.type === 'unloadChunk') {
world.removeColumn(data.x, data.z)
if (Object.keys(world.columns).length === 0) softCleanup()
} else if (data.type === 'blockUpdate') {
const loc = new Vec3(data.pos.x, data.pos.y, data.pos.z).floored()
world.setBlockStateId(loc, data.stateId)

View file

@ -127,8 +127,8 @@ export class World {
// todo export in chunk instead
const hasChunkSection = (column, pos) => {
if (column._getSection) return column._getSection(pos)
if (column.sections) return column.sections[pos.y >> 4]
if (column.skyLightSections) return column.skyLightSections[getLightSectionIndex(pos, column.minY)]
if (column.sections) return column.sections[pos.y >> 4]
}
function posInChunk (pos) {

View file

@ -168,6 +168,7 @@ export class Viewer {
emitter.on('renderDistance', (d) => {
this.world.viewDistance = d
this.world.chunksLength = d === 0 ? 1 : generateSpiralMatrix(d).length
this.world.allChunksFinished = Object.keys(this.world.finishedChunks).length === this.world.chunksLength
})
emitter.on('updateLight', ({ pos }) => {

View file

@ -111,10 +111,6 @@ export class ViewerWrapper {
if (this.renderer) {
this.renderer.setSize(width, height)
}
// canvas updated by renderer
// if (viewer.composer) {
// viewer.updateComposerSize()
// }
viewer.world.handleResize()
}
}

View file

@ -40,6 +40,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
skyLight = 15
smoothLighting = true
enableLighting = true
allChunksFinished = false
handleResize = () => { }
abstract outputFormat: 'threeJs' | 'webgl'
@ -76,6 +78,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
const allFinished = Object.keys(this.finishedChunks).length === this.chunksLength
if (allFinished) {
this.allChunksLoaded?.()
this.allChunksFinished = true
}
}
@ -127,6 +130,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
this.active = false
this.loadedChunks = {}
this.sectionsOutstanding = new Map()
this.finishedChunks = {}
this.allChunksFinished = false
for (const worker of this.workers) {
worker.postMessage({ type: 'reset' })
}
@ -198,6 +203,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
for (const worker of this.workers) {
worker.postMessage({ type: 'unloadChunk', x, z })
}
this.allChunksFinished = Object.keys(this.finishedChunks).length === this.chunksLength
delete this.finishedChunks[`${x},${z}`]
}
setBlockStateId (pos: Vec3, stateId: number) {
@ -215,6 +222,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
setSectionDirty (pos: Vec3, value = true) {
if (this.viewDistance === -1) throw new Error('viewDistance not set')
this.allChunksFinished = false
const distance = this.getDistance(pos)
if (distance[0] > this.viewDistance || distance[1] > this.viewDistance) return
const key = `${Math.floor(pos.x / 16) * 16},${Math.floor(pos.y / 16) * 16},${Math.floor(pos.z / 16) * 16}`

View file

@ -7,6 +7,7 @@ import { renderSign } from '../sign-renderer/'
import { chunkPos, sectionPos } from './simpleUtils'
import { WorldRendererCommon } from './worldrendererCommon'
import * as tweenJs from '@tweenjs/tween.js'
import { BloomPass, RenderPass, UnrealBloomPass, EffectComposer, WaterPass, GlitchPass } from 'three-stdlib'
function mod (x, n) {
return ((x % n) + n) % n
@ -20,6 +21,10 @@ export class WorldRendererThree extends WorldRendererCommon {
chunkTextures = new Map<string, { [pos: string]: THREE.Texture }>()
signsCache = new Map<string, any>()
get tilesRendered () {
return Object.values(this.sectionObjects).reduce((acc, obj) => acc + (obj as any).tilesCount, 0)
}
constructor(public scene: THREE.Scene, public renderer: THREE.WebGLRenderer, public camera: THREE.PerspectiveCamera, numWorkers = 4) {
super(numWorkers)
}
@ -87,6 +92,8 @@ export class WorldRendererThree extends WorldRendererCommon {
boxHelper.name = 'helper'
object.add(boxHelper)
object.name = 'chunk'
//@ts-ignore
object.tilesCount = data.geometry.positions.length / 3 / 4
if (!this.showChunkBorders) {
boxHelper.visible = false
}

View file

@ -759,8 +759,14 @@ async function connect (connectOptions: {
if (appStatusState.isError) return
setLoadingScreenStatus(undefined)
void viewer.waitForChunksToRender().then(() => {
console.log('All done and ready!')
const start = Date.now()
let done = false
void viewer.world.renderUpdateEmitter.on('update', () => {
// todo might not emit as servers simply don't send chunk if it's empty
if (!viewer.world.allChunksFinished || done) return
done = true
console.log('All done and ready! In', (Date.now() - start) / 1000, 's')
viewer.render() // ensure the last state is rendered
document.dispatchEvent(new Event('cypress-world-ready'))
})
})

View file

@ -40,7 +40,6 @@ subscribeKey(miscUiState, 'gameLoaded', async () => {
return
}
if (!options.volume) return
console.debug('play sound', soundId)
const parts = soundString.split(';')
const soundVolume = +parts[0]!
const soundName = parts[1]!

View file

@ -161,11 +161,19 @@ let prevRenderDistance = options.renderDistance
export const setRenderDistance = () => {
assertDefined(worldView)
const { renderDistance: singleplayerRenderDistance, multiplayerRenderDistance } = options
const renderDistance = miscUiState.singleplayer ? singleplayerRenderDistance : multiplayerRenderDistance
let renderDistance = miscUiState.singleplayer ? singleplayerRenderDistance : multiplayerRenderDistance
const zeroRenderDistance = miscUiState.singleplayer && renderDistance === 0
if (zeroRenderDistance) {
renderDistance = 1 // mineflayer limitation workaround
}
bot.setSettings({
viewDistance: renderDistance
})
worldView.viewDistance = renderDistance
if (zeroRenderDistance) {
localServer!.players[0].view = 0
renderDistance = 0
}
worldView.updateViewDistance(renderDistance)
prevRenderDistance = renderDistance
}
export const reloadChunks = async () => {