Skip to content

Commit

Permalink
World rendering improvements (#408)
Browse files Browse the repository at this point in the history
* add missing texture texture instead of rendering nothing

* utils: load browser version if in browser

* support for sneaking

* allow forcefully reload chunks

* add a way to dispose world renderer resources (exit)

* [doubt] skip rendering if folder already is here (speedup pm commands)

* support for resource packs (texturepacks)

* standard fix

* retrigger ci

---------

Co-authored-by: Romain Beaumont <[email protected]>
  • Loading branch information
zardoy and rom1504 authored Dec 31, 2023
1 parent 782e7ea commit 4265ad7
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 15 deletions.
11 changes: 10 additions & 1 deletion viewer/lib/atlas.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,18 @@ function nextPowerOfTwo (n) {
return n + 1
}

function readTexture (basePath, name) {
if (name === 'missing_texture.png') {
// grab ./missing_texture.png
basePath = __dirname
}
return fs.readFileSync(path.join(basePath, name), 'base64')
}

function makeTextureAtlas (mcAssets) {
const blocksTexturePath = path.join(mcAssets.directory, '/blocks')
const textureFiles = fs.readdirSync(blocksTexturePath).filter(file => file.endsWith('.png'))
textureFiles.unshift('missing_texture.png')

const texSize = nextPowerOfTwo(Math.ceil(Math.sqrt(textureFiles.length)))
const tileSize = 16
Expand All @@ -35,7 +44,7 @@ function makeTextureAtlas (mcAssets) {
texturesIndex[name] = { u: x / imgSize, v: y / imgSize, su: tileSize / imgSize, sv: tileSize / imgSize }

const img = new Image()
img.src = 'data:image/png;base64,' + fs.readFileSync(path.join(blocksTexturePath, textureFiles[i]), 'base64')
img.src = 'data:image/png;base64,' + readTexture(blocksTexturePath, textureFiles[i])
g.drawImage(img, 0, 0, 16, 16, x, y, 16, 16)
}

Expand Down
Binary file added viewer/lib/missing_texture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 5 additions & 3 deletions viewer/lib/models.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,8 @@ function renderElement (world, cursor, element, doAO, attr, globalMatrix, global
if (block.name === 'redstone_wire') {
tint = tints.redstone[`${block.getProperties().power}`]
} else if (block.name === 'birch_leaves' ||
block.name === 'spruce_leaves' ||
block.name === 'lily_pad') {
block.name === 'spruce_leaves' ||
block.name === 'lily_pad') {
tint = tints.constant[block.name]
} else if (block.name.includes('leaves') || block.name === 'vine') {
tint = tints.foliage[biome]
Expand Down Expand Up @@ -478,7 +478,9 @@ function matchProperties (block, properties) {
}

function getModelVariants (block, blockStates) {
const state = blockStates[block.name]
// air, cave_air, void_air and so on...
if (block.name.includes('air')) return []
const state = blockStates[block.name] ?? blockStates.missing_texture
if (!state) return []
if (state.variants) {
for (const [properties, variant] of Object.entries(state.variants)) {
Expand Down
14 changes: 14 additions & 0 deletions viewer/lib/modelsBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ function getModel (name, blocksModels) {
}

function prepareModel (model, texturesJson) {
// resolve texture names eg west: #all -> blocks/stone
for (const tex in model.textures) {
let root = model.textures[tex]
while (root.charAt(0) === '#') {
Expand Down Expand Up @@ -94,6 +95,19 @@ function resolveModel (name, blocksModels, texturesJson) {

function prepareBlocksStates (mcAssets, atlas) {
const blocksStates = mcAssets.blocksStates
mcAssets.blocksStates.missing_texture = {
variants: {
normal: {
model: 'missing_texture'
}
}
}
mcAssets.blocksModels.missing_texture = {
parent: 'block/cube_all',
textures: {
all: 'blocks/missing_texture'
}
}
for (const block of Object.values(blocksStates)) {
if (!block) continue
if (block.variants) {
Expand Down
8 changes: 8 additions & 0 deletions viewer/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ const THREE = require('three')
const path = require('path')

const textureCache = {}
// todo not ideal, export different functions for browser and node
function loadTexture (texture, cb) {
if (process.platform === 'browser') {
return require('./utils.web').loadTexture(texture, cb)
}

if (textureCache[texture]) {
cb(textureCache[texture])
} else {
Expand All @@ -22,6 +27,9 @@ function loadTexture (texture, cb) {
}

function loadJSON (json, cb) {
if (process.platform === 'browser') {
return require('./utils.web').loadJSON(json, cb)
}
cb(require(path.resolve(__dirname, '../../public/' + json)))
}

Expand Down
14 changes: 13 additions & 1 deletion viewer/lib/viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ class Viewer {
this.primitives = new Primitives(this.scene, this.camera)

this.domElement = renderer.domElement
this.playerHeight = 1.6
this.isSneaking = false
}

resetAll () {
this.world.resetWorld()
this.entities.clear()
this.primitives.clear()
}

setVersion (version) {
Expand Down Expand Up @@ -66,7 +74,11 @@ class Viewer {
}

setFirstPersonCamera (pos, yaw, pitch) {
if (pos) new TWEEN.Tween(this.camera.position).to({ x: pos.x, y: pos.y + 1.6, z: pos.z }, 50).start()
if (pos) {
let y = pos.y + this.playerHeight
if (this.isSneaking) y -= 0.3
new TWEEN.Tween(this.camera.position).to({ x: pos.x, y, z: pos.z }, 50).start()
}
this.camera.rotation.set(pitch, yaw, 0, 'ZYX')
}

Expand Down
3 changes: 3 additions & 0 deletions viewer/lib/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ self.onmessage = ({ data }) => {
} else if (data.type === 'blockUpdate') {
const loc = new Vec3(data.pos.x, data.pos.y, data.pos.z).floored()
world.setBlockStateId(loc, data.stateId)
} else if (data.type === 'reset') {
world = null
blocksStates = null
}
}

Expand Down
4 changes: 2 additions & 2 deletions viewer/lib/worldView.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,10 @@ class WorldView extends EventEmitter {
delete this.loadedChunks[`${pos.x},${pos.z}`]
}

async updatePosition (pos) {
async updatePosition (pos, force = false) {
const [lastX, lastZ] = chunkPos(this.lastPos)
const [botX, botZ] = chunkPos(pos)
if (lastX !== botX || lastZ !== botZ) {
if (lastX !== botX || lastZ !== botZ || force) {
const newView = new ViewRect(botX, botZ, this.viewDistance)
for (const coords of Object.keys(this.loadedChunks)) {
const x = parseInt(coords.split(',')[0])
Expand Down
30 changes: 27 additions & 3 deletions viewer/lib/worldrenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ function mod (x, n) {
class WorldRenderer {
constructor (scene, numWorkers = 4) {
this.sectionMeshs = {}
this.active = false
this.version = undefined
this.scene = scene
this.loadedChunks = {}
this.sectionsOutstanding = new Set()
this.renderUpdateEmitter = new EventEmitter()
this.blockStatesData = undefined
this.texturesDataUrl = undefined

this.material = new THREE.MeshLambertMaterial({ vertexColors: true, transparent: true, alphaTest: 0.1 })

Expand Down Expand Up @@ -60,23 +64,43 @@ class WorldRenderer {
}
}

setVersion (version) {
resetWorld () {
this.active = false
for (const mesh of Object.values(this.sectionMeshs)) {
this.scene.remove(mesh)
}
this.sectionMeshs = {}
for (const worker of this.workers) {
worker.postMessage({ type: 'reset' })
}
}

setVersion (version) {
this.version = version
this.resetWorld()
this.active = true
for (const worker of this.workers) {
worker.postMessage({ type: 'version', version })
}

loadTexture(`textures/${version}.png`, texture => {
this.updateTexturesData()
}

updateTexturesData () {
loadTexture(this.texturesDataUrl || `textures/${this.version}.png`, texture => {
texture.magFilter = THREE.NearestFilter
texture.minFilter = THREE.NearestFilter
texture.flipY = false
this.material.map = texture
})

loadJSON(`blocksStates/${version}.json`, blockStates => {
const loadBlockStates = () => {
return new Promise(resolve => {
if (this.blockStatesData) return resolve(this.blockStatesData)
return loadJSON(`blocksStates/${this.version}.json`, resolve)
})
}
loadBlockStates().then((blockStates) => {
for (const worker of this.workers) {
worker.postMessage({ type: 'blockStates', json: blockStates })
}
Expand Down
10 changes: 5 additions & 5 deletions viewer/prerender.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ const mcAssets = require('minecraft-assets')
const fs = require('fs-extra')

const texturesPath = path.resolve(__dirname, '../public/textures')
if (!fs.existsSync(texturesPath)) {
fs.mkdirSync(texturesPath)
if (fs.existsSync(texturesPath) && !process.argv.includes('-f')) {
console.log('textures folder already exists, skipping...')
process.exit(0)
}
fs.mkdirSync(texturesPath, { recursive: true })

const blockStatesPath = path.resolve(__dirname, '../public/blocksStates')
if (!fs.existsSync(blockStatesPath)) {
fs.mkdirSync(blockStatesPath)
}
fs.mkdirSync(blockStatesPath, { recursive: true })

const supportedVersions = require('./lib/version').supportedVersions

Expand Down

0 comments on commit 4265ad7

Please sign in to comment.