mc-assets 0.2.35 → 0.2.36
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 +1 -1
- package/dist/atlasCreator.d.ts +1 -0
- package/dist/atlasCreator.js +57 -58
- package/dist/atlasParser.d.ts +1 -0
- package/dist/atlasParser.js +107 -1
- package/dist/blocksAtlasLatest.png +0 -0
- package/dist/blocksAtlasLegacy.png +0 -0
- package/dist/blocksAtlases.json +6764 -6774
- package/dist/itemsAtlasLatest.png +0 -0
- package/dist/itemsAtlasLegacy.png +0 -0
- package/dist/itemsAtlases.json +4671 -4681
- package/dist/itemsRenderer.test.js +41 -76
- package/dist/particlesAtlasLatest.png +0 -0
- package/dist/particlesAtlasLegacy.png +0 -0
- package/dist/particlesAtlases.json +672 -682
- package/dist/worldBlockProvider.d.ts +2 -2
- package/package.json +2 -1
package/README.MD
CHANGED
|
@@ -28,7 +28,7 @@ For contributing & building instructions see [building](#building) section.
|
|
|
28
28
|
> Tested on Node.js 18 and above.
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
All blockstates + models + all atlas textures for all versions bundled with rsbuild (uncompressed): 5.
|
|
31
|
+
All blockstates + models + all atlas textures for all versions bundled with rsbuild (uncompressed): 5.15 MB.
|
|
32
32
|
|
|
33
33
|
This packages includes versions for: 1.7.10, 1.8, 1.8.1, 1.8.2, 1.8.3, 1.8.4, 1.8.5, 1.8.6, 1.8.7, 1.8.8, 1.8.9, 1.9, 1.9.1, 1.9.2, 1.9.3, 1.9.4, 1.10, 1.10.1, 1.10.2, 1.11, 1.11.1, 1.11.2, 1.12, 1.12.1, 1.12.2, 1.13, 1.13.1, 1.13.2, 1.14, 1.14.1, 1.14.2, 1.14.3, 1.14.4, 1.15, 1.15.1, 1.15.2, 1.16, 1.16.1, 1.16.2, 1.16.3, 1.16.4, 1.16.5, 1.17, 1.17.1, 1.18, 1.18.1, 1.18.2, 1.19, 1.19.1, 1.19.2, 1.19.3, 1.19.4, 1.20, 1.20.1, 1.20.2, 1.20.3, 1.20.4, 1.20.5, 1.20.6, 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4.
|
|
34
34
|
|
package/dist/atlasCreator.d.ts
CHANGED
package/dist/atlasCreator.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createAtlas } from 'apl-image-packer';
|
|
2
|
+
export const MAX_CANVAS_SIZE = 16_384;
|
|
1
3
|
function nextPowerOfTwo(n) {
|
|
2
4
|
if (n === 0)
|
|
3
5
|
return 1;
|
|
@@ -17,30 +19,8 @@ export const getAtlasSize = (numberOfTiles, tileSize) => {
|
|
|
17
19
|
};
|
|
18
20
|
};
|
|
19
21
|
export const makeTextureAtlas = ({ input, getLoadedImage, tileSize = 16, getCanvas = (imgSize) => typeof document !== 'undefined' && document.createElement ? document.createElement('canvas') : new globalThis.Canvas(imgSize, imgSize, 'png'), }) => {
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
const MAX_CANVAS_SIZE = 16_384;
|
|
23
|
-
if (imgSize > MAX_CANVAS_SIZE) {
|
|
24
|
-
throw new Error(`Image resolution ${imgSize} is too big, max is ${MAX_CANVAS_SIZE}x${MAX_CANVAS_SIZE}`);
|
|
25
|
-
}
|
|
26
|
-
const canvas = getCanvas(imgSize);
|
|
27
|
-
canvas.width = imgSize;
|
|
28
|
-
canvas.height = imgSize;
|
|
29
|
-
const g = canvas.getContext('2d');
|
|
30
|
-
g.imageSmoothingEnabled = false;
|
|
31
|
-
const texturesIndex = {};
|
|
32
|
-
let nextX = 0;
|
|
33
|
-
let nextY = 0;
|
|
34
|
-
let rowMaxY = 0;
|
|
35
|
-
const goToNextRow = () => {
|
|
36
|
-
nextX = 0;
|
|
37
|
-
nextY += Math.ceil(rowMaxY / tileSize) * tileSize;
|
|
38
|
-
rowMaxY = 0;
|
|
39
|
-
};
|
|
40
|
-
const suSv = tileSize / imgSize;
|
|
41
|
-
const tilesPerRow = Math.ceil(imgSize / tileSize);
|
|
42
|
-
for (const i in input) {
|
|
43
|
-
const keyValue = input[i];
|
|
22
|
+
// Pre-calculate all texture dimensions and prepare images
|
|
23
|
+
const texturesWithDimensions = input.map(keyValue => {
|
|
44
24
|
const inputData = getLoadedImage(keyValue);
|
|
45
25
|
let img;
|
|
46
26
|
if (inputData.image) {
|
|
@@ -53,57 +33,76 @@ export const makeTextureAtlas = ({ input, getLoadedImage, tileSize = 16, getCanv
|
|
|
53
33
|
else {
|
|
54
34
|
throw new Error('No image or contents');
|
|
55
35
|
}
|
|
56
|
-
let su = suSv;
|
|
57
|
-
let sv = suSv;
|
|
58
36
|
let renderWidth = tileSize * (inputData.tileWidthMult ?? 1);
|
|
59
37
|
let renderHeight = tileSize;
|
|
60
38
|
if (inputData.useOriginalSize || inputData.renderWidth || inputData.renderHeight) {
|
|
61
39
|
const texWidth = inputData.renderWidth ?? img.width;
|
|
62
40
|
const texHeight = inputData.renderHeight ?? img.height;
|
|
63
|
-
// todo check have enough space
|
|
64
41
|
renderWidth = Math.ceil(texWidth / tileSize) * tileSize;
|
|
65
42
|
renderHeight = Math.ceil(texHeight / tileSize) * tileSize;
|
|
66
|
-
su = texWidth / imgSize;
|
|
67
|
-
sv = texHeight / imgSize;
|
|
68
|
-
// renderWidth and renderHeight take full tile size so everything is aligned to the grid
|
|
69
|
-
if (renderHeight > imgSize || renderWidth > imgSize) {
|
|
70
|
-
throw new Error('Texture ' + keyValue + ' is too big');
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (nextX + renderWidth > imgSize) {
|
|
74
|
-
goToNextRow();
|
|
75
43
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
44
|
+
return {
|
|
45
|
+
keyValue,
|
|
46
|
+
img,
|
|
47
|
+
inputData,
|
|
48
|
+
renderWidth,
|
|
49
|
+
renderHeight,
|
|
50
|
+
renderSourceWidth: inputData.useOriginalSize ? img.width : inputData.renderSourceWidth ?? Math.min(img.width, img.height),
|
|
51
|
+
renderSourceHeight: inputData.useOriginalSize ? img.height : inputData.renderSourceHeight ?? Math.min(img.width, img.height),
|
|
52
|
+
renderSourceStartX: inputData.renderSourceStartX ?? 0,
|
|
53
|
+
renderSourceStartY: inputData.renderSourceStartY ?? 0,
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
// Use apl-image-packer to calculate optimal positions
|
|
57
|
+
const atlas = createAtlas(texturesWithDimensions.map(tex => ({
|
|
58
|
+
width: tex.renderWidth,
|
|
59
|
+
height: tex.renderHeight,
|
|
60
|
+
data: tex // Store all texture data for later use
|
|
61
|
+
})));
|
|
62
|
+
// Round up atlas size to power of 2
|
|
63
|
+
const imgSize = Math.max(nextPowerOfTwo(atlas.width), nextPowerOfTwo(atlas.height));
|
|
64
|
+
if (imgSize > MAX_CANVAS_SIZE) {
|
|
65
|
+
const sizeGroups = texturesWithDimensions.reduce((acc, t) => {
|
|
66
|
+
const key = `${t.renderWidth}x${t.renderHeight}`;
|
|
67
|
+
acc[key] = (acc[key] || 0) + 1;
|
|
68
|
+
return acc;
|
|
69
|
+
}, {});
|
|
70
|
+
const sizeGroupsStr = Object.entries(sizeGroups)
|
|
71
|
+
.sort(([, a], [, b]) => b - a)
|
|
72
|
+
.map(([size, count]) => `${size}(${count})`)
|
|
73
|
+
.join(', ');
|
|
74
|
+
throw new Error(`Required atlas size ${imgSize} exceeds maximum ${MAX_CANVAS_SIZE}. Texture sizes: ${sizeGroupsStr}`);
|
|
75
|
+
}
|
|
76
|
+
const canvas = getCanvas(imgSize);
|
|
77
|
+
canvas.width = imgSize;
|
|
78
|
+
canvas.height = imgSize;
|
|
79
|
+
const g = canvas.getContext('2d');
|
|
80
|
+
g.imageSmoothingEnabled = false;
|
|
81
|
+
const texturesIndex = {};
|
|
82
|
+
const suSv = tileSize / imgSize;
|
|
83
|
+
const tilesPerRow = Math.ceil(imgSize / tileSize);
|
|
84
|
+
// Draw textures at their calculated positions
|
|
85
|
+
for (const coord of atlas.coords) {
|
|
86
|
+
const tex = coord.img.data;
|
|
87
|
+
const x = coord.x;
|
|
88
|
+
const y = coord.y;
|
|
89
|
+
const yIndex = Math.floor(y / tileSize);
|
|
90
|
+
const xIndex = Math.floor(x / tileSize);
|
|
80
91
|
const tileIndex = yIndex * tilesPerRow + xIndex;
|
|
81
|
-
nextX += renderWidth;
|
|
82
|
-
rowMaxY = Math.max(rowMaxY, renderHeight);
|
|
83
|
-
if (nextX >= imgSize) {
|
|
84
|
-
goToNextRow();
|
|
85
|
-
}
|
|
86
|
-
const renderSourceDefaultSize = Math.min(img.width, img.height);
|
|
87
|
-
const renderSourceWidth = inputData.useOriginalSize ? img.width : inputData.renderSourceWidth ?? renderSourceDefaultSize;
|
|
88
|
-
const renderSourceHeight = inputData.useOriginalSize ? img.height : inputData.renderSourceHeight ?? renderSourceDefaultSize;
|
|
89
92
|
try {
|
|
90
|
-
g.drawImage(img,
|
|
93
|
+
g.drawImage(tex.img, tex.renderSourceStartX, tex.renderSourceStartY, tex.renderSourceWidth, tex.renderSourceHeight, x, y, tex.renderWidth, tex.renderHeight);
|
|
91
94
|
}
|
|
92
95
|
catch (err) {
|
|
93
|
-
throw new Error(`Error drawing ${keyValue}: ${err}`);
|
|
96
|
+
throw new Error(`Error drawing ${tex.keyValue}: ${err}`);
|
|
94
97
|
}
|
|
95
|
-
|
|
96
|
-
const
|
|
98
|
+
const cleanName = tex.keyValue.split('.').slice(0, -1).join('.') || tex.keyValue;
|
|
99
|
+
const su = tex.renderWidth / imgSize;
|
|
100
|
+
const sv = tex.renderHeight / imgSize;
|
|
97
101
|
texturesIndex[cleanName] = {
|
|
98
102
|
u: x / imgSize,
|
|
99
103
|
v: y / imgSize,
|
|
100
104
|
tileIndex,
|
|
101
|
-
...su == suSv && sv == suSv ? {} : {
|
|
102
|
-
su,
|
|
103
|
-
sv,
|
|
104
|
-
// width: renderWidth,
|
|
105
|
-
// height: renderHeight
|
|
106
|
-
}
|
|
105
|
+
...su == suSv && sv == suSv ? {} : { su, sv }
|
|
107
106
|
};
|
|
108
107
|
}
|
|
109
108
|
return {
|
package/dist/atlasParser.d.ts
CHANGED
package/dist/atlasParser.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { VersionedStore } from './versionedStore';
|
|
2
|
-
import { makeTextureAtlas } from './atlasCreator';
|
|
2
|
+
import { makeTextureAtlas, MAX_CANVAS_SIZE } from './atlasCreator';
|
|
3
3
|
import { getLoadedImage } from './utils';
|
|
4
4
|
export class AtlasParser {
|
|
5
5
|
atlasJson;
|
|
@@ -122,4 +122,110 @@ export class AtlasParser {
|
|
|
122
122
|
}
|
|
123
123
|
};
|
|
124
124
|
}
|
|
125
|
+
async createDebugImage(writeNames = false) {
|
|
126
|
+
const atlas = this.atlas.latest;
|
|
127
|
+
if (atlas.width !== atlas.height) {
|
|
128
|
+
throw new Error('Atlas must be square');
|
|
129
|
+
}
|
|
130
|
+
const wantedSize = Math.min(MAX_CANVAS_SIZE, atlas.width * (writeNames ? 6 : 1));
|
|
131
|
+
const scale = wantedSize / atlas.width;
|
|
132
|
+
const height = atlas.height * scale;
|
|
133
|
+
const width = atlas.width * scale;
|
|
134
|
+
const canvas = globalThis.Canvas ? new globalThis.Canvas(width, height) : document.createElement('canvas');
|
|
135
|
+
canvas.width = width;
|
|
136
|
+
canvas.height = height;
|
|
137
|
+
const ctx = canvas.getContext('2d');
|
|
138
|
+
// Disable image smoothing for pixelated rendering
|
|
139
|
+
ctx.imageSmoothingEnabled = false;
|
|
140
|
+
// For older browsers
|
|
141
|
+
//@ts-ignore
|
|
142
|
+
ctx.webkitImageSmoothingEnabled = false;
|
|
143
|
+
//@ts-ignore
|
|
144
|
+
ctx.mozImageSmoothingEnabled = false;
|
|
145
|
+
//@ts-ignore
|
|
146
|
+
ctx.msImageSmoothingEnabled = false;
|
|
147
|
+
// Draw the base atlas image
|
|
148
|
+
const img = await getLoadedImage(this.latestImage);
|
|
149
|
+
ctx.drawImage(img, 0, 0, atlas.width, atlas.height, 0, 0, width, height);
|
|
150
|
+
// Draw debug rectangles for each texture
|
|
151
|
+
ctx.strokeStyle = '#ff0000';
|
|
152
|
+
ctx.lineWidth = 2;
|
|
153
|
+
const textureNames = Object.keys(atlas.textures);
|
|
154
|
+
const totalTextures = textureNames.length;
|
|
155
|
+
let lastProgress = 0;
|
|
156
|
+
textureNames.forEach((textureName, i) => {
|
|
157
|
+
const texture = atlas.textures[textureName];
|
|
158
|
+
// Log progress every 10%
|
|
159
|
+
const progress = Math.floor((i / totalTextures) * 100);
|
|
160
|
+
if (progress >= lastProgress + 10) {
|
|
161
|
+
console.log(`Processing textures: ${progress}% (${i}/${totalTextures})`);
|
|
162
|
+
lastProgress = progress;
|
|
163
|
+
}
|
|
164
|
+
const x = texture.u * atlas.width * scale;
|
|
165
|
+
const y = texture.v * atlas.height * scale;
|
|
166
|
+
const width = (texture.su || atlas.suSv) * atlas.width * scale;
|
|
167
|
+
const height = (texture.sv || atlas.suSv) * atlas.height * scale;
|
|
168
|
+
// Create striped pattern
|
|
169
|
+
const pattern = ctx.createPattern((() => {
|
|
170
|
+
const patternCanvas = globalThis.Canvas ? new globalThis.Canvas(10, 10) : document.createElement('canvas');
|
|
171
|
+
patternCanvas.width = 10;
|
|
172
|
+
patternCanvas.height = 10;
|
|
173
|
+
const patternCtx = patternCanvas.getContext('2d');
|
|
174
|
+
patternCtx.fillStyle = '#ff0000';
|
|
175
|
+
patternCtx.fillRect(0, 0, 5, 10);
|
|
176
|
+
patternCtx.fillStyle = '#ffff00';
|
|
177
|
+
patternCtx.fillRect(5, 0, 5, 10);
|
|
178
|
+
return patternCanvas;
|
|
179
|
+
})(), 'repeat');
|
|
180
|
+
ctx.strokeStyle = pattern;
|
|
181
|
+
ctx.strokeRect(x, y, width, height);
|
|
182
|
+
if (writeNames) {
|
|
183
|
+
// Configure text style
|
|
184
|
+
const text = textureName;
|
|
185
|
+
const padding = 4;
|
|
186
|
+
const maxWidth = width - padding * 2;
|
|
187
|
+
// Start with a relatively large font size and decrease until text fits
|
|
188
|
+
let fontSize = 12;
|
|
189
|
+
do {
|
|
190
|
+
ctx.font = `${fontSize}px monospace`;
|
|
191
|
+
const metrics = ctx.measureText(text);
|
|
192
|
+
if (metrics.width <= maxWidth || fontSize <= 6) {
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
fontSize -= 1;
|
|
196
|
+
} while (fontSize > 6);
|
|
197
|
+
ctx.fillStyle = 'white';
|
|
198
|
+
ctx.strokeStyle = 'black';
|
|
199
|
+
ctx.lineWidth = Math.max(1, fontSize / 6); // Scale outline with font size
|
|
200
|
+
ctx.textBaseline = 'top';
|
|
201
|
+
// Draw text with outline for better visibility
|
|
202
|
+
const textX = x + padding;
|
|
203
|
+
const textY = y + padding;
|
|
204
|
+
// Split text into lines if it's still too wide
|
|
205
|
+
const words = text.split(/(?=[A-Z_/])/g);
|
|
206
|
+
let line = '';
|
|
207
|
+
let lines = [];
|
|
208
|
+
for (const word of words) {
|
|
209
|
+
const testLine = line + word;
|
|
210
|
+
const metrics = ctx.measureText(testLine);
|
|
211
|
+
if (metrics.width > maxWidth && line !== '') {
|
|
212
|
+
lines.push(line);
|
|
213
|
+
line = word;
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
line = testLine;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
lines.push(line);
|
|
220
|
+
// Draw each line
|
|
221
|
+
const lineHeight = fontSize * 1.2;
|
|
222
|
+
lines.forEach((line, i) => {
|
|
223
|
+
ctx.strokeText(line, textX, textY + i * lineHeight);
|
|
224
|
+
ctx.fillText(line, textX, textY + i * lineHeight);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
console.log(`Processing textures: 100% (${totalTextures}/${totalTextures})`);
|
|
229
|
+
return canvas.toDataURL();
|
|
230
|
+
}
|
|
125
231
|
}
|
|
Binary file
|
|
Binary file
|