feat: full chests rendering support
This commit is contained in:
parent
1d4e0955c4
commit
ac82a39797
5 changed files with 229 additions and 47 deletions
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
|
|
@ -86,7 +86,7 @@ importers:
|
|||
version: 3.45.0
|
||||
net-browserify:
|
||||
specifier: github:zardoy/prismarinejs-net-browserify
|
||||
version: github.com/zardoy/prismarinejs-net-browserify/d63bd1df82186cf2f0baa161a83754037d537282
|
||||
version: github.com/zardoy/prismarinejs-net-browserify/f88123ad4f4407ac21fc435b11c561742825d0a7
|
||||
peerjs:
|
||||
specifier: ^1.5.0
|
||||
version: 1.5.0
|
||||
|
|
@ -10731,8 +10731,8 @@ packages:
|
|||
dependencies:
|
||||
vec3: 0.1.8
|
||||
|
||||
github.com/zardoy/prismarinejs-net-browserify/d63bd1df82186cf2f0baa161a83754037d537282:
|
||||
resolution: {tarball: https://codeload.github.com/zardoy/prismarinejs-net-browserify/tar.gz/d63bd1df82186cf2f0baa161a83754037d537282}
|
||||
github.com/zardoy/prismarinejs-net-browserify/f88123ad4f4407ac21fc435b11c561742825d0a7:
|
||||
resolution: {tarball: https://codeload.github.com/zardoy/prismarinejs-net-browserify/tar.gz/f88123ad4f4407ac21fc435b11c561742825d0a7}
|
||||
name: net-browserify
|
||||
version: 0.2.4
|
||||
dependencies:
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export function makeTextureAtlas (mcAssets) {
|
|||
const textureFiles = fs.readdirSync(blocksTexturePath).filter(file => file.endsWith('.png'))
|
||||
textureFiles.unshift(...localTextures)
|
||||
|
||||
const {generated:additionalTextures, twoBlockTextures} = getAdditionalTextures()
|
||||
const { generated: additionalTextures, twoBlockTextures } = getAdditionalTextures()
|
||||
textureFiles.push(...Object.keys(additionalTextures))
|
||||
|
||||
const texSize = nextPowerOfTwo(Math.ceil(Math.sqrt(textureFiles.length + twoBlockTextures.length)))
|
||||
|
|
@ -60,9 +60,9 @@ export function makeTextureAtlas (mcAssets) {
|
|||
offset++
|
||||
}
|
||||
const renderWidth = twoTileWidth ? tileSize * 2 : tileSize
|
||||
g.drawImage(img, 0, 0, img.width, img.height, x, y, renderWidth, tileSize)
|
||||
g.drawImage(img, 0, 0, renderWidth, tileSize, x, y, renderWidth, tileSize)
|
||||
|
||||
texturesIndex[name] = { u: x / imgSize, v: y / imgSize, su: renderWidth / imgSize, sv: tileSize / imgSize }
|
||||
texturesIndex[name] = { u: x / imgSize, v: y / imgSize, su: tileSize / imgSize, sv: tileSize / imgSize }
|
||||
}
|
||||
|
||||
return { image: canvas.toBuffer(), canvas, json: { size: tileSize / imgSize, textures: texturesIndex } }
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ fs.mkdirSync(blockStatesPath, { recursive: true })
|
|||
const warnings = new Set<string>()
|
||||
Promise.resolve().then(async () => {
|
||||
console.time('generateTextures')
|
||||
for (const version of mcAssets.versions) {
|
||||
for (const version of mcAssets.versions as typeof mcAssets['versions']) {
|
||||
// for debugging (e.g. when above is overridden)
|
||||
if (!mcAssets.versions.includes(version)) {
|
||||
throw new Error(`Version ${version} is not supported by minecraft-assets, skipping...`)
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ export type McAssets = {
|
|||
|
||||
export type BlockStatesOutput = {
|
||||
// states: {
|
||||
[blockName: string]: ResolvedModel
|
||||
// [blockName: string]: ResolvedModel
|
||||
// }
|
||||
// defaults: {
|
||||
// su: number
|
||||
|
|
@ -123,7 +123,7 @@ function cleanupBlockName (name: string) {
|
|||
return name
|
||||
}
|
||||
|
||||
const objectAssignStrict = <T extends Record<string, any>>(target: T, source: Partial<T>) => Object.assign(target, source)
|
||||
const objectAssignStrict = <T extends Record<string, any>> (target: T, source: Partial<T>) => Object.assign(target, source)
|
||||
|
||||
function getFinalModel (name: string, blocksModels: { [x: string]: BlockModel }) {
|
||||
name = cleanupBlockName(name)
|
||||
|
|
@ -155,13 +155,14 @@ function getFinalModel (name: string, blocksModels: { [x: string]: BlockModel })
|
|||
|
||||
const deepCopy = (obj) => JSON.parse(JSON.stringify(obj))
|
||||
|
||||
const workerUsedTextures = ['particle']
|
||||
function prepareModel (model: BlockModel, texturesJson) {
|
||||
const newModel = {}
|
||||
|
||||
const getFinalTexture = (originalBlockName) => {
|
||||
// texture name e.g. blocks/anvil_base
|
||||
const cleanBlockName = cleanupBlockName(originalBlockName);
|
||||
return {...texturesJson[cleanBlockName], __debugName: cleanBlockName}
|
||||
return { ...texturesJson[cleanBlockName], __debugName: cleanBlockName }
|
||||
}
|
||||
|
||||
const finalTextures = []
|
||||
|
|
@ -171,10 +172,15 @@ function prepareModel (model: BlockModel, texturesJson) {
|
|||
let texture = model.textures[side]
|
||||
|
||||
while (texture.charAt(0) === '#') {
|
||||
texture = model.textures[texture.slice(1)]
|
||||
const textureName = texture.slice(1)
|
||||
texture = model.textures[textureName]
|
||||
if (texture === undefined) throw new Error(`Texture ${textureName} in ${JSON.stringify(model.textures)} not found`)
|
||||
}
|
||||
|
||||
finalTextures[side] = getFinalTexture(texture)
|
||||
if (workerUsedTextures.includes(side)) {
|
||||
model.textures[side] = finalTextures[side]
|
||||
}
|
||||
}
|
||||
|
||||
for (const elem of model.elements!) {
|
||||
|
|
|
|||
|
|
@ -2,14 +2,18 @@ import Jimp from 'jimp'
|
|||
import minecraftData from 'minecraft-data'
|
||||
import prismarineRegistry from 'prismarine-registry'
|
||||
import { McAssets } from './modelsBuilder'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
// todo refactor
|
||||
const twoBlockTextures = []
|
||||
const twoBlockTextures: string[] = []
|
||||
let currentImage: Jimp
|
||||
let currentBlockName: string
|
||||
let currentMcAssets: McAssets
|
||||
let isPreFlattening = false
|
||||
const postFlatenningRegistry = prismarineRegistry('1.13')
|
||||
const __dirname = path.dirname(fileURLToPath(new URL(import.meta.url)))
|
||||
|
||||
type SidesType = {
|
||||
"up": string
|
||||
|
|
@ -46,6 +50,17 @@ type TextureMap = [
|
|||
height?: number,
|
||||
]
|
||||
|
||||
const justCropUV = (x: number, y: number, x1, y1) => {
|
||||
// input: 0-16, output: 0-currentImage.getWidth()
|
||||
const width = Math.abs(x1 - x)
|
||||
const height = Math.abs(y1 - y)
|
||||
return currentImage.clone().crop(
|
||||
x / 16 * currentImage.getWidth(),
|
||||
y / 16 * currentImage.getHeight(),
|
||||
width / 16 * currentImage.getWidth(),
|
||||
height / 16 * currentImage.getHeight(),
|
||||
)
|
||||
}
|
||||
const justCrop = (x: number, y: number, width = 16, height = 16) => {
|
||||
return currentImage.clone().crop(x, y, width, height)
|
||||
}
|
||||
|
|
@ -65,10 +80,10 @@ const combineTextures = (locations: TextureMap[]) => {
|
|||
|
||||
const generatedImageTextures: { [blockName: string]: /* base64 */string } = {}
|
||||
|
||||
const getBlockTexturesFromJimp = async <T extends Record<string, Jimp>> (sides: T, withUv = false): Promise<Record<keyof T, any>> => {
|
||||
const getBlockTexturesFromJimp = async <T extends Record<string, Jimp>> (sides: T, withUv = false, textureNameBase = currentBlockName): Promise<Record<keyof T, any>> => {
|
||||
const sidesTextures = {} as any
|
||||
for (const [side, jimp] of Object.entries(sides)) {
|
||||
const textureName = `${currentBlockName}_${side}`
|
||||
const textureName = `${textureNameBase}_${side}`
|
||||
const sideTexture = withUv ? { uv: [0, 0, jimp.getWidth(), jimp.getHeight()], texture: textureName } : textureName
|
||||
const base64 = await jimp.getBase64Async(jimp.getMIME())
|
||||
if (side === 'side') {
|
||||
|
|
@ -128,10 +143,11 @@ const handleSign = async (dataBase: string, match: RegExpExecArray) => {
|
|||
const isWall = currentBlockName.includes('wall_')
|
||||
const isHanging = currentBlockName.includes('hanging_')
|
||||
const rotationState = states.find(state => state.name === 'rotation')
|
||||
const faceTexture = { texture: blockTextures.face.texture, uv: blockTextures.face.uv }
|
||||
if (isWall || isHanging) {
|
||||
// todo isHanging
|
||||
if (!isHanging) {
|
||||
const facingState = states.find(state => state.name === 'facing')
|
||||
const facingState = states.find(state => state.name === 'facing')!
|
||||
const facingMap = {
|
||||
south: 0,
|
||||
west: 90,
|
||||
|
|
@ -157,12 +173,11 @@ const handleSign = async (dataBase: string, match: RegExpExecArray) => {
|
|||
"from": [0, 4.5, 0],
|
||||
"to": [16, 11.5, 1.5],
|
||||
faces: {
|
||||
// north: { texture: blockTextures.face, uv: [0, 0, 16, 16] },
|
||||
south: { texture: blockTextures.face.texture, uv: [0, 0, 16, 16] },
|
||||
east: { texture: blockTextures.signboard_side.texture, uv: [0, 0, 16, 16] },
|
||||
west: { texture: blockTextures.signboard_side.texture, uv: [0, 0, 16, 16] },
|
||||
up: { texture: blockTextures.up.texture, uv: [0, 0, 16, 16] },
|
||||
down: { texture: blockTextures.up.texture, uv: [0, 0, 16, 16] },
|
||||
south: faceTexture,
|
||||
east: blockTextures.signboard_side,
|
||||
west: blockTextures.signboard_side,
|
||||
up: blockTextures.up,
|
||||
down: blockTextures.up,
|
||||
},
|
||||
}
|
||||
],
|
||||
|
|
@ -202,18 +217,197 @@ const handleSign = async (dataBase: string, match: RegExpExecArray) => {
|
|||
"from": [0, 9, 7.25],
|
||||
"to": [16, 16, 8.75],
|
||||
faces: {
|
||||
north: { texture: blockTextures.face.texture, uv: [0, 0, 16, 16] },
|
||||
south: { texture: blockTextures.face.texture, uv: [0, 0, 16, 16] },
|
||||
east: { texture: blockTextures.signboard_side.texture, uv: [0, 0, 16, 16] },
|
||||
west: { texture: blockTextures.signboard_side.texture, uv: [0, 0, 16, 16] },
|
||||
up: { texture: blockTextures.up.texture, uv: [0, 0, 16, 16] },
|
||||
down: { texture: blockTextures.up.texture, uv: [0, 0, 16, 16] },
|
||||
north: faceTexture,
|
||||
south: faceTexture,
|
||||
east: blockTextures.signboard_side,
|
||||
west: blockTextures.signboard_side,
|
||||
up: blockTextures.up,
|
||||
down: blockTextures.up,
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
twoBlockTextures.push(blockTextures.face.texture)
|
||||
twoBlockTextures.push(blockTextures.up.texture)
|
||||
}
|
||||
|
||||
const chestModels = {
|
||||
chest: {
|
||||
"parent": "block/block",
|
||||
"textures": {
|
||||
"particle": "#particles"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [1, 0, 1],
|
||||
"to": [15, 10, 15],
|
||||
"faces": {
|
||||
"down": { "texture": "#chest", "uv": [3.5, 4.75, 7, 8.25], "rotation": 180 },
|
||||
"north": { "texture": "#chest", "uv": [10.5, 8.25, 14, 10.75], "rotation": 180 },
|
||||
"east": { "texture": "#chest", "uv": [0, 8.25, 3.5, 10.75], "rotation": 180 },
|
||||
"south": { "texture": "#chest", "uv": [3.5, 8.25, 7, 10.75], "rotation": 180 },
|
||||
"west": { "texture": "#chest", "uv": [7, 8.25, 10.5, 10.75], "rotation": 180 }
|
||||
},
|
||||
},
|
||||
{
|
||||
"from": [1, 10, 1],
|
||||
"to": [15, 14, 15],
|
||||
"faces": {
|
||||
"up": { "texture": "#chest", "uv": [3.5, 4.75, 7, 8.25] },
|
||||
"north": { "texture": "#chest", "uv": [10.5, 3.75, 14, 4.75], "rotation": 180 },
|
||||
"east": { "texture": "#chest", "uv": [0, 3.75, 3.5, 4.75], "rotation": 180 },
|
||||
"south": { "texture": "#chest", "uv": [3.5, 3.75, 7, 4.75], "rotation": 180 },
|
||||
"west": { "texture": "#chest", "uv": [7, 3.75, 10.5, 4.75], "rotation": 180 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [7, 7, 0],
|
||||
"to": [9, 11, 1],
|
||||
"faces": {
|
||||
"down": { "texture": "#chest", "uv": [0.25, 0, 0.75, 0.25], "rotation": 180 },
|
||||
"up": { "texture": "#chest", "uv": [0.75, 0, 1.25, 0.25], "rotation": 180 },
|
||||
"north": { "texture": "#chest", "uv": [1, 0.25, 1.5, 1.25], "rotation": 180 },
|
||||
"west": { "texture": "#chest", "uv": [0.75, 0.25, 1, 1.25], "rotation": 180 },
|
||||
"east": { "texture": "#chest", "uv": [0, 0.25, 0.25, 1.25], "rotation": 180 }
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
chest_left: {
|
||||
"parent": "block/block",
|
||||
"textures": {
|
||||
"particle": "#particles"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [1, 0, 1],
|
||||
"to": [16, 10, 15],
|
||||
"faces": {
|
||||
"down": { "texture": "#chest", "uv": [3.5, 4.75, 7.25, 8.25], "rotation": 180 },
|
||||
"north": { "texture": "#chest", "uv": [10.75, 8.25, 14.5, 10.75], "rotation": 180 },
|
||||
"south": { "texture": "#chest", "uv": [3.5, 8.25, 7.25, 10.75], "rotation": 180 },
|
||||
"west": { "texture": "#chest", "uv": [7.25, 8.25, 10.75, 10.75], "rotation": 180 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [1, 10, 1],
|
||||
"to": [16, 14, 15],
|
||||
"faces": {
|
||||
"up": { "texture": "#chest", "uv": [3.5, 4.75, 7.25, 8.25], "rotation": 180 },
|
||||
"north": { "texture": "#chest", "uv": [10.75, 3.75, 14.5, 4.75], "rotation": 180 },
|
||||
"south": { "texture": "#chest", "uv": [3.5, 3.75, 7.25, 4.75], "rotation": 180 },
|
||||
"west": { "texture": "#chest", "uv": [7.25, 3.75, 10.75, 4.75], "rotation": 180 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [15, 7, 0],
|
||||
"to": [16, 11, 1],
|
||||
"faces": {
|
||||
"down": { "texture": "#chest", "uv": [0.25, 0, 0.5, 0.25], "rotation": 180 },
|
||||
"up": { "texture": "#chest", "uv": [0.5, 0, 0.75, 0.25], "rotation": 180 },
|
||||
"north": { "texture": "#chest", "uv": [0.75, 0.25, 1, 1.25], "rotation": 180 },
|
||||
"west": { "texture": "#chest", "uv": [0.5, 0.25, 0.75, 1.25], "rotation": 180 }
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
chest_right: {
|
||||
"parent": "block/block",
|
||||
"textures": {
|
||||
"particle": "#particles"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [0, 0, 1],
|
||||
"to": [15, 10, 15],
|
||||
"faces": {
|
||||
"down": { "texture": "#chest", "uv": [3.5, 4.75, 7.25, 8.25], "rotation": 180 },
|
||||
"north": { "texture": "#chest", "uv": [10.75, 8.25, 14.5, 10.75], "rotation": 180 },
|
||||
"east": { "texture": "#chest", "uv": [0, 8.25, 3.5, 10.75], "rotation": 180 },
|
||||
"south": { "texture": "#chest", "uv": [3.5, 8.25, 7.25, 10.75], "rotation": 180 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [0, 10, 1],
|
||||
"to": [15, 14, 15],
|
||||
"faces": {
|
||||
"up": { "texture": "#chest", "uv": [3.5, 4.75, 7.25, 8.25], "rotation": 180 },
|
||||
"north": { "texture": "#chest", "uv": [10.75, 3.75, 14.5, 4.75], "rotation": 180 },
|
||||
"east": { "texture": "#chest", "uv": [0, 3.75, 3.5, 4.75], "rotation": 180 },
|
||||
"south": { "texture": "#chest", "uv": [3.5, 3.75, 7.25, 4.75], "rotation": 180 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [0, 7, 0],
|
||||
"to": [1, 11, 1],
|
||||
"faces": {
|
||||
"down": { "texture": "#chest", "uv": [0.25, 0, 0.5, 0.25], "rotation": 180 },
|
||||
"up": { "texture": "#chest", "uv": [0.5, 0, 0.75, 0.25], "rotation": 180 },
|
||||
"north": { "texture": "#chest", "uv": [0.75, 0.25, 1, 1.25], "rotation": 180 },
|
||||
"east": { "texture": "#chest", "uv": [0.0, 0.25, 0.25, 1.25], "rotation": 180 }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// these blockStates / models copied from https://github.com/FakeDomi/FastChest/blob/master/src/main/resources/assets/minecraft/blockstates/
|
||||
const chestBlockStatesMap = {
|
||||
chest: JSON.parse(fs.readFileSync(path.join(__dirname, 'blockStates/chest.json'), 'utf-8')),
|
||||
trapped_chest: JSON.parse(fs.readFileSync(path.join(__dirname, 'blockStates/trapped_chest.json'), 'utf-8')),
|
||||
ender_chest: JSON.parse(fs.readFileSync(path.join(__dirname, 'blockStates/ender_chest.json'), 'utf-8')),
|
||||
}
|
||||
const handleChest = async (dataBase: string, match: RegExpExecArray) => {
|
||||
const blockStates = structuredClone(chestBlockStatesMap[currentBlockName])
|
||||
|
||||
const particle = match[1] === 'ender' ? 'obsidian' : 'oak_planks'
|
||||
|
||||
const blockStatesVariants = Object.values(blockStates.variants) as { model }[]
|
||||
const neededModels = [...new Set(blockStatesVariants.map((x) => x.model))]
|
||||
|
||||
for (const modelName of neededModels) {
|
||||
let chestTextureName = {
|
||||
chest: 'normal',
|
||||
trapped_chest: 'trapped',
|
||||
ender_chest: 'ender',
|
||||
}[currentBlockName]
|
||||
if (modelName.endsWith('_left')) chestTextureName = `${chestTextureName}_left`
|
||||
if (modelName.endsWith('_right')) chestTextureName = `${chestTextureName}_right`
|
||||
|
||||
// reading latest version since the texture wasn't changed, but in pre-flatenning need custom mapping for doubled_chest
|
||||
const texture = path.join(currentMcAssets.directory, `../1.19.1/entity/chest/${chestTextureName}.png`)
|
||||
|
||||
currentImage = await Jimp.read(texture)
|
||||
|
||||
const model = structuredClone(chestModels[modelName])
|
||||
// todo < 1.9
|
||||
if (currentMcAssets.version === '1.8.8') {
|
||||
// doesn't have definition of block yet
|
||||
model.parent = undefined
|
||||
}
|
||||
model.textures.particle = particle
|
||||
const newModelName = `${currentBlockName}_${modelName}`
|
||||
for (const variant of blockStatesVariants) {
|
||||
if (variant.model !== modelName) continue
|
||||
variant.model = newModelName
|
||||
}
|
||||
for (const [i, { faces }] of model.elements.entries()) {
|
||||
for (const [faceName, face] of Object.entries(faces) as any) {
|
||||
const { uv } = face
|
||||
//@ts-ignore
|
||||
const jimp = justCropUV(...uv)
|
||||
const key = `${chestTextureName}_${modelName}_${i}_${faceName}`
|
||||
const texture = await getBlockTexturesFromJimp({
|
||||
[key]: jimp
|
||||
}, true, key).then(a => a[key])
|
||||
face.texture = texture.texture
|
||||
face.uv = texture.uv
|
||||
}
|
||||
}
|
||||
currentMcAssets.blocksModels[newModelName] = model
|
||||
}
|
||||
currentMcAssets.blocksStates[currentBlockName] = blockStates
|
||||
}
|
||||
|
||||
const handlers = [
|
||||
|
|
@ -224,6 +418,7 @@ const handlers = [
|
|||
[/^wall_sign$/, handleSign],
|
||||
[/(.+)_wall_sign$/, handleSign],
|
||||
[/(.+)_sign$/, handleSign],
|
||||
[/^(?:(ender|trapped)_)?chest$/, handleChest],
|
||||
// no-op just suppress warning
|
||||
[/(^light|^moving_piston$)/, true],
|
||||
] as const
|
||||
|
|
@ -262,7 +457,7 @@ export const prepareMoreGeneratedBlocks = async (mcAssets: McAssets) => {
|
|||
}
|
||||
}
|
||||
|
||||
const warnings = []
|
||||
const warnings: string[] = []
|
||||
for (const [name, model] of Object.entries(mcAssets.blocksModels)) {
|
||||
if (Object.keys(model).length === 1 && model.textures) {
|
||||
const keys = Object.keys(model.textures)
|
||||
|
|
@ -279,22 +474,3 @@ export const prepareMoreGeneratedBlocks = async (mcAssets: McAssets) => {
|
|||
export const getAdditionalTextures = () => {
|
||||
return { generated: generatedImageTextures, twoBlockTextures }
|
||||
}
|
||||
|
||||
// test below
|
||||
// const dataBase = '...'
|
||||
// const blockName = 'light_blue_shulker_box'
|
||||
|
||||
// currentMcAssets = {
|
||||
// blocksModels: {},
|
||||
// blocksStates: {}
|
||||
// } as any
|
||||
// tryHandleBlockEntity(dataBase, blockName).then(() => {
|
||||
// for (const [key, value] of Object.entries(generatedImageTextures)) {
|
||||
// console.log(key, value)
|
||||
// }
|
||||
// console.log(currentMcAssets)
|
||||
// })
|
||||
// { name: 'chest_top', x: 0, y: 0, width: 16, height: 15 },
|
||||
// { name: 'chest_side_top', x: 0, y: 15, width: 16, height: 5 },
|
||||
// { name: 'chest_side_bottom', x: 0, y: 34, width: 16, height: 9 },
|
||||
// { name: 'chest_front', x: 0, y: 43, width: 16, height: 14 },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue