diff --git a/src/app/components/generator/PreviewPanel.tsx b/src/app/components/generator/PreviewPanel.tsx
index ca2c6479..cb21905c 100644
--- a/src/app/components/generator/PreviewPanel.tsx
+++ b/src/app/components/generator/PreviewPanel.tsx
@@ -4,10 +4,9 @@ import { useState } from 'preact/hooks'
import { useModel } from '../../hooks/index.js'
import type { VersionId } from '../../services/index.js'
import { checkVersion } from '../../services/index.js'
-import { BiomeSourcePreview, DecoratorPreview, DensityFunctionPreview, NoisePreview, NoiseSettingsPreview } from '../previews/index.js'
-import { LootTablePreview } from '../previews/LootTablePreview.jsx'
+import { BiomeSourcePreview, BlockStatePreview, DecoratorPreview, DensityFunctionPreview, LootTablePreview, ModelPreview, NoisePreview, NoiseSettingsPreview } from '../previews/index.js'
-export const HasPreview = ['loot_table', 'dimension', 'worldgen/density_function', 'worldgen/noise', 'worldgen/noise_settings', 'worldgen/configured_feature', 'worldgen/placed_feature']
+export const HasPreview = ['loot_table', 'dimension', 'worldgen/density_function', 'worldgen/noise', 'worldgen/noise_settings', 'worldgen/configured_feature', 'worldgen/placed_feature', 'block_definition', 'model']
type PreviewPanelProps = {
model: DataModel | undefined,
@@ -51,5 +50,13 @@ export function PreviewPanel({ model, version, id, shown }: PreviewPanelProps) {
return
}
+ if (id === 'block_definition') {
+ return
+ }
+
+ if (id === 'model') {
+ return
+ }
+
return <>>
}
diff --git a/src/app/components/previews/BlockStatePreview.tsx b/src/app/components/previews/BlockStatePreview.tsx
new file mode 100644
index 00000000..7066b981
--- /dev/null
+++ b/src/app/components/previews/BlockStatePreview.tsx
@@ -0,0 +1,51 @@
+import { DataModel } from '@mcschema/core'
+import { BlockDefinition, Identifier, Structure, StructureRenderer } from 'deepslate/render'
+import type { mat4 } from 'gl-matrix'
+import { useCallback, useRef } from 'preact/hooks'
+import { useVersion } from '../../contexts/index.js'
+import { useAsync } from '../../hooks/useAsync.js'
+import { AsyncCancel } from '../../hooks/useAsyncFn.js'
+import { getResources, ResourceWrapper } from '../../services/Resources.js'
+import type { PreviewProps } from './index.js'
+import { InteractiveCanvas3D } from './InteractiveCanvas3D.jsx'
+
+const PREVIEW_ID = Identifier.parse('misode:preview')
+
+export const BlockStatePreview = ({ data, shown }: PreviewProps) => {
+ const { version } = useVersion()
+ const serializedData = JSON.stringify(data)
+
+ const { value: resources } = useAsync(async () => {
+ if (!shown) return AsyncCancel
+ const resources = await getResources(version)
+ const definition = BlockDefinition.fromJson(PREVIEW_ID.toString(), DataModel.unwrapLists(data))
+ const wrapper = new ResourceWrapper(resources, {
+ getBlockDefinition(id) {
+ if (id.equals(PREVIEW_ID)) return definition
+ return null
+ },
+ })
+ return wrapper
+ }, [shown, version, serializedData])
+
+ const renderer = useRef(undefined)
+
+ const onSetup = useCallback((canvas: HTMLCanvasElement) => {
+ if (!resources || !shown) return
+ const gl = canvas.getContext('webgl')
+ if (!gl) return
+ renderer.current = new StructureRenderer(gl, new Structure([1, 1, 1]).addBlock([0, 0, 0], PREVIEW_ID), resources)
+ }, [resources, shown])
+ const onResize = useCallback((width: number, height: number) => {
+ renderer.current?.setViewport(0, 0, width, height)
+ }, [resources])
+ const onDraw = useCallback((transform: mat4) => {
+ renderer.current?.drawStructure(transform)
+ }, [])
+
+ return <>
+
+
+
+ >
+}
diff --git a/src/app/components/previews/InteractiveCanvas3D.tsx b/src/app/components/previews/InteractiveCanvas3D.tsx
index b3639a5c..ad545be8 100644
--- a/src/app/components/previews/InteractiveCanvas3D.tsx
+++ b/src/app/components/previews/InteractiveCanvas3D.tsx
@@ -100,7 +100,6 @@ export function InteractiveCanvas3D({ onSetup, onDraw, onResize, state, startPos
redraw.current()
}
- console.warn('Uhhh...', canvas.current)
onSetup(canvas.current)
resizeHandler()
diff --git a/src/app/components/previews/ModelPreview.tsx b/src/app/components/previews/ModelPreview.tsx
new file mode 100644
index 00000000..d6baef21
--- /dev/null
+++ b/src/app/components/previews/ModelPreview.tsx
@@ -0,0 +1,57 @@
+import { DataModel } from '@mcschema/core'
+import { BlockDefinition, BlockModel, Identifier, Structure, StructureRenderer } from 'deepslate/render'
+import type { mat4 } from 'gl-matrix'
+import { useCallback, useRef } from 'preact/hooks'
+import { useVersion } from '../../contexts/index.js'
+import { useAsync } from '../../hooks/useAsync.js'
+import { AsyncCancel } from '../../hooks/useAsyncFn.js'
+import { getResources, ResourceWrapper } from '../../services/Resources.js'
+import type { PreviewProps } from './index.js'
+import { InteractiveCanvas3D } from './InteractiveCanvas3D.jsx'
+
+const PREVIEW_ID = Identifier.parse('misode:preview')
+const PREVIEW_DEFINITION = new BlockDefinition(PREVIEW_ID, { '': { model: PREVIEW_ID.toString() }}, undefined)
+
+export const ModelPreview = ({ data, shown }: PreviewProps) => {
+ const { version } = useVersion()
+ const serializedData = JSON.stringify(data)
+
+ const { value: resources } = useAsync(async () => {
+ if (!shown) return AsyncCancel
+ const resources = await getResources(version)
+ const model = BlockModel.fromJson(PREVIEW_ID.toString(), DataModel.unwrapLists(data))
+ model.flatten(resources)
+ const wrapper = new ResourceWrapper(resources, {
+ getBlockDefinition(id) {
+ if (id.equals(PREVIEW_ID)) return PREVIEW_DEFINITION
+ return null
+ },
+ getBlockModel(id) {
+ if (id.equals(PREVIEW_ID)) return model
+ return null
+ },
+ })
+ return wrapper
+ }, [shown, version, serializedData])
+
+ const renderer = useRef(undefined)
+
+ const onSetup = useCallback((canvas: HTMLCanvasElement) => {
+ if (!resources || !shown) return
+ const gl = canvas.getContext('webgl')
+ if (!gl) return
+ renderer.current = new StructureRenderer(gl, new Structure([1, 1, 1]).addBlock([0, 0, 0], PREVIEW_ID), resources)
+ }, [resources, shown])
+ const onResize = useCallback((width: number, height: number) => {
+ renderer.current?.setViewport(0, 0, width, height)
+ }, [resources])
+ const onDraw = useCallback((transform: mat4) => {
+ renderer.current?.drawStructure(transform)
+ }, [])
+
+ return <>
+
+
+
+ >
+}
diff --git a/src/app/components/previews/index.ts b/src/app/components/previews/index.ts
index c7b075dd..82ad9d42 100644
--- a/src/app/components/previews/index.ts
+++ b/src/app/components/previews/index.ts
@@ -2,8 +2,11 @@ import type { DataModel } from '@mcschema/core'
import type { VersionId } from '../../services/index.js'
export * from './BiomeSourcePreview.js'
+export * from './BlockStatePreview.jsx'
export * from './DecoratorPreview.js'
export * from './DensityFunctionPreview.js'
+export * from './LootTablePreview.jsx'
+export * from './ModelPreview.jsx'
export * from './NoisePreview.js'
export * from './NoiseSettingsPreview.js'
diff --git a/src/app/services/DataFetcher.ts b/src/app/services/DataFetcher.ts
index 8d062f9a..3fbadea9 100644
--- a/src/app/services/DataFetcher.ts
+++ b/src/app/services/DataFetcher.ts
@@ -167,12 +167,13 @@ export async function fetchResources(versionId: VersionId) {
const version = config.versions.find(v => v.id === versionId)!
await validateCache(version)
try {
- const [models, uvMapping, atlas] = await Promise.all([
+ const [blockDefinitions, models, uvMapping, atlas] = await Promise.all([
+ fetchAllPresets(versionId, 'block_definition'),
fetchAllPresets(versionId, 'model'),
fetch(`${mcmeta(version, 'atlas')}/all/data.min.json`).then(r => r.json()),
loadImage(`${mcmeta(version, 'atlas')}/all/atlas.png`),
])
- return { models, uvMapping, atlas }
+ return { blockDefinitions, models, uvMapping, atlas }
} catch (e) {
throw new Error(`Error occured while fetching resources: ${message(e)}`)
}
diff --git a/src/app/services/Resources.ts b/src/app/services/Resources.ts
index 5618effe..8caf0c64 100644
--- a/src/app/services/Resources.ts
+++ b/src/app/services/Resources.ts
@@ -1,5 +1,5 @@
-import type { BlockModelProvider, ItemStack, TextureAtlasProvider, UV } from 'deepslate/render'
-import { BlockModel, Identifier, ItemRenderer, TextureAtlas, upperPowerOfTwo } from 'deepslate/render'
+import type { BlockDefinitionProvider, BlockFlagsProvider, BlockModelProvider, BlockPropertiesProvider, ItemStack, TextureAtlasProvider, UV } from 'deepslate/render'
+import { BlockDefinition, BlockModel, Identifier, ItemRenderer, TextureAtlas, upperPowerOfTwo } from 'deepslate/render'
import { message } from '../Utils.js'
import { fetchLanguage, fetchResources } from './DataFetcher.js'
import type { VersionId } from './Schemas.js'
@@ -10,8 +10,8 @@ export async function getResources(version: VersionId) {
if (!Resources[version]) {
Resources[version] = (async () => {
try {
- const { models, uvMapping, atlas} = await fetchResources(version)
- Resources[version] = new ResourceManager(models, uvMapping, atlas)
+ const { blockDefinitions, models, uvMapping, atlas} = await fetchResources(version)
+ Resources[version] = new ResourceManager(blockDefinitions, models, uvMapping, atlas)
return Resources[version]
} catch (e) {
console.error('Error: ', e)
@@ -51,17 +51,26 @@ export async function renderItem(version: VersionId, item: ItemStack) {
return promise
}
-export class ResourceManager implements BlockModelProvider, TextureAtlasProvider {
- private blockModels: { [id: string]: BlockModel }
+interface Resources extends BlockDefinitionProvider, BlockModelProvider, TextureAtlasProvider, BlockFlagsProvider, BlockPropertiesProvider {}
+
+export class ResourceManager implements Resources {
+ private readonly blockDefinitions: { [id: string]: BlockDefinition }
+ private readonly blockModels: { [id: string]: BlockModel }
private textureAtlas: TextureAtlas
- constructor(models: Map, uvMapping: any, textureAtlas: HTMLImageElement) {
+ constructor(blockDefinitions: Map, models: Map, uvMapping: any, textureAtlas: HTMLImageElement) {
+ this.blockDefinitions = {}
this.blockModels = {}
this.textureAtlas = TextureAtlas.empty()
+ this.loadBlockDefinitions(blockDefinitions)
this.loadBlockModels(models)
this.loadBlockAtlas(textureAtlas, uvMapping)
}
+ public getBlockDefinition(id: Identifier) {
+ return this.blockDefinitions[id.toString()]
+ }
+
public getBlockModel(id: Identifier) {
return this.blockModels[id.toString()]
}
@@ -74,6 +83,18 @@ export class ResourceManager implements BlockModelProvider, TextureAtlasProvider
return this.textureAtlas.getTextureAtlas()
}
+ public getBlockFlags() {
+ return { opaque: false }
+ }
+
+ public getBlockProperties() {
+ return null
+ }
+
+ public getDefaultBlockProperties() {
+ return null
+ }
+
private loadBlockModels(models: Map) {
[...models.entries()].forEach(([id, model]) => {
this.blockModels[Identifier.create(id).toString()] = BlockModel.fromJson(id, model)
@@ -81,6 +102,12 @@ export class ResourceManager implements BlockModelProvider, TextureAtlasProvider
Object.values(this.blockModels).forEach(m => m.flatten(this))
}
+ private loadBlockDefinitions(definitions: Map) {
+ [...definitions.entries()].forEach(([id, definition]) => {
+ this.blockDefinitions[Identifier.create(id).toString()] = BlockDefinition.fromJson(id, definition)
+ })
+ }
+
private loadBlockAtlas(image: HTMLImageElement, textures: any) {
const atlasCanvas = document.createElement('canvas')
const w = upperPowerOfTwo(image.width)
@@ -101,6 +128,41 @@ export class ResourceManager implements BlockModelProvider, TextureAtlasProvider
}
}
+export class ResourceWrapper implements Resources {
+ constructor(
+ private readonly wrapped: Resources,
+ private readonly overrides: Partial,
+ ) {}
+
+ public getBlockDefinition(id: Identifier) {
+ return this.overrides.getBlockDefinition?.(id) ?? this.wrapped.getBlockDefinition(id)
+ }
+
+ public getBlockModel(id: Identifier) {
+ return this.overrides.getBlockModel?.(id) ?? this.wrapped.getBlockModel(id)
+ }
+
+ public getTextureUV(texture: Identifier) {
+ return this.overrides.getTextureUV?.(texture) ?? this.wrapped.getTextureUV(texture)
+ }
+
+ public getTextureAtlas() {
+ return this.overrides.getTextureAtlas?.() ?? this.wrapped.getTextureAtlas()
+ }
+
+ public getBlockFlags(id: Identifier) {
+ return this.overrides.getBlockFlags?.(id) ?? this.wrapped.getBlockFlags(id)
+ }
+
+ public getBlockProperties(id: Identifier) {
+ return this.overrides.getBlockProperties?.(id) ?? this.wrapped.getBlockProperties(id)
+ }
+
+ public getDefaultBlockProperties(id: Identifier) {
+ return this.overrides.getDefaultBlockProperties?.(id) ?? this.wrapped.getDefaultBlockProperties(id)
+ }
+}
+
const Languages: Record | Promise>> = {}
export async function getLanguage(version: VersionId) {