mc-assets 0.2.28 → 0.2.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,7 +19,24 @@ export declare class AssetsParser {
19
19
  getElements(queriedBlock: Omit<QueriedBlock, 'stateId'>, fallbackVariant?: boolean): 0 | 1 | BlockElement[];
20
20
  private resolvedModel;
21
21
  private getModelsByBlock;
22
- private getResolvedModelsByModel;
22
+ getResolvedModelsByModel(model: string, debugQueryName?: string, clearModel?: boolean): {
23
+ resolvedModel: Pick<BlockModel, "textures" | "ao" | "elements"> & {
24
+ x?: number;
25
+ y?: number;
26
+ z?: number;
27
+ uvlock?: boolean;
28
+ weight?: number;
29
+ };
30
+ } | undefined;
31
+ getResolvedModelsByModelData(modelData: BlockModel, debugQueryName?: string, clearModel?: boolean): {
32
+ resolvedModel: Pick<BlockModel, "textures" | "ao" | "elements"> & {
33
+ x?: number;
34
+ y?: number;
35
+ z?: number;
36
+ uvlock?: boolean;
37
+ weight?: number;
38
+ };
39
+ };
23
40
  getResolvedModelFirst(queriedBlock: Omit<QueriedBlock, 'stateId'>, fallbackVariant?: boolean): (Pick<BlockModel, "textures" | "ao" | "elements"> & {
24
41
  x?: number;
25
42
  y?: number;
@@ -143,6 +143,12 @@ export class AssetsParser {
143
143
  const modelData = this.blockModelsStore.get(this.version, model);
144
144
  if (!modelData)
145
145
  return;
146
+ return this.getResolvedModelsByModelData(modelData, debugQueryName, clearModel);
147
+ }
148
+ getResolvedModelsByModelData(modelData, debugQueryName, clearModel = true) {
149
+ if (clearModel) {
150
+ this.resolvedModel = {};
151
+ }
146
152
  const collectedParentModels = [];
147
153
  const collectModels = (model) => {
148
154
  collectedParentModels.push(model);
@@ -1,6 +1,7 @@
1
1
  import { AtlasParser } from './atlasParser';
2
2
  import { AssetsParser } from './assetsParser';
3
3
  import { BlockModelsStore, BlockStatesStore } from './stores';
4
+ import { BlockModel } from './types';
4
5
  export declare class ItemsRenderer {
5
6
  version: string;
6
7
  itemsAtlasParser: AtlasParser;
@@ -14,8 +15,8 @@ export declare class ItemsRenderer {
14
15
  type: string;
15
16
  /** @deprecated */
16
17
  path: string;
17
- };
18
- tryGetFullBlock(blockName: string, properties?: Record<string, string | boolean>): {
18
+ } | undefined;
19
+ tryGetFullBlock(model: any, blockName: string): {
19
20
  top: {
20
21
  slice: [number, number, number, number];
21
22
  type: string;
@@ -34,8 +35,9 @@ export declare class ItemsRenderer {
34
35
  /** @deprecated */
35
36
  path: string;
36
37
  };
38
+ resolvedModel: BlockModel;
37
39
  } | undefined;
38
- getItemTexture(itemName: string, properties?: Record<string, string | boolean>): {
40
+ getItemTexture(itemNameOrModel: string, _properties?: Record<string, string | boolean>, exactItemResolve?: boolean): {
39
41
  slice: [number, number, number, number];
40
42
  type: string;
41
43
  /** @deprecated */
@@ -59,5 +61,6 @@ export declare class ItemsRenderer {
59
61
  /** @deprecated */
60
62
  path: string;
61
63
  };
64
+ resolvedModel: BlockModel;
62
65
  } | undefined;
63
66
  }
@@ -19,6 +19,8 @@ export class ItemsRenderer {
19
19
  const type = texture.includes('items/') ? 'items' : (texture.includes('block/') || texture.includes('blocks/')) ? 'blocks' : 'items';
20
20
  const atlasParser = type === 'blocks' ? this.blocksAtlasParser : this.itemsAtlasParser;
21
21
  const textureInfo = atlasParser.getTextureInfo(texture.replace('block/', '').replace('blocks/', '').replace('item/', '').replace('items/', ''), this.version);
22
+ if (!textureInfo)
23
+ return;
22
24
  const atlas = atlasParser.atlas[textureInfo.imageType];
23
25
  return {
24
26
  slice: [
@@ -32,14 +34,10 @@ export class ItemsRenderer {
32
34
  path: type
33
35
  };
34
36
  }
35
- tryGetFullBlock(blockName, properties = {}) {
37
+ tryGetFullBlock(model, blockName) {
36
38
  if (!this.blocksAtlasParser)
37
39
  return;
38
- const resolvedModelParts = this.assetsParser.getResolvedModelFirst({
39
- name: blockName,
40
- properties,
41
- }, true);
42
- const resolvedModel = resolvedModelParts?.[0];
40
+ const { resolvedModel } = this.assetsParser.getResolvedModelsByModelData(model);
43
41
  if (!resolvedModel?.elements?.length)
44
42
  return;
45
43
  const isAllFullModels = resolvedModel.elements.every(elem => elem.from[0] === 0 && elem.from[1] === 0 && elem.from[2] === 0 && elem.to[0] === 16 && elem.to[1] === 16 && elem.to[2] === 16);
@@ -51,32 +49,55 @@ export class ItemsRenderer {
51
49
  const rightTexture = elem.faces.north?.texture ?? elem.faces.right?.texture ?? elem.faces.side?.texture;
52
50
  if (!topTexture || !leftTexture || !rightTexture)
53
51
  return;
52
+ const topTextureResolved = this.resolveTexture(topTexture);
53
+ if (!topTextureResolved)
54
+ throw new Error(`Missing texture for ${blockName} top texture`);
55
+ const leftTextureResolved = this.resolveTexture(leftTexture);
56
+ if (!leftTextureResolved)
57
+ throw new Error(`Missing texture for ${blockName} left texture`);
58
+ const rightTextureResolved = this.resolveTexture(rightTexture);
59
+ if (!rightTextureResolved)
60
+ throw new Error(`Missing texture for ${blockName} right texture`);
54
61
  return {
55
- top: this.resolveTexture(topTexture),
56
- left: this.resolveTexture(leftTexture),
57
- right: this.resolveTexture(rightTexture),
62
+ top: topTextureResolved,
63
+ left: leftTextureResolved,
64
+ right: rightTextureResolved,
65
+ resolvedModel: resolvedModel
58
66
  };
59
67
  }
60
- getItemTexture(itemName, properties = {}) {
68
+ getItemTexture(itemNameOrModel, _properties = {}, exactItemResolve = false) {
69
+ let [_namespace, _name] = itemNameOrModel.includes(':') ? itemNameOrModel.split(':') : ['minecraft', itemNameOrModel];
70
+ const namespace = _namespace === 'minecraft' ? '' : _namespace;
71
+ const name = _name;
72
+ const itemModelPath = namespace ? `${namespace}:item/${name}` : `item/${name}`;
73
+ const blockModelPath = namespace ? `${namespace}:block/${name}` : `block/${name}`;
74
+ const cleanFullModelPath = namespace ? `${namespace}:${name}` : name;
61
75
  let model;
62
- if (itemName.includes('/')) {
63
- model = this.modelsStore.get(this.version, itemName);
76
+ if (cleanFullModelPath.includes('/') || exactItemResolve) {
77
+ model = this.modelsStore.get(this.version, cleanFullModelPath);
64
78
  }
65
79
  else {
66
- model = this.modelsStore.get(this.version, `item/${itemName}`);
67
- if (!model || model.parent?.includes('block/')) {
68
- return this.tryGetFullBlock(itemName, properties);
80
+ model = this.modelsStore.get(this.version, itemModelPath);
81
+ let blockModel = model?.parent?.includes('block/');
82
+ if (!model) {
83
+ model = this.modelsStore.get(this.version, blockModelPath);
84
+ if (model)
85
+ blockModel = true;
86
+ }
87
+ if (blockModel) {
88
+ return this.tryGetFullBlock(model, cleanFullModelPath);
69
89
  }
70
90
  }
71
91
  if (!model)
72
92
  return;
73
- const texture = itemName.includes('block/') ?
93
+ const texture = cleanFullModelPath.includes('block/') ?
74
94
  // first defined block texture
75
95
  Object.values(model.textures ?? {})[0] :
76
96
  model.textures?.layer0; // classic item texture
77
97
  if (!texture)
78
98
  return;
79
- return this.resolveTexture(texture);
99
+ return (texture.startsWith('invsprite_') ? this.resolveTexture(texture.replace('invsprite_', '')) : undefined)
100
+ ?? this.resolveTexture(texture);
80
101
  // const {resolvedModel} = this.assetsParser.getResolvedModelByModelName('item/' + itemName, itemName) ?? {}
81
102
  // resolvedModel?.textures['layer0']
82
103
  }
@@ -10,11 +10,19 @@ describe('ItemsRenderer', () => {
10
10
  const itemsAtlasParser = new AtlasParser(itemsAtlases, '');
11
11
  const blocksAtlasParser = new AtlasParser(blocksAtlases, '');
12
12
  const renderer = new ItemsRenderer('latest', blockstatesModels, itemsAtlasParser, blocksAtlasParser);
13
+ const getItemTexture = (item) => {
14
+ const result = renderer.getItemTexture(item);
15
+ if (!result)
16
+ return result;
17
+ result['resolvedModel'] = !!result['resolvedModel'];
18
+ return result;
19
+ };
13
20
  describe('getItemTexture', () => {
14
21
  it('items texture', () => {
15
- expect(renderer.getItemTexture('item_frame')).toMatchInlineSnapshot(`
22
+ expect(getItemTexture('item_frame')).toMatchInlineSnapshot(`
16
23
  {
17
24
  "path": "items",
25
+ "resolvedModel": false,
18
26
  "slice": [
19
27
  720,
20
28
  128,
@@ -26,7 +34,7 @@ describe('ItemsRenderer', () => {
26
34
  `);
27
35
  });
28
36
  it('full blocks texture', () => {
29
- expect(renderer.getItemTexture('stone')).toMatchInlineSnapshot(`
37
+ expect(getItemTexture('stone')).toMatchInlineSnapshot(`
30
38
  {
31
39
  "left": {
32
40
  "path": "blocks",
@@ -38,6 +46,7 @@ describe('ItemsRenderer', () => {
38
46
  ],
39
47
  "type": "blocks",
40
48
  },
49
+ "resolvedModel": true,
41
50
  "right": {
42
51
  "path": "blocks",
43
52
  "slice": [
@@ -62,9 +71,10 @@ describe('ItemsRenderer', () => {
62
71
  `);
63
72
  });
64
73
  it('invsprite textures', () => {
65
- expect(renderer.getItemTexture('chest')).toMatchInlineSnapshot(`
74
+ expect(getItemTexture('chest')).toMatchInlineSnapshot(`
66
75
  {
67
76
  "path": "items",
77
+ "resolvedModel": false,
68
78
  "slice": [
69
79
  400,
70
80
  0,
@@ -76,7 +86,8 @@ describe('ItemsRenderer', () => {
76
86
  `);
77
87
  });
78
88
  it('not implemented logic', () => {
79
- expect(renderer.getItemTexture('cut_copper_slab')).toMatchInlineSnapshot(`undefined`);
89
+ expect(getItemTexture('cut_copper_slab')).toMatchInlineSnapshot(`undefined`);
90
+ expect(getItemTexture('bla_bla')).toMatchInlineSnapshot(`undefined`);
80
91
  });
81
92
  });
82
93
  describe('resolveTexture', () => {
@@ -93,6 +104,7 @@ describe('ItemsRenderer', () => {
93
104
  "type": "items",
94
105
  }
95
106
  `);
107
+ expect(renderer.resolveTexture('bla')).toMatchInlineSnapshot(`undefined`);
96
108
  });
97
109
  });
98
110
  });
@@ -1,6 +1,107 @@
1
1
  import { QueriedBlock } from './assetsParser';
2
+ import { BlockModel } from './types';
2
3
  export default function worldBlockProvider(blockstatesModels: any, blocksAtlas: any, version: string): {
3
- getAllResolvedModels0_1(block: Omit<QueriedBlock, "stateId">, fallbackVariant?: boolean): {
4
+ getAllResolvedModels0_1(block: Omit<QueriedBlock, "stateId">, fallbackVariant?: boolean): ({
5
+ parent?: string;
6
+ ambientocclusion?: boolean;
7
+ ao?: boolean;
8
+ elements: {
9
+ faces: {
10
+ [k: string]: {
11
+ texture: {
12
+ u: number;
13
+ v: number;
14
+ su: number;
15
+ sv: number;
16
+ tileIndex: number;
17
+ debugName: string;
18
+ };
19
+ uv?: number[];
20
+ cullface?: string;
21
+ rotation?: number;
22
+ tintindex?: number;
23
+ };
24
+ };
25
+ from: import("./types").BlockElementPos;
26
+ to: import("./types").BlockElementPos;
27
+ rotation?: {
28
+ origin: [number, number, number];
29
+ axis: string;
30
+ angle: number;
31
+ rescale?: boolean;
32
+ };
33
+ }[];
34
+ } | {
35
+ ao?: boolean;
36
+ x?: number;
37
+ y?: number;
38
+ z?: number;
39
+ uvlock?: boolean;
40
+ weight?: number;
41
+ elements: {
42
+ faces: {
43
+ [k: string]: {
44
+ texture: {
45
+ u: number;
46
+ v: number;
47
+ su: number;
48
+ sv: number;
49
+ tileIndex: number;
50
+ debugName: string;
51
+ };
52
+ uv?: number[];
53
+ cullface?: string;
54
+ rotation?: number;
55
+ tintindex?: number;
56
+ };
57
+ };
58
+ from: import("./types").BlockElementPos;
59
+ to: import("./types").BlockElementPos;
60
+ rotation?: {
61
+ origin: [number, number, number];
62
+ axis: string;
63
+ angle: number;
64
+ rescale?: boolean;
65
+ };
66
+ }[];
67
+ })[][];
68
+ transformModel: (model: BlockModel | (Pick<BlockModel, "textures" | "ao" | "elements"> & {
69
+ x?: number;
70
+ y?: number;
71
+ z?: number;
72
+ uvlock?: boolean;
73
+ weight?: number;
74
+ }), block: Omit<QueriedBlock, "stateId">) => {
75
+ parent?: string;
76
+ ambientocclusion?: boolean;
77
+ ao?: boolean;
78
+ elements: {
79
+ faces: {
80
+ [k: string]: {
81
+ texture: {
82
+ u: number;
83
+ v: number;
84
+ su: number;
85
+ sv: number;
86
+ tileIndex: number;
87
+ debugName: string;
88
+ };
89
+ uv?: number[];
90
+ cullface?: string;
91
+ rotation?: number;
92
+ tintindex?: number;
93
+ };
94
+ };
95
+ from: import("./types").BlockElementPos;
96
+ to: import("./types").BlockElementPos;
97
+ rotation?: {
98
+ origin: [number, number, number];
99
+ axis: string;
100
+ angle: number;
101
+ rescale?: boolean;
102
+ };
103
+ }[];
104
+ } | {
4
105
  ao?: boolean;
5
106
  x?: number;
6
107
  y?: number;
@@ -33,8 +134,8 @@ export default function worldBlockProvider(blockstatesModels: any, blocksAtlas:
33
134
  rescale?: boolean;
34
135
  };
35
136
  }[];
36
- }[][];
37
- getTextureInfo(textureName: string): {
137
+ };
138
+ getTextureInfo: (textureName: string) => {
38
139
  su: number;
39
140
  sv: number;
40
141
  getLoadedImage: () => Promise<HTMLImageElement>;
@@ -6,62 +6,67 @@ export default function worldBlockProvider(blockstatesModels, blocksAtlas, versi
6
6
  const blockModelsStore = getLoadedModelsStore(blockstatesModels);
7
7
  const assetsParser = new AssetsParser(version, blockStatesStore, blockModelsStore);
8
8
  const atlasParser = new AtlasParser(blocksAtlas, 'latest', 'legacy');
9
+ const getTextureInfo = (textureName) => {
10
+ return atlasParser.getTextureInfo(textureName.replace('block/', '').replace('blocks/', ''), version);
11
+ };
12
+ const transformModel = (model, block) => {
13
+ const { elements, textures, ...rest } = model;
14
+ return {
15
+ // todo validate elements
16
+ elements: elements?.map((elem) => {
17
+ return {
18
+ ...elem,
19
+ faces: Object.fromEntries(Object.entries(elem.faces).map(([faceName, face]) => {
20
+ const texture = face.texture;
21
+ if (!texture)
22
+ throw new Error(`Missing resolved texture ${texture} for face ${faceName} of ${block.name}`);
23
+ const finalTexture = getTextureInfo(texture);
24
+ if (!finalTexture)
25
+ throw new Error(`Missing texture data ${texture} for ${block.name}`);
26
+ const _from = elem.from;
27
+ const _to = elem.to;
28
+ // taken from https://github.com/DragonDev1906/Minecraft-Overviewer/
29
+ const COORDINATE_MAX = 16;
30
+ const uv = (face.uv || {
31
+ // default UVs
32
+ // format: [u1, v1, u2, v2] (u = x, v = y)
33
+ north: [_to[0], COORDINATE_MAX - _to[1], _from[0], COORDINATE_MAX - _from[1]],
34
+ east: [_from[2], COORDINATE_MAX - _to[1], _to[2], COORDINATE_MAX - _from[1]],
35
+ south: [_from[0], COORDINATE_MAX - _to[1], _to[0], COORDINATE_MAX - _from[1]],
36
+ west: [_from[2], COORDINATE_MAX - _to[1], _to[2], COORDINATE_MAX - _from[1]],
37
+ up: [_from[0], _from[2], _to[0], _to[2]],
38
+ down: [_to[0], _from[2], _from[0], _to[2]]
39
+ }[faceName]);
40
+ const su = (uv[2] - uv[0]) / COORDINATE_MAX * finalTexture.su;
41
+ const sv = (uv[3] - uv[1]) / COORDINATE_MAX * finalTexture.sv;
42
+ return [faceName, {
43
+ ...face,
44
+ texture: {
45
+ u: finalTexture.u + uv[0] / 16 * finalTexture.su,
46
+ v: finalTexture.v + uv[1] / 16 * finalTexture.sv,
47
+ su,
48
+ sv,
49
+ tileIndex: finalTexture.tileIndex,
50
+ debugName: texture,
51
+ },
52
+ }];
53
+ }))
54
+ };
55
+ }),
56
+ ...rest
57
+ };
58
+ };
9
59
  return {
10
60
  getAllResolvedModels0_1(block, fallbackVariant = false) {
11
61
  const modelsParts = assetsParser.getAllResolvedModels(block, fallbackVariant) ?? [];
12
62
  const interestedFaces = ['north', 'east', 'south', 'west', 'up', 'down'];
13
63
  return modelsParts.map(modelVariants => {
14
64
  return modelVariants.map(model => {
15
- const { elements, textures, ...rest } = model;
16
- return {
17
- // todo validate elements
18
- elements: elements?.map((elem) => {
19
- return {
20
- ...elem,
21
- faces: Object.fromEntries(Object.entries(elem.faces).map(([faceName, face]) => {
22
- const texture = face.texture;
23
- if (!texture)
24
- throw new Error(`Missing resolved texture ${texture} for face ${faceName} of ${block.name}`);
25
- const finalTexture = this.getTextureInfo(texture);
26
- if (!finalTexture)
27
- throw new Error(`Missing texture data ${texture} for ${block.name}`);
28
- const _from = elem.from;
29
- const _to = elem.to;
30
- // taken from https://github.com/DragonDev1906/Minecraft-Overviewer/
31
- const COORDINATE_MAX = 16;
32
- const uv = (face.uv || {
33
- // default UVs
34
- // format: [u1, v1, u2, v2] (u = x, v = y)
35
- north: [_to[0], COORDINATE_MAX - _to[1], _from[0], COORDINATE_MAX - _from[1]],
36
- east: [_from[2], COORDINATE_MAX - _to[1], _to[2], COORDINATE_MAX - _from[1]],
37
- south: [_from[0], COORDINATE_MAX - _to[1], _to[0], COORDINATE_MAX - _from[1]],
38
- west: [_from[2], COORDINATE_MAX - _to[1], _to[2], COORDINATE_MAX - _from[1]],
39
- up: [_from[0], _from[2], _to[0], _to[2]],
40
- down: [_to[0], _from[2], _from[0], _to[2]]
41
- }[faceName]);
42
- const su = (uv[2] - uv[0]) / COORDINATE_MAX * finalTexture.su;
43
- const sv = (uv[3] - uv[1]) / COORDINATE_MAX * finalTexture.sv;
44
- return [faceName, {
45
- ...face,
46
- texture: {
47
- u: finalTexture.u + uv[0] / 16 * finalTexture.su,
48
- v: finalTexture.v + uv[1] / 16 * finalTexture.sv,
49
- su,
50
- sv,
51
- tileIndex: finalTexture.tileIndex,
52
- debugName: texture,
53
- },
54
- }];
55
- }))
56
- };
57
- }),
58
- ...rest
59
- };
65
+ return transformModel(model, block);
60
66
  }).filter(a => a.elements?.length);
61
67
  }).filter(a => a.length);
62
68
  },
63
- getTextureInfo(textureName) {
64
- return atlasParser.getTextureInfo(textureName.replace('block/', '').replace('blocks/', ''), version);
65
- }
69
+ transformModel,
70
+ getTextureInfo
66
71
  };
67
72
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mc-assets",
3
- "version": "0.2.28",
3
+ "version": "0.2.30",
4
4
  "author": "Vitaly Turovsky <vital2580@icloud.com>",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -15,7 +15,7 @@
15
15
  "@xmcl/core": "^2.13.0",
16
16
  "@xmcl/installer": "^5.4.0",
17
17
  "@zardoy/tsconfig": "^1.5.1",
18
- "canvas": "^2.11.2",
18
+ "canvas": "^3.1.0",
19
19
  "filesize": "^10.1.4",
20
20
  "imagemin": "^9.0.0",
21
21
  "imagemin-optipng": "^8.0.0",