mc-assets 0.1.7 → 0.2.1
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.
- package/README.MD +57 -49
- package/dist/assetsParser.d.ts +23 -31
- package/dist/assetsParser.js +78 -58
- package/dist/blockStatesModels.json +42446 -42293
- package/dist/blocksAtlasLatest.png +0 -0
- package/dist/blocksAtlases.json +52 -66
- package/dist/itemsRenderer.js +3 -2
- package/dist/types.d.ts +3 -3
- package/dist/worldBlockProvider.d.ts +5 -3
- package/dist/worldBlockProvider.js +49 -45
- package/package.json +5 -3
package/README.MD
CHANGED
|
@@ -19,7 +19,7 @@ npm i mc-assets
|
|
|
19
19
|
- **Easy to Use Items Textures** - Includes hand-crafted isometric textures for some blocks.
|
|
20
20
|
- **Block Entities Models** - Includes community-crafted models for block entities.
|
|
21
21
|
|
|
22
|
-
This module was originally designed as standalone package for [https://mcraft.fun](mcraft.fun) (repo) project so it is easier to maintain, but now it became a library that I think can cover any use case where you need to work with minecraft assets. This library means block states, models data and texture contents, it doesn't cover minecraft-data use cases.
|
|
22
|
+
This module was originally designed as standalone package for [https://mcraft.fun](mcraft.fun) (repo) project so it is easier to maintain, but now it became a library that I think can cover any use case where you need to work with minecraft assets. This library means block states, models data and texture contents, it doesn't cover minecraft-data use cases. With the best block model parser you can build your own renderers, editors, etc.
|
|
23
23
|
|
|
24
24
|
> Bundled modules & block states are version-accurate starting from 1.13.0 (post-flattening) version.
|
|
25
25
|
> Tested on Node.js 18 and above.
|
|
@@ -162,32 +162,26 @@ This packages includes versions for: 1.7.10, 1.8, 1.8.1, 1.8.2, 1.8.3, 1.8.4, 1.
|
|
|
162
162
|
- ✅ yellow_wall_banner
|
|
163
163
|
- ✅ zombie_head
|
|
164
164
|
- ✅ zombie_wall_head
|
|
165
|
-
- ❌
|
|
166
|
-
- ❌
|
|
167
|
-
- ❌
|
|
168
|
-
- ❌
|
|
169
|
-
- ❌ cyan_candle
|
|
165
|
+
- ❌ black_glazed_terracotta
|
|
166
|
+
- ❌ blue_glazed_terracotta
|
|
167
|
+
- ❌ brown_glazed_terracotta
|
|
168
|
+
- ❌ cyan_glazed_terracotta
|
|
170
169
|
- ❌ end_gateway
|
|
171
170
|
- ❌ end_portal
|
|
172
|
-
- ❌
|
|
173
|
-
- ❌
|
|
174
|
-
- ❌
|
|
175
|
-
- ❌
|
|
176
|
-
- ❌
|
|
177
|
-
- ❌
|
|
178
|
-
- ❌
|
|
179
|
-
- ❌
|
|
180
|
-
- ❌
|
|
181
|
-
- ❌
|
|
182
|
-
- ❌ purple_candle
|
|
183
|
-
- ❌ red_candle
|
|
184
|
-
- ❌ repeater
|
|
185
|
-
- ❌ sea_pickle
|
|
171
|
+
- ❌ gray_glazed_terracotta
|
|
172
|
+
- ❌ green_glazed_terracotta
|
|
173
|
+
- ❌ light_blue_glazed_terracotta
|
|
174
|
+
- ❌ light_gray_glazed_terracotta
|
|
175
|
+
- ❌ lime_glazed_terracotta
|
|
176
|
+
- ❌ magenta_glazed_terracotta
|
|
177
|
+
- ❌ orange_glazed_terracotta
|
|
178
|
+
- ❌ pink_glazed_terracotta
|
|
179
|
+
- ❌ purple_glazed_terracotta
|
|
180
|
+
- ❌ red_glazed_terracotta
|
|
186
181
|
- ❌ structure_void
|
|
187
|
-
- ❌
|
|
188
|
-
- ❌
|
|
189
|
-
- ❌
|
|
190
|
-
- ❌ yellow_candle
|
|
182
|
+
- ❌ trial_spawner
|
|
183
|
+
- ❌ white_glazed_terracotta
|
|
184
|
+
- ❌ yellow_glazed_terracotta
|
|
191
185
|
|
|
192
186
|
</details>
|
|
193
187
|
|
|
@@ -195,6 +189,8 @@ This packages includes versions for: 1.7.10, 1.8, 1.8.1, 1.8.2, 1.8.3, 1.8.4, 1.
|
|
|
195
189
|
|
|
196
190
|
[Atlas explorer (web demo)](https://zardoy.github.io/mc-assets/).
|
|
197
191
|
|
|
192
|
+
[External Models Playground](https://mcraft.fun/playground.html)
|
|
193
|
+
|
|
198
194
|
## Usage
|
|
199
195
|
|
|
200
196
|
Following atlases are generated:
|
|
@@ -223,38 +219,50 @@ const model = modelsStore.get('latest', 'block/stone_mirrored')
|
|
|
223
219
|
// note: you can always specify a specific version instead of 'latest'
|
|
224
220
|
const assetsParser = new AssetsParser('latest', blockstatesStore, modelsStore)
|
|
225
221
|
|
|
226
|
-
const resolvedModel = assetsParser.
|
|
222
|
+
const resolvedModel = assetsParser.getAllResolvedModels({
|
|
227
223
|
name: 'stone', // block name not model name
|
|
228
224
|
properties: {},
|
|
229
225
|
}, false) // false (default) means return empty if variant matching properties is not found
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
-
// all: 'block/stone',
|
|
233
|
-
// particle: 'block/stone',
|
|
234
|
-
// down: 'block/stone',
|
|
235
|
-
// up: 'block/stone',
|
|
236
|
-
// north: 'block/stone',
|
|
237
|
-
// east: 'block/stone',
|
|
238
|
-
// south: 'block/stone',
|
|
239
|
-
// west: 'block/stone'
|
|
240
|
-
// },
|
|
241
|
-
// elements: [
|
|
226
|
+
// [
|
|
227
|
+
// [
|
|
242
228
|
// {
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
//
|
|
248
|
-
//
|
|
249
|
-
//
|
|
250
|
-
//
|
|
251
|
-
//
|
|
252
|
-
//
|
|
229
|
+
// x: undefined,
|
|
230
|
+
// y: undefined,
|
|
231
|
+
// z: undefined,
|
|
232
|
+
// uvlock: undefined,
|
|
233
|
+
// weight: undefined,
|
|
234
|
+
// textures: {
|
|
235
|
+
// all: 'block/stone',
|
|
236
|
+
// particle: 'block/stone',
|
|
237
|
+
// down: 'block/stone',
|
|
238
|
+
// up: 'block/stone',
|
|
239
|
+
// north: 'block/stone',
|
|
240
|
+
// east: 'block/stone',
|
|
241
|
+
// south: 'block/stone',
|
|
242
|
+
// west: 'block/stone'
|
|
243
|
+
// },
|
|
244
|
+
// elements: [
|
|
245
|
+
// {
|
|
246
|
+
// from: [ 0, 0, 0 ],
|
|
247
|
+
// to: [ 16, 16, 16 ],
|
|
248
|
+
// faces: {
|
|
249
|
+
// down: { texture: 'block/stone', cullface: 'down' },
|
|
250
|
+
// up: { texture: 'block/stone', cullface: 'up' },
|
|
251
|
+
// north: { texture: 'block/stone', cullface: 'north' },
|
|
252
|
+
// south: { texture: 'block/stone', cullface: 'south' },
|
|
253
|
+
// west: { texture: 'block/stone', cullface: 'west' },
|
|
254
|
+
// east: { texture: 'block/stone', cullface: 'east' }
|
|
255
|
+
// }
|
|
256
|
+
// }
|
|
257
|
+
// ]
|
|
253
258
|
// }
|
|
254
|
-
// ]
|
|
255
|
-
//
|
|
259
|
+
// ],
|
|
260
|
+
// [ ... ] // second variant and so on
|
|
261
|
+
// ]
|
|
256
262
|
```
|
|
257
263
|
|
|
264
|
+
`getAllResolvedModels` returns an array of arrays, where first depth level is model parts, second depth level is variants (for example Minecraft chooses them randomly). You can use `getResolvedModelFirst` to get first variant only. Or `getResolvedModelRandom` to get a random variant.
|
|
265
|
+
|
|
258
266
|
### Rendering Atlas Textures
|
|
259
267
|
|
|
260
268
|
```ts
|
package/dist/assetsParser.d.ts
CHANGED
|
@@ -14,39 +14,31 @@ export declare class AssetsParser {
|
|
|
14
14
|
blockModelsStore: BlockModelsStore;
|
|
15
15
|
stateIdToElements: Record<string, BlockElement[] | 1>;
|
|
16
16
|
constructor(version: string, blockStatesStore: BlockStatesStore, blockModelsStore: BlockModelsStore);
|
|
17
|
-
getElementsCached(queriedBlock: QueriedBlock):
|
|
17
|
+
getElementsCached(queriedBlock: QueriedBlock): 1 | BlockElement[];
|
|
18
|
+
parseProperties(properties: string): Record<string, string | boolean>;
|
|
19
|
+
getElements(queriedBlock: Omit<QueriedBlock, 'stateId'>, fallbackVariant?: boolean): 0 | 1 | BlockElement[];
|
|
18
20
|
private resolvedModel;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
resolvedModel: Pick<BlockModel, "x" | "y" | "z" | "textures" | "ao" | "elements">;
|
|
23
|
-
} | undefined;
|
|
24
|
-
getResolvedModel(queriedBlock: Omit<QueriedBlock, 'stateId'>, fallbackVariant?: boolean): {
|
|
21
|
+
private getModelsByBlock;
|
|
22
|
+
private getResolvedModelsByModel;
|
|
23
|
+
getResolvedModelFirst(queriedBlock: Omit<QueriedBlock, 'stateId'>, fallbackVariant?: boolean): (Pick<BlockModel, "textures" | "ao" | "elements"> & {
|
|
25
24
|
x?: number;
|
|
26
25
|
y?: number;
|
|
27
26
|
z?: number;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
cullface?: string;
|
|
46
|
-
rotation?: number;
|
|
47
|
-
tintindex?: number;
|
|
48
|
-
};
|
|
49
|
-
};
|
|
50
|
-
}[];
|
|
51
|
-
};
|
|
27
|
+
uvlock?: boolean;
|
|
28
|
+
weight?: number;
|
|
29
|
+
})[] | undefined;
|
|
30
|
+
getResolvedModelRandom(queriedBlock: Omit<QueriedBlock, 'stateId'>, fallbackVariant?: boolean): (Pick<BlockModel, "textures" | "ao" | "elements"> & {
|
|
31
|
+
x?: number;
|
|
32
|
+
y?: number;
|
|
33
|
+
z?: number;
|
|
34
|
+
uvlock?: boolean;
|
|
35
|
+
weight?: number;
|
|
36
|
+
})[] | undefined;
|
|
37
|
+
getAllResolvedModels(queriedBlock: Omit<QueriedBlock, 'stateId'>, fallbackVariant?: boolean): (Pick<BlockModel, "textures" | "ao" | "elements"> & {
|
|
38
|
+
x?: number;
|
|
39
|
+
y?: number;
|
|
40
|
+
z?: number;
|
|
41
|
+
uvlock?: boolean;
|
|
42
|
+
weight?: number;
|
|
43
|
+
})[][] | undefined;
|
|
52
44
|
}
|
package/dist/assetsParser.js
CHANGED
|
@@ -17,26 +17,39 @@ export class AssetsParser {
|
|
|
17
17
|
this.stateIdToElements[stateId] = final;
|
|
18
18
|
return final;
|
|
19
19
|
}
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
parseProperties(properties) {
|
|
21
|
+
if (typeof properties === 'object') {
|
|
22
|
+
return properties;
|
|
23
|
+
}
|
|
24
|
+
const json = {};
|
|
25
|
+
for (const prop of properties.split(',')) {
|
|
26
|
+
const [key, value] = prop.split('=');
|
|
27
|
+
json[key] = value;
|
|
28
|
+
}
|
|
29
|
+
return json;
|
|
30
|
+
}
|
|
22
31
|
getElements(queriedBlock, fallbackVariant = false) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
return json;
|
|
32
|
+
const model = this.getResolvedModelFirst(queriedBlock, fallbackVariant);
|
|
33
|
+
if (!model)
|
|
34
|
+
return 0;
|
|
35
|
+
const allElements = [];
|
|
36
|
+
for (const m of model) {
|
|
37
|
+
if (!m?.elements)
|
|
38
|
+
continue;
|
|
39
|
+
allElements.push(...m.elements.map(({ from, to }) => [from, to]));
|
|
33
40
|
}
|
|
34
|
-
|
|
41
|
+
const elementsOptimized = allElements.length === 1 && arrEq(allElements[0][0], [0, 0, 0]) && arrEq(allElements[0][1], [16, 16, 16]) ? 1 : allElements;
|
|
42
|
+
return elementsOptimized;
|
|
43
|
+
}
|
|
44
|
+
// looks like workaround
|
|
45
|
+
resolvedModel = {};
|
|
46
|
+
getModelsByBlock(queriedBlock, fallbackVariant, multiOptim) {
|
|
47
|
+
const matchProperties = (block, /* to match against */ properties) => {
|
|
35
48
|
if (!properties) {
|
|
36
49
|
return true;
|
|
37
50
|
}
|
|
38
51
|
if (typeof properties === 'string') {
|
|
39
|
-
properties = parseProperties(properties);
|
|
52
|
+
properties = this.parseProperties(properties);
|
|
40
53
|
}
|
|
41
54
|
const blockProps = block.properties;
|
|
42
55
|
if (properties.OR) {
|
|
@@ -50,11 +63,11 @@ export class AssetsParser {
|
|
|
50
63
|
}
|
|
51
64
|
}
|
|
52
65
|
return true;
|
|
53
|
-
}
|
|
54
|
-
let
|
|
66
|
+
};
|
|
67
|
+
let applyModels = [];
|
|
55
68
|
const blockStates = this.blockStatesStore.get(this.version, queriedBlock.name);
|
|
56
69
|
if (!blockStates)
|
|
57
|
-
return
|
|
70
|
+
return;
|
|
58
71
|
const states = blockStates.variants;
|
|
59
72
|
if (states) {
|
|
60
73
|
let state = states[''] || states['normal'];
|
|
@@ -71,42 +84,62 @@ export class AssetsParser {
|
|
|
71
84
|
state = states[Object.keys(states)[0]];
|
|
72
85
|
}
|
|
73
86
|
else {
|
|
74
|
-
return
|
|
87
|
+
return;
|
|
75
88
|
}
|
|
76
89
|
}
|
|
77
|
-
|
|
90
|
+
if (state) {
|
|
91
|
+
applyModels.push(state);
|
|
92
|
+
}
|
|
78
93
|
}
|
|
79
94
|
if (blockStates.multipart) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
for (const { when, apply } of multipartWithWhen) {
|
|
84
|
-
if (matchProperties(queriedBlock, when)) {
|
|
85
|
-
modelApply = apply;
|
|
95
|
+
for (const { when, apply } of blockStates.multipart) {
|
|
96
|
+
if (!when || matchProperties(queriedBlock, when)) {
|
|
97
|
+
applyModels.push(apply);
|
|
86
98
|
}
|
|
87
99
|
}
|
|
88
|
-
if (!
|
|
89
|
-
|
|
100
|
+
if (!applyModels.length && fallbackVariant) {
|
|
101
|
+
const multipartWithWhen = blockStates.multipart.filter(x => x.when);
|
|
102
|
+
const apply = multipartWithWhen[0]?.apply;
|
|
103
|
+
if (apply) {
|
|
104
|
+
applyModels.push(apply);
|
|
105
|
+
}
|
|
90
106
|
}
|
|
91
|
-
|
|
92
|
-
|
|
107
|
+
}
|
|
108
|
+
if (!applyModels.length)
|
|
109
|
+
return;
|
|
110
|
+
const modelsResolved = [];
|
|
111
|
+
let part = 0;
|
|
112
|
+
for (const model of applyModels) {
|
|
113
|
+
part++;
|
|
114
|
+
let variant = 0;
|
|
115
|
+
modelsResolved.push([]);
|
|
116
|
+
for (const varModel of Array.isArray(model) ? model : [model]) {
|
|
117
|
+
variant++;
|
|
118
|
+
this.resolvedModel = {
|
|
119
|
+
x: varModel.x,
|
|
120
|
+
y: varModel.y,
|
|
121
|
+
z: varModel.z,
|
|
122
|
+
uvlock: varModel.uvlock,
|
|
123
|
+
weight: varModel.weight,
|
|
124
|
+
};
|
|
125
|
+
if (!varModel)
|
|
126
|
+
continue;
|
|
127
|
+
this.getResolvedModelsByModel(varModel.model, queriedBlock.name + '-' + part + '-' + variant, false);
|
|
128
|
+
// if (!result || Object.keys(result).length === 0) continue // todo: maybe we should push null?
|
|
129
|
+
modelsResolved[modelsResolved.length - 1].push(this.resolvedModel);
|
|
130
|
+
if (!multiOptim && modelsResolved[modelsResolved.length - 1].length > 0)
|
|
131
|
+
break;
|
|
93
132
|
}
|
|
94
133
|
}
|
|
95
|
-
|
|
96
|
-
return 0;
|
|
97
|
-
// TODO losing x, y, uvlock, and not 0!
|
|
98
|
-
const model = (Array.isArray(modelApply) ? modelApply[0 /* Math.floor(Math.random() * modelApply.length) */] : modelApply).model;
|
|
99
|
-
return this.getResolvedModelByModelName(model, queriedBlock.name)?.elementsOptimized ?? 0;
|
|
134
|
+
return modelsResolved; // todo figure out the type error
|
|
100
135
|
}
|
|
101
|
-
|
|
136
|
+
getResolvedModelsByModel(model, debugQueryName, clearModel = true) {
|
|
102
137
|
if (clearModel) {
|
|
103
138
|
this.resolvedModel = {};
|
|
104
139
|
}
|
|
105
140
|
const modelData = this.blockModelsStore.get(this.version, model);
|
|
106
141
|
if (!modelData)
|
|
107
142
|
return;
|
|
108
|
-
// let textures = {} as Record<string, string>
|
|
109
|
-
let elements = [];
|
|
110
143
|
const resolveModel = (model) => {
|
|
111
144
|
if (model.ambientocclusion !== undefined) {
|
|
112
145
|
this.resolvedModel.ao = model.ambientocclusion;
|
|
@@ -114,21 +147,11 @@ export class AssetsParser {
|
|
|
114
147
|
if (model.ao !== undefined) {
|
|
115
148
|
this.resolvedModel.ao = model.ao;
|
|
116
149
|
}
|
|
117
|
-
if (model.x !== undefined) {
|
|
118
|
-
this.resolvedModel.x = model.x;
|
|
119
|
-
}
|
|
120
|
-
if (model.y !== undefined) {
|
|
121
|
-
this.resolvedModel.y = model.y;
|
|
122
|
-
}
|
|
123
|
-
if (model.z !== undefined) {
|
|
124
|
-
this.resolvedModel.z = model.z;
|
|
125
|
-
}
|
|
126
150
|
if (model.textures) {
|
|
127
151
|
this.resolvedModel.textures ??= {};
|
|
128
152
|
Object.assign(this.resolvedModel.textures, model.textures);
|
|
129
153
|
}
|
|
130
154
|
if (model.elements) {
|
|
131
|
-
elements.push(...model.elements.map(({ from, to }) => [from, to]));
|
|
132
155
|
this.resolvedModel.elements ??= [];
|
|
133
156
|
this.resolvedModel.elements.push(...structuredClone(model.elements));
|
|
134
157
|
}
|
|
@@ -174,21 +197,18 @@ export class AssetsParser {
|
|
|
174
197
|
face.texture = this.resolvedModel.textures[face.texture.replace('#', '')] ?? face.texture;
|
|
175
198
|
}
|
|
176
199
|
}
|
|
177
|
-
// todo cleanup methods
|
|
178
200
|
return {
|
|
179
|
-
elementsOptimized: elements.length === 1 && arrEq(elements[0][0], [0, 0, 0]) && arrEq(elements[0][1], [16, 16, 16]) ? 1 : elements,
|
|
180
201
|
resolvedModel: this.resolvedModel,
|
|
181
202
|
};
|
|
182
203
|
}
|
|
183
|
-
|
|
184
|
-
this.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
};
|
|
204
|
+
getResolvedModelFirst(queriedBlock, fallbackVariant = false) {
|
|
205
|
+
return this.getModelsByBlock(queriedBlock, fallbackVariant, false)?.map(x => x[0]);
|
|
206
|
+
}
|
|
207
|
+
getResolvedModelRandom(queriedBlock, fallbackVariant = false) {
|
|
208
|
+
return this.getModelsByBlock(queriedBlock, fallbackVariant, false)?.map(x => x[Math.floor(Math.random() * x.length)]);
|
|
209
|
+
}
|
|
210
|
+
getAllResolvedModels(queriedBlock, fallbackVariant = false) {
|
|
211
|
+
return this.getModelsByBlock(queriedBlock, fallbackVariant, true);
|
|
192
212
|
}
|
|
193
213
|
}
|
|
194
214
|
const arrEq = (a, b) => !!a && !!b && a.length === b.length && a.every((v, i) => v === b[i]);
|