Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Item renderer update #41

Merged
merged 27 commits into from
Dec 3, 2024
Merged
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
00eded2
implement default components and component removal
jacobsjo Nov 11, 2024
85868ab
start update item renderer
jacobsjo Nov 12, 2024
38ca03a
implement range dispatch
jacobsjo Nov 12, 2024
f7314fc
refactor Color to its own namespace, add fromJson
jacobsjo Nov 12, 2024
5b0eb45
implement item tints
jacobsjo Nov 12, 2024
b005e8a
fix imports
jacobsjo Nov 12, 2024
81cb1fa
update demo and fixes
jacobsjo Nov 12, 2024
de1a8ba
fix circular dependencies
jacobsjo Nov 12, 2024
6180c2c
add item component string parser
jacobsjo Nov 13, 2024
b2ecc70
don't error on missing item_model component & map color fix
jacobsjo Nov 13, 2024
06d0dca
fix circular dependencies
jacobsjo Nov 13, 2024
a6c3f4c
start special renderer
jacobsjo Nov 13, 2024
e89a37e
update from 24w46a
jacobsjo Nov 13, 2024
52a3252
fix imports
jacobsjo Nov 13, 2024
0350a6e
store id in itemstack
jacobsjo Nov 13, 2024
bbd3983
implement most special models
jacobsjo Nov 14, 2024
171e3e3
improve item registry
jacobsjo Nov 14, 2024
cab2ccf
implement bundle/selected_item
jacobsjo Nov 14, 2024
290af74
implement bundle/fullness
jacobsjo Nov 14, 2024
4cb1fbd
remove local_time, fix chest special renderer
jacobsjo Nov 14, 2024
0bce53b
minor fixes
jacobsjo Nov 14, 2024
c1b281a
add tests
jacobsjo Nov 14, 2024
f6dc6b0
fix defaults of properties and tints
jacobsjo Nov 16, 2024
9649fea
add more tests
jacobsjo Nov 19, 2024
623599e
undo unnecessary formatting changes
jacobsjo Nov 19, 2024
7f0533d
update item_definition url
jacobsjo Nov 19, 2024
eb6229d
add changes from 1.21.4-pre1
jacobsjo Nov 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
update from 24w46a
changed default component handling again
  • Loading branch information
jacobsjo committed Nov 13, 2024
commit e89a37eedf73e77157d897c7d10d33f29c33df34
15 changes: 4 additions & 11 deletions demo/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { mat4 } from 'gl-matrix'
import type { ItemRendererResources, NbtTag, Resources, Voxel } from '../src/index.js'
import { BlockDefinition, BlockModel, Identifier, ItemRenderer, ItemStack, NormalNoise, Structure, StructureRenderer, TextureAtlas, VoxelRenderer, XoroshiroRandom, jsonToNbt, upperPowerOfTwo } from '../src/index.js'
import { BlockDefinition, BlockModel, Identifier, Item, ItemRenderer, ItemStack, NormalNoise, Structure, StructureRenderer, TextureAtlas, VoxelRenderer, XoroshiroRandom, jsonToNbt, upperPowerOfTwo } from '../src/index.js'
import { } from '../src/nbt/Util.js'
import { ItemModel } from '../src/render/ItemModel.js'

@@ -108,13 +108,12 @@ Promise.all([
})


const itemComponents: Record<string, Map<string, NbtTag>> = {}
Object.keys(item_components).forEach(id => {
const components = new Map<string, NbtTag>()
Object.keys(item_components[id]).forEach(c_id => {
components.set(c_id, jsonToNbt(item_components[id][c_id]))
})
itemComponents['minecraft:' + id] = components
Item.getRegistry().register(Identifier.create(id), new Item(components))
})

