Skip to content

Commit

Permalink
feat: big fix: rendering blocks for preflat versions (<1.13)! (#125)
Browse files Browse the repository at this point in the history
  • Loading branch information
zardoy committed May 20, 2024
1 parent 2bec255 commit 5e94239
Show file tree
Hide file tree
Showing 8 changed files with 1,880 additions and 12 deletions.
2 changes: 2 additions & 0 deletions prismarine-viewer/buildMesherWorker.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import path from 'path'
import { fileURLToPath } from 'url'
import fs from 'fs'
import { dynamicMcDataFiles } from './buildMesherConfig.mjs'
import { mesherSharedPlugins } from '../scripts/esbuildPlugins.mjs'

const allowedBundleFiles = ['legacy', 'versions', 'protocolVersions', 'features']

Expand Down Expand Up @@ -34,6 +35,7 @@ const buildOptions = {
'process.env.BROWSER': '"true"',
},
plugins: [
...mesherSharedPlugins,
{
name: 'external-json',
setup (build) {
Expand Down
1 change: 1 addition & 0 deletions prismarine-viewer/viewer/lib/mesher/mesher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ self.onmessage = ({ data }) => {
if (data.config) {
world ??= new World(data.config.version)
world.config = {...world.config, ...data.config}
globalThis.world = world
}

if (data.type === 'mesherData') {
Expand Down
76 changes: 71 additions & 5 deletions prismarine-viewer/viewer/lib/mesher/models.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Vec3 } from 'vec3'
import type { BlockStatesOutput } from '../../prepare/modelsBuilder'
import { World } from './world'
import { Block } from 'prismarine-block'
import { WorldBlock as Block } from './world'
import legacyJson from '../../../../src/preflatMap.json'
import { versionToNumber } from '../../prepare/utils'

const tints: any = {}
let blockStates: BlockStatesOutput
Expand Down Expand Up @@ -33,6 +35,53 @@ function prepareTints (tints) {
})
}

const calculatedBlocksEntries = Object.entries(legacyJson.clientCalculatedBlocks);
export function preflatBlockCalculation (block: Block, world: World, position: Vec3) {
const type = calculatedBlocksEntries.find(([name, blocks]) => blocks.includes(block.name))?.[0]
if (!type) return
switch (type) {
case 'directional': {
const isSolidConnection = !block.name.includes('redstone') && !block.name.includes('tripwire')
const neighbors = [
world.getBlock(position.offset(0, 0, 1)),
world.getBlock(position.offset(0, 0, -1)),
world.getBlock(position.offset(1, 0, 0)),
world.getBlock(position.offset(-1, 0, 0))
]
// set needed props to true: east:'false',north:'false',south:'false',west:'false'
const props = {}
for (const [i, neighbor] of neighbors.entries()) {
const isConnectedToSolid = isSolidConnection ? (neighbor && !neighbor.transparent) : false
if (isConnectedToSolid || neighbor?.name === block.name) {
props[['south', 'north', 'east', 'west'][i]] = 'true'
}
}
return props
}
// case 'gate_in_wall': {}
case 'block_snowy': {
const aboveIsSnow = world.getBlock(position.offset(0, 1, 0))?.name === 'snow'
return {
snowy: `${aboveIsSnow}`
}
}
case 'door': {
// upper half matches lower in
const half = block.getProperties().half
if (half === 'upper') {
// copy other properties
const lower = world.getBlock(position.offset(0, -1, 0))
if (lower?.name === block.name) {
return {
...lower.getProperties(),
half: 'upper'
}
}
}
}
}
}

function tintToGl (tint) {
const r = (tint >> 16) & 0xff
const g = (tint >> 8) & 0xff
Expand Down Expand Up @@ -349,7 +398,7 @@ function renderElement (world: World, cursor: Vec3, element, doAO: boolean, attr

const aos: number[] = []
const neighborPos = position.plus(new Vec3(...dir))
const baseLight = world.getLight(neighborPos) / 15
const baseLight = world.getLight(neighborPos, undefined, undefined, block.name) / 15
for (const pos of corners) {
let vertex = [
(pos[0] ? maxx : minx),
Expand Down Expand Up @@ -462,7 +511,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) {
for (cursor.z = sz; cursor.z < sz + 16; cursor.z++) {
for (cursor.x = sx; cursor.x < sx + 16; cursor.x++) {
const block = world.getBlock(cursor)!
if (block.name.includes('_sign')) {
if (block.name.includes('_sign') || block.name === 'sign') {
const key = `${cursor.x},${cursor.y},${cursor.z}`
const props: any = block.getProperties()
const facingRotationMap = {
Expand All @@ -478,7 +527,24 @@ export function getSectionGeometry (sx, sy, sz, world: World) {
}
}
const biome = block.biome.name
if (block.variant === undefined) {

let preflatRecomputeVariant = !!(block as any)._originalProperties
if (world.preflat) {
const patchProperties = preflatBlockCalculation(block, world, cursor)
if (patchProperties) {
//@ts-ignore
block._originalProperties ??= block._properties
//@ts-ignore
block._properties = { ...block._originalProperties, ...patchProperties }
preflatRecomputeVariant = true
} else {
//@ts-ignore
block._properties = block._originalProperties ?? block._properties
//@ts-ignore
block._originalProperties = undefined
}
}
if (block.variant === undefined || preflatRecomputeVariant) {
block.variant = getModelVariants(block)
}

Expand Down Expand Up @@ -592,7 +658,7 @@ function matchProperties (block: Block, /* to match against */properties: Record
return true
}

function getModelVariants (block: import('prismarine-block').Block) {
function getModelVariants (block: Block) {
// air, cave_air, void_air and so on...
// full list of invisible & special blocks https://minecraft.wiki/w/Model#Blocks_and_fluids
if (block.name === '' || block.name === 'air' || block.name.endsWith('_air')) return []
Expand Down
39 changes: 35 additions & 4 deletions prismarine-viewer/viewer/lib/mesher/world.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Block } from "prismarine-block"
import { Vec3 } from 'vec3'
import moreBlockDataGeneratedJson from '../moreBlockDataGenerated.json'
import { defaultMesherConfig } from './shared'
import legacyJson from '../../../../src/preflatMap.json'

const ignoreAoBlocks = Object.keys(moreBlockDataGeneratedJson.noOcclusions)

Expand All @@ -17,7 +18,7 @@ function isCube (shapes) {
return shape[0] === 0 && shape[1] === 0 && shape[2] === 0 && shape[3] === 1 && shape[4] === 1 && shape[5] === 1
}

export type WorldBlock = Block & {
export type WorldBlock = Omit<Block, 'position'> & {
variant?: any
// todo
isCube: boolean
Expand All @@ -30,14 +31,16 @@ export class World {
columns = {} as { [key: string]: import('prismarine-chunk/types/index').PCChunk }
blockCache = {}
biomeCache: { [id: number]: mcData.Biome }
preflat: boolean

constructor(version) {
this.Chunk = Chunks(version) as any
this.biomeCache = mcData(version).biomes
this.preflat = !mcData(version).supportFeature('blockStateId')
this.config.version = version
}

getLight (pos: Vec3, isNeighbor = false) {
getLight (pos: Vec3, isNeighbor = false, skipMoreChecks = false, curBlockName = '') {
const { enableLighting, skyLight } = this.config
if (!enableLighting) return 15
// const key = `${pos.x},${pos.y},${pos.z}`
Expand All @@ -52,8 +55,17 @@ export class World {
) + 2
)
// lightsCache.set(key, result)
if (result === 2 && this.getBlock(pos)?.name.match(/_stairs|slab/)) { // todo this is obviously wrong
result = this.getLight(pos.offset(0, 1, 0))
if (result === 2 && [this.getBlock(pos)?.name ?? '', curBlockName].some(x => x.match(/_stairs|slab|glass_pane/)) && !skipMoreChecks) { // todo this is obviously wrong
const lights = [
this.getLight(pos.offset(0, 1, 0), undefined, true),
this.getLight(pos.offset(0, -1, 0), undefined, true),
this.getLight(pos.offset(0, 0, 1), undefined, true),
this.getLight(pos.offset(0, 0, -1), undefined, true),
this.getLight(pos.offset(1, 0, 0), undefined, true),
this.getLight(pos.offset(-1, 0, 0), undefined, true)
].filter(x => x !== 2)
const min = Math.min(...lights)
result = min
}
if (isNeighbor && result === 2) result = 15 // TODO
return result
Expand Down Expand Up @@ -91,6 +103,8 @@ export class World {
}

getBlock (pos: Vec3): WorldBlock | null {
// for easier testing
if (!(pos instanceof Vec3)) pos = new Vec3(...pos as [number, number, number])
const key = columnKey(Math.floor(pos.x / 16) * 16, Math.floor(pos.z / 16) * 16)

const column = this.columns[key]
Expand All @@ -111,6 +125,23 @@ export class World {
throw new Error('position is not reliable, use pos parameter instead of block.position')
}
})
if (this.preflat) {
const namePropsStr = legacyJson.blocks[b.type + ':' + b.metadata] || legacyJson.blocks[b.type + ':' + '0']
b.name = namePropsStr.split('[')[0]
const propsStr = namePropsStr.split('[')?.[1]?.split(']');
if (propsStr) {
const newProperties = Object.fromEntries(propsStr.join('').split(',').map(x => {
let [key, val] = x.split('=') as any
if (!isNaN(val)) val = parseInt(val)
return [key, val]
}))
//@ts-ignore
b._properties = newProperties
} else {
//@ts-ignore
b._properties = {}
}
}
}

const block = this.blockCache[stateId]
Expand Down
4 changes: 3 additions & 1 deletion prismarine-viewer/viewer/lib/viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import EventEmitter from 'events'
import { WorldRendererThree } from './worldrendererThree'
import { generateSpiralMatrix } from 'flying-squid/dist/utils'
import { WorldRendererCommon, WorldRendererConfig, defaultWorldRendererConfig } from './worldrendererCommon'
import { versionToNumber } from '../prepare/utils'

export class Viewer {
scene: THREE.Scene
Expand Down Expand Up @@ -76,7 +77,8 @@ export class Viewer {
}

setVersion (userVersion: string) {
const texturesVersion = getVersion(userVersion)
let texturesVersion = getVersion(userVersion)
if (versionToNumber(userVersion) < versionToNumber('1.13')) texturesVersion = '1.13.2' // we normalize to post-flatenning in mesher
console.log('[viewer] Using version:', userVersion, 'textures:', texturesVersion)
this.world.setVersion(userVersion, texturesVersion)
this.entities.clear()
Expand Down
5 changes: 5 additions & 0 deletions prismarine-viewer/viewer/prepare/generateTextures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import mcAssets from 'minecraft-assets'
import fs from 'fs-extra'
import { prepareMoreGeneratedBlocks } from './moreGeneratedBlocks'
import { generateItemsAtlases } from './genItemsAtlas'
import { versionToNumber } from './utils'

const publicPath = path.resolve(__dirname, '../../public')

Expand All @@ -23,6 +24,10 @@ Promise.resolve().then(async () => {
if (!mcAssets.versions.includes(version)) {
throw new Error(`Version ${version} is not supported by minecraft-assets`)
}
if (versionToNumber(version) < versionToNumber('1.13')) {
// we normalize data to 1.13 for pre 1.13 versions
continue
}
const assets = mcAssets(version)
const { warnings: _warnings } = await prepareMoreGeneratedBlocks(assets)
_warnings.forEach(x => warnings.add(x))
Expand Down
24 changes: 22 additions & 2 deletions scripts/esbuildPlugins.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import MCProtocol from 'minecraft-protocol'
import MCData from 'minecraft-data'
import { throttle } from 'lodash-es'

const __dirname = dirname(new URL(import.meta.url).pathname)
const { supportedVersions } = MCProtocol

const prod = process.argv.includes('--prod')
Expand All @@ -26,16 +27,35 @@ export const startWatchingHmr = () => {
// 'dist/webglRendererWorker.js': 'webglRendererWorker',
}
for (const name of Object.keys(eventsPerFile)) {
const file = join('dist', name);
const file = join('dist', name)
if (!fs.existsSync(file)) console.warn(`[missing worker] File ${name} does not exist`)
fs.watchFile(file, () => {
writeToClients({ replace: { type: eventsPerFile[name] } })
})
}
}

/** @type {import('esbuild').Plugin[]} */
const mesherSharedPlugins = [
{
name: 'minecraft-data',
setup (build) {
build.onLoad({
filter: /data[\/\\]pc[\/\\]common[\/\\]legacy.json$/,
}, async (args) => {
const data = fs.readFileSync(join(__dirname, '../src/preflatMap.json'), 'utf8')
return {
contents: `module.exports = ${data}`,
loader: 'js',
}
})
}
}
]

/** @type {import('esbuild').Plugin[]} */
const plugins = [
...mesherSharedPlugins,
{
name: 'strict-aliases',
setup (build) {
Expand Down Expand Up @@ -339,4 +359,4 @@ const plugins = [
})
]

export { plugins, connectedClients as clients }
export { plugins, connectedClients as clients, mesherSharedPlugins }

0 comments on commit 5e94239

Please sign in to comment.