148 lines
5.8 KiB
TypeScript
148 lines
5.8 KiB
TypeScript
import fs from 'fs'
|
|
import McAssets from 'minecraft-assets'
|
|
import { join } from 'path'
|
|
import { filesize } from 'filesize'
|
|
import minecraftDataLoader from 'minecraft-data'
|
|
import BlockLoader from 'prismarine-block'
|
|
import { JsonAtlas, makeTextureAtlas, writeCanvasStream } from './atlas'
|
|
import looksSame from 'looks-same' // ensure after canvas import
|
|
import { Version as _Version } from 'minecraft-data'
|
|
import { versionToNumber } from './utils'
|
|
|
|
// todo move it, remove it
|
|
const legacyInvsprite = JSON.parse(fs.readFileSync(join(__dirname, '../../../src/invsprite.json'), 'utf8'))
|
|
|
|
//@ts-ignore
|
|
const latestMcAssetsVersion = McAssets.versions.at(-1)!
|
|
// const latestVersion = minecraftDataLoader.supportedVersions.pc.at(-1)
|
|
const mcData = minecraftDataLoader(latestMcAssetsVersion)
|
|
const PBlock = BlockLoader(latestMcAssetsVersion)
|
|
|
|
function isCube (name) {
|
|
const id = mcData.blocksByName[name]?.id
|
|
if (!id) return
|
|
const block = new PBlock(id, 0, 0)
|
|
const shape = block.shapes?.[0]
|
|
return block.shapes?.length === 1 && shape[0] === 0 && shape[1] === 0 && shape[2] === 0 && shape[3] === 1 && shape[4] === 1 && shape[5] === 1
|
|
}
|
|
|
|
export type ItemsAtlasesOutputJson = {
|
|
latest: JsonAtlas
|
|
legacy: JsonAtlas
|
|
legacyMap: [string, string[]][]
|
|
}
|
|
|
|
export const generateItemsAtlases = async () => {
|
|
const latestAssets = McAssets(latestMcAssetsVersion)
|
|
const latestItems = fs.readdirSync(join(latestAssets.directory, 'items')).map(f => f.split('.')[0])
|
|
|
|
// item - texture path
|
|
const toAddTextures = {
|
|
fromBlocks: {} as Record<string, string>,
|
|
remapItems: {} as Record<string, string>, // todo
|
|
}
|
|
|
|
const getItemTextureOfBlock = (name: string) => {
|
|
const blockModel = latestAssets.blocksModels[name]
|
|
// const isPlainBlockDisplay = blockModel?.display?.gui?.rotation?.[0] === 0 && blockModel?.display?.gui?.rotation?.[1] === 0 && blockModel?.display?.gui?.rotation?.[2] === 0
|
|
// it seems that information about cross blocks is hardcoded
|
|
if (blockModel?.parent?.endsWith('block/cross')) {
|
|
toAddTextures.fromBlocks[name] = `blocks/${blockModel.textures.cross.split('/')[1]}`
|
|
return true
|
|
}
|
|
|
|
if (legacyInvsprite[name]) {
|
|
return true
|
|
}
|
|
|
|
if (fs.existsSync(join(latestAssets.directory, 'blocks', name + '.png'))) {
|
|
// very last resort
|
|
toAddTextures.fromBlocks[name] = `blocks/${name}`
|
|
return true
|
|
}
|
|
if (name.endsWith('_spawn_egg')) {
|
|
// todo also color
|
|
toAddTextures.fromBlocks[name] = `items/spawn_egg`
|
|
}
|
|
}
|
|
|
|
for (const item of mcData.itemsArray) {
|
|
if (latestItems.includes(item.name)) {
|
|
continue
|
|
}
|
|
// USE IN RUNTIME
|
|
if (isCube(item.name)) {
|
|
// console.log('cube', block.name)
|
|
} else if (!getItemTextureOfBlock(item.name)) {
|
|
console.warn('skipping item (not cube, no item texture)', item.name)
|
|
}
|
|
}
|
|
|
|
let fullItemsMap = {} as Record<string, string[]>
|
|
|
|
const itemsSizes = {}
|
|
let saving = 0
|
|
let overallsize = 0
|
|
let prevItemsDir
|
|
let prevVersion
|
|
for (const version of [...McAssets.versions].reverse()) {
|
|
const itemsDir = join(McAssets(version).directory, 'items')
|
|
for (const item of fs.readdirSync(itemsDir)) {
|
|
const prevItemPath = !prevItemsDir ? undefined : join(prevItemsDir, item)
|
|
const itemSize = fs.statSync(join(itemsDir, item)).size
|
|
if (prevItemPath && fs.existsSync(prevItemPath) && (await looksSame(join(itemsDir, item), prevItemPath, { strict: true })).equal) {
|
|
saving += itemSize
|
|
} else {
|
|
fullItemsMap[version] ??= []
|
|
fullItemsMap[version].push(item)
|
|
}
|
|
overallsize += itemSize
|
|
}
|
|
prevItemsDir = itemsDir
|
|
prevVersion = version
|
|
}
|
|
|
|
fullItemsMap = Object.fromEntries(Object.entries(fullItemsMap).map(([ver, items]) => [ver, items.filter(item => item.endsWith('.png'))]))
|
|
const latestVersionItems = fullItemsMap[latestMcAssetsVersion]
|
|
delete fullItemsMap[latestMcAssetsVersion]
|
|
const legacyItemsSortedEntries = Object.entries(fullItemsMap).sort(([a], [b]) => versionToNumber(a) - versionToNumber(b)).map(([key, value]) => [key, value.map(x => x.replace('.png', ''))] as [typeof key, typeof value])
|
|
// const allItemsLength = Object.values(fullItemsMap).reduce((acc, x) => acc + x.length, 0)
|
|
// console.log(`Items to generate: ${allItemsLength} (latest version: ${latestVersionItems.length})`)
|
|
const fullLatestItemsObject = {
|
|
...Object.fromEntries(latestVersionItems.map(item => [item, `items/${item.replace('.png', '')}`])),
|
|
...toAddTextures.fromBlocks,
|
|
...toAddTextures.remapItems
|
|
}
|
|
|
|
const latestAtlas = makeTextureAtlas(Object.keys(fullLatestItemsObject), (name) => {
|
|
const contents = `data:image/png;base64,${fs.readFileSync(join(latestAssets.directory, `${fullLatestItemsObject[name]}.png`), 'base64')}`
|
|
return {
|
|
contents,
|
|
}
|
|
}, undefined, 'remove')
|
|
const texturesPath = join(__dirname, '../../public/textures')
|
|
writeCanvasStream(latestAtlas.canvas, join(texturesPath, 'items.png'), () => {
|
|
console.log('Generated latest items atlas')
|
|
})
|
|
|
|
const legacyItemsMap = legacyItemsSortedEntries.flatMap(([ver, items]) => items.map(item => `${ver}-${item}.png`))
|
|
const legacyItemsAtlas = makeTextureAtlas(legacyItemsMap, (name) => {
|
|
const [ver, item] = name.split('-')
|
|
const contents = `data:image/png;base64,${fs.readFileSync(join(McAssets(ver).directory, `items/${item}`), 'base64')}`
|
|
return {
|
|
contents,
|
|
}
|
|
}, undefined, 'remove')
|
|
writeCanvasStream(legacyItemsAtlas.canvas, join(texturesPath, 'items-legacy.png'), () => {
|
|
console.log('Generated legacy items atlas')
|
|
})
|
|
|
|
const allItemsMaps: ItemsAtlasesOutputJson = {
|
|
latest: latestAtlas.json,
|
|
legacy: legacyItemsAtlas.json,
|
|
legacyMap: legacyItemsSortedEntries
|
|
}
|
|
fs.writeFileSync(join(texturesPath, 'items.json'), JSON.stringify(allItemsMaps), 'utf8')
|
|
|
|
console.log(`Generated items! Input size: ${filesize(overallsize)}, saving: ~${filesize(saving)}`)
|
|
}
|