const atlasCanvas = document.createElement('canvas')
@@ -149,19 +148,13 @@ Promise.all([
const itemGl = itemCanvas.getContext('webgl')!
const itemInput = document.getElementById('item-input') as HTMLInputElement
itemInput.value = localStorage.getItem('deepslate_demo_item') ?? 'stone'
const baseItemStack = ItemStack.fromString(itemInput.value)
const itemStack = baseItemStack.withDefaultComponents(itemComponents[baseItemStack.id.toString()])
const itemStack = ItemStack.fromString(itemInput.value)
const itemRenderer = new ItemRenderer(itemGl, itemStack, resources)

itemInput.addEventListener('keyup', () => {
try {
const id = itemInput.value
const baseItemStack = ItemStack.fromString(itemInput.value)
if (!itemComponents[baseItemStack.id.toString()]){
itemInput.classList.add('invalid')
return
}
const itemStack = baseItemStack.withDefaultComponents(itemComponents[baseItemStack.id.toString()])
const itemStack = ItemStack.fromString(itemInput.value)
itemGl.clear(itemGl.DEPTH_BUFFER_BIT | itemGl.COLOR_BUFFER_BIT);
itemRenderer.setItem(itemStack)
itemRenderer.drawItem()
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -59,6 +59,7 @@
"prepublishOnly": "npm run build"
},
"dependencies": {
"dayjs": "^1.11.13",
"gl-matrix": "^3.3.0",
"md5": "^2.3.0",
"pako": "^2.0.3"
36 changes: 36 additions & 0 deletions src/core/Item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Identifier, NbtTag, Registry } from "../index.js"


export class Item {
private static REGISTRY: Registry<Item>

public static getRegistry() {
if (this.REGISTRY === undefined){
this.REGISTRY = Registry.createAndRegister<Item>('item')
}
return this.REGISTRY
}

constructor(
public components: Map<string, NbtTag> = new Map(),
) {
}

public getComponent<T>(key: string | Identifier, reader: (tag: NbtTag) => T) {
if (typeof key === 'string') {
key = Identifier.parse(key)
}
const value = this.components.get(key.toString())
if (value) {
return reader(value)
}
return undefined
}

public hasComponent(key: string | Identifier) {
if (typeof key === 'string') {
key = Identifier.parse(key)
}
return this.components.has(key.toString())
}
}
54 changes: 27 additions & 27 deletions src/core/ItemStack.ts
Original file line number Diff line number Diff line change
@@ -2,47 +2,61 @@ import { NbtParser } from '../nbt/NbtParser.js'
import type { NbtTag } from '../nbt/index.js'
import { NbtCompound, NbtInt, NbtString } from '../nbt/index.js'
import { StringReader } from '../util/index.js'
import { Holder } from './Holder.js'
import { Identifier } from './Identifier.js'
import { Item } from './Item.js'

export class ItemStack {
public readonly item: Holder<Item | undefined>

constructor(
public id: Identifier,
id: Identifier,
public count: number,
public components: Map<string, NbtTag> = new Map(),
) {}
) {
this.item = Holder.reference(Item.getRegistry(), id, false)
}

public getComponent<T>(key: string | Identifier, reader: (tag: NbtTag) => T) {
public getComponent<T>(key: string | Identifier, reader: (tag: NbtTag) => T, includeDefaultComponents: boolean = true): T | undefined {
if (typeof key === 'string') {
key = Identifier.parse(key)
}

if (this.components.has('!' + key.toString())){
return undefined
}
const value = this.components.get(key.toString())
if (value) {
return reader(value)
}
return undefined
return includeDefaultComponents ? this.item.value()?.getComponent(key, reader) : undefined
}

public hasComponent(key: string | Identifier) {
public hasComponent(key: string | Identifier, includeDefaultComponents: boolean = true): boolean {
if (typeof key === 'string') {
key = Identifier.parse(key)
}
return this.components.has(key.toString())
if (this.components.has('!' + key.toString())){
return false
}

return this.components.has(key.toString()) || (includeDefaultComponents && (this.item.value()?.hasComponent(key) ?? false))
}

public clone(): ItemStack {
// Component values are not cloned because they are assumed to be immutable
const components = new Map(this.components)
return new ItemStack(this.id, this.count, components)
return new ItemStack(this.item.key()!, this.count, components)
}

public is(other: string | Identifier | ItemStack) {
if (typeof other === 'string') {
return this.id.equals(Identifier.parse(other))
return this.item.key()!.equals(Identifier.parse(other))
}
if (other instanceof Identifier) {
return this.id.equals(other)
return this.item.key()!.equals(other)
}
return this.id.equals(other.id)
return this.item.key()!.equals(other.item.key())
}

public equals(other: unknown) {
@@ -56,7 +70,7 @@ export class ItemStack {
}

public isSameItemSameComponents(other: ItemStack) {
if (!this.id.equals(other.id) || this.components.size !== other.components.size) {
if (!this.item.key()!.equals(other.item.key()) || this.components.size !== other.components.size) {
return false
}
for (const [key, value] of this.components) {
@@ -68,22 +82,8 @@ export class ItemStack {
return true
}

public withDefaultComponents(defaultComponents: Map<string, NbtTag>): ItemStack {
const components = new Map(defaultComponents)

for (const [key, value] of this.components) {
if (key.startsWith('!')) {
components.delete(key.slice(1))
} else {
components.set(key, value)
}
}

return new ItemStack(this.id, this.count, components)
}

public toString() {
let result = this.id.toString()
let result = this.item.key()!.toString()
if (this.components.size > 0) {
result += `[${[...this.components.entries()].map(([k, v]) => `${k}=${v.toString()}`).join(',')}]`
}
@@ -142,7 +142,7 @@ export class ItemStack {

public toNbt() {
const result = new NbtCompound()
.set('id', new NbtString(this.id.toString()))
.set('id', new NbtString(this.item.key()!.toString()))
if (this.count > 1) {
result.set('count', new NbtInt(this.count))
}
2 changes: 2 additions & 0 deletions src/core/index.ts
Original file line number Diff line number Diff line change
@@ -8,9 +8,11 @@ export * from './Effects.js'
export * from './Holder.js'
export * from './HolderSet.js'
export * from './Identifier.js'
export * from './Item.js'
export * from './ItemStack.js'
export * from './PalettedContainer.js'
export * from './Registry.js'
export * from './Rotation.js'
export * from './Structure.js'
export * from './StructureProvider.js'

27 changes: 22 additions & 5 deletions src/render/ItemModel.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import dayjs from "dayjs"
import tz from "dayjs/plugin/timezone.js"
import { BlockModelProvider, Color, Cull, Identifier, ItemRenderingContext, ItemStack, Json, Mesh, TextureAtlasProvider, clamp } from "../index.js"
import { ItemTint } from "./ItemTint.js"
import { SpecialModel } from "./SpecialModel.js"

tz // don't remove import

export interface ItemModelProvider {
getItemModel(id: Identifier): ItemModel | null
@@ -124,10 +126,9 @@ export namespace ItemModel {
case 'using_item':
case 'fishing_rod/cast':
case 'bundle/has_selected_item':
case 'xmas':
case 'selected':
case 'carried':
case 'shift_down':
case 'extended_view':
return (item, context) => context[property] ?? false
case 'broken': return (item, context) => {
const damage = item.getComponent('damage', tag => tag.getAsNumber())
@@ -141,7 +142,11 @@ export namespace ItemModel {
}
case 'has_component':
const componentId = Identifier.parse(Json.readString(root.component) ?? '')
return (item, context) => item.hasComponent(componentId)
const ignore_default = Json.readBoolean(root.ignore_default) ?? false
return (item, context) => item.hasComponent(componentId, !ignore_default)
case 'keybind_down':
const keybind = Json.readString(root.keybind) ?? ''
return (item, context) => context.keybind_down?.includes(keybind) ?? false
case 'custom_model_data':
const index = Json.readInt(root.index) ?? 0
return (item, context) => item.getComponent('custom_model_data', tag => {
@@ -198,13 +203,25 @@ export namespace ItemModel {
return Identifier.parse(tag.getString('material')).toString()
}) ?? '' // TODO: verify default value
case 'block_state':
const block_state_property = Json.readString('block_state_property') ?? ''
const block_state_property = Json.readString(root.block_state_property) ?? ''
return (item, context) => item.getComponent('block_state', tag => {
if (!tag.isCompound()) {
return undefined
}
return tag.getString(block_state_property)
}) ?? '' // TODO: verify default value
case 'local_time':
const time_zone = Json.readString(root.time_zone)
const pattern = Json.readString(root.pattern) ?? 'yyyy-MM-dd'
return (item, context) => {
let time = dayjs(context.local_time)
if (time_zone) {
time = time.tz(time_zone)
}
return time.format(pattern)
}
case 'holder_type':
return (item, context) => context.holder_type?.toString() ?? ''
case 'custom_model_data':
const index = Json.readInt(root.index) ?? 0
return (item, context) => item.getComponent('custom_model_data', tag => {
7 changes: 5 additions & 2 deletions src/render/ItemRenderer.ts
Original file line number Diff line number Diff line change
@@ -18,12 +18,15 @@ export type ItemRenderingContext = {
using_item?: boolean,
'fishing_rod/cast'?: boolean,
'bundle/has_selected_item'?: boolean,
xmas?: boolean,
selected?: boolean,
carried?: boolean,
shift_down?: boolean,
extended_view?: boolean,

keybind_down?: string[],

main_hand?: 'left' | 'right',
local_time?: number, //milliseconds
holder_type?: Identifier,

cooldown_normalized?: number,
game_time?: number,
2 changes: 1 addition & 1 deletion src/util/Color.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NbtTag } from "../index.js"
import { NbtTag } from "../nbt/index.js"
import { Json } from "./Json.js"

export type Color = [number, number, number]
Loading
Oops, something went wrong.