minerender 2.1.0-alpha.3 → 2.1.0-alpha.31

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.
@@ -23,14 +23,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
24
  mod
25
25
  ));
26
- var __decorateClass = (decorators, target, key, kind) => {
27
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
28
- for (var i = decorators.length - 1, decorator; i >= 0; i--)
29
- if (decorator = decorators[i])
30
- result = (kind ? decorator(target, key, result) : decorator(result)) || result;
31
- if (kind && result) __defProp(target, key, result);
32
- return result;
33
- };
34
26
 
35
27
  // ../../node_modules/ts-deepmerge/dist/index.js
36
28
  var require_dist = __commonJS({
@@ -40,8 +32,8 @@ var require_dist = __commonJS({
40
32
  __assign = Object.assign || function(t) {
41
33
  for (var s, i = 1, n = arguments.length; i < n; i++) {
42
34
  s = arguments[i];
43
- for (var p13 in s) if (Object.prototype.hasOwnProperty.call(s, p13))
44
- t[p13] = s[p13];
35
+ for (var p12 in s) if (Object.prototype.hasOwnProperty.call(s, p12))
36
+ t[p12] = s[p12];
45
37
  }
46
38
  return t;
47
39
  };
@@ -85,7 +77,7 @@ var require_dist = __commonJS({
85
77
  return false;
86
78
  };
87
79
  var PROTECTED_KEYS = ["__proto__"];
88
- var merge11 = function() {
80
+ var merge10 = function() {
89
81
  var objects = [];
90
82
  for (var _i = 0; _i < arguments.length; _i++) {
91
83
  objects[_i] = arguments[_i];
@@ -96,9 +88,9 @@ var require_dist = __commonJS({
96
88
  return;
97
89
  }
98
90
  if (Array.isArray(result[key]) && Array.isArray(current[key])) {
99
- result[key] = merge11.options.mergeArrays ? Array.from(new Set(result[key].concat(current[key]))) : current[key];
91
+ result[key] = merge10.options.mergeArrays ? Array.from(new Set(result[key].concat(current[key]))) : current[key];
100
92
  } else if (isObject(result[key]) && isObject(current[key])) {
101
- result[key] = merge11(result[key], current[key]);
93
+ result[key] = merge10(result[key], current[key]);
102
94
  } else {
103
95
  result[key] = current[key];
104
96
  }
@@ -109,18 +101,18 @@ var require_dist = __commonJS({
109
101
  var defaultOptions = {
110
102
  mergeArrays: true
111
103
  };
112
- merge11.options = defaultOptions;
113
- merge11.withOptions = function(options) {
104
+ merge10.options = defaultOptions;
105
+ merge10.withOptions = function(options) {
114
106
  var objects = [];
115
107
  for (var _i = 1; _i < arguments.length; _i++) {
116
108
  objects[_i - 1] = arguments[_i];
117
109
  }
118
- merge11.options = __assign({ mergeArrays: true }, options);
119
- var result = merge11.apply(void 0, __spreadArray([], __read(objects), false));
120
- merge11.options = defaultOptions;
110
+ merge10.options = __assign({ mergeArrays: true }, options);
111
+ var result = merge10.apply(void 0, __spreadArray([], __read(objects), false));
112
+ merge10.options = defaultOptions;
121
113
  return result;
122
114
  };
123
- exports.default = merge11;
115
+ exports.default = merge10;
124
116
  }
125
117
  });
126
118
 
@@ -154,17 +146,20 @@ var _PersistentCache = class _PersistentCache {
154
146
  static open(name) {
155
147
  return Env.provider.openCache(name, _PersistentCache.VERSION);
156
148
  }
149
+ /** Loads a missing entry and stores only non-nullish results. */
157
150
  async getOrLoad(key, loader) {
158
151
  let v = await this.get(key);
159
152
  if (typeof v === "undefined" || v === null) {
160
153
  v = await loader(key);
161
- await this.put(key, v);
154
+ if (typeof v !== "undefined" && v !== null) {
155
+ await this.put(key, v);
156
+ }
162
157
  }
163
158
  return v;
164
159
  }
165
160
  };
166
161
  /** Bump to invalidate every persisted entry. */
167
- _PersistentCache.VERSION = 1;
162
+ _PersistentCache.VERSION = 2;
168
163
  var PersistentCache = _PersistentCache;
169
164
 
170
165
  // src/env/browser/BrowserCache.ts
@@ -265,7 +260,8 @@ var BrowserEnv = class {
265
260
  }
266
261
  openCache(name, version) {
267
262
  return new BrowserCache(localforage.createInstance({
268
- name,
263
+ // IndexedDB schema upgrades retain records; each cache generation needs its own database.
264
+ name: name + version,
269
265
  version
270
266
  }));
271
267
  }
@@ -282,7 +278,7 @@ registerBrowserEnv();
282
278
  // src/Axis.ts
283
279
  import { Vector3 } from "three";
284
280
  var Axis = /* @__PURE__ */ ((Axis3) => {
285
- Axis3["X"] = "X";
281
+ Axis3["X"] = "x";
286
282
  Axis3["Y"] = "y";
287
283
  Axis3["Z"] = "z";
288
284
  return Axis3;
@@ -290,7 +286,7 @@ var Axis = /* @__PURE__ */ ((Axis3) => {
290
286
  var AXES = Object.values(Axis);
291
287
  function axisToVec3(axis) {
292
288
  switch (axis) {
293
- case "X" /* X */:
289
+ case "x" /* X */:
294
290
  return new Vector3(1, 0, 0);
295
291
  case "y" /* Y */:
296
292
  return new Vector3(0, 1, 0);
@@ -393,7 +389,10 @@ function serializeMeshKey(key) {
393
389
  // src/cache/Caching.ts
394
390
  import { Caches } from "@inventivetalent/loading-cache";
395
391
  import { Time } from "@inventivetalent/time";
396
- var Caching = class {
392
+ var _Caching = class _Caching {
393
+ static createAssetCache() {
394
+ return Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
395
+ }
397
396
  /** Every cache above, so clear()/end() can never fall out of sync with the field list again. */
398
397
  static get all() {
399
398
  return [
@@ -410,7 +409,12 @@ var Caching = class {
410
409
  this.rawModelCache,
411
410
  this.mergedModelCache,
412
411
  this.modelTextureAtlasCache,
413
- this.blockStateCache
412
+ this.blockStateCache,
413
+ this.listAssetCache,
414
+ this.defaultBlockStatesCache,
415
+ this.blockTintCache,
416
+ this.entityModelCache,
417
+ this.entityTextureCache
414
418
  ];
415
419
  }
416
420
  static clear() {
@@ -421,8 +425,8 @@ var Caching = class {
421
425
  /**
422
426
  * Clears every cache and stops its expiry timer.
423
427
  *
424
- * loading-cache reschedules that timer indefinitely, so a Node process will not exit until
425
- * this is called - see {@link shutdown}.
428
+ * Expiry timers are unref'd in Node, so idle processes can exit without this cleanup.
429
+ * Use {@link shutdown} to also stop requests and the shared Ticker.
426
430
  */
427
431
  static end() {
428
432
  for (const cache of this.all) {
@@ -436,20 +440,26 @@ var Caching = class {
436
440
  };
437
441
  }
438
442
  };
439
- Caching.rawImageCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(5)).buildAsync();
440
- Caching.imageDataCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(10)).buildAsync();
441
- Caching.canvasImageDataCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(10)).buildAsync();
442
- Caching.wrappedImageCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(10)).buildAsync();
443
- Caching.boxGeometryCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(20)).build();
444
- Caching.textureCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).build();
445
- Caching.materialCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).build();
446
- Caching.boxMeshCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).build();
447
- Caching.textureAssetCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
448
- Caching.textureMetaCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
449
- Caching.rawModelCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
450
- Caching.mergedModelCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
451
- Caching.modelTextureAtlasCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
452
- Caching.blockStateCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
443
+ _Caching.rawImageCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(5)).buildAsync();
444
+ _Caching.imageDataCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(10)).buildAsync();
445
+ _Caching.canvasImageDataCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(10)).buildAsync();
446
+ _Caching.wrappedImageCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(10)).buildAsync();
447
+ _Caching.boxGeometryCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(20)).build();
448
+ _Caching.textureCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).build();
449
+ _Caching.materialCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).build();
450
+ _Caching.boxMeshCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).build();
451
+ _Caching.textureAssetCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
452
+ _Caching.textureMetaCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
453
+ _Caching.rawModelCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
454
+ _Caching.mergedModelCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
455
+ _Caching.modelTextureAtlasCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
456
+ _Caching.blockStateCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
457
+ _Caching.listAssetCache = _Caching.createAssetCache();
458
+ _Caching.defaultBlockStatesCache = _Caching.createAssetCache();
459
+ _Caching.blockTintCache = _Caching.createAssetCache();
460
+ _Caching.entityModelCache = _Caching.createAssetCache();
461
+ _Caching.entityTextureCache = _Caching.createAssetCache();
462
+ var Caching = _Caching;
453
463
 
454
464
  // src/Geometries.ts
455
465
  var Geometries = class _Geometries {
@@ -477,6 +487,7 @@ import { CanvasTexture, Texture as Texture2 } from "three";
477
487
 
478
488
  // src/texture/TextureLoader.ts
479
489
  import { RGBAFormat, Texture } from "three";
490
+ import * as THREE from "three";
480
491
 
481
492
  // src/canvas/CanvasCompat.ts
482
493
  function createImage(width, height) {
@@ -487,9 +498,7 @@ function createCanvas(width, height) {
487
498
  }
488
499
 
489
500
  // src/request/Requests.ts
490
- import axios from "axios";
491
- import { JobQueue } from "jobqu";
492
- import { Time as Time2 } from "@inventivetalent/time";
501
+ import { JobCancelledError, JobQueue } from "jobqu";
493
502
 
494
503
  // src/util/log.ts
495
504
  var Reset = "\x1B[0m";
@@ -499,198 +508,170 @@ function prefix(prefix2) {
499
508
  return `${FgGreen}MR${BgBlack}${prefix2 ?? ""}${Reset}`;
500
509
  }
501
510
 
502
- // src/util/BatchedExecutor.ts
503
- var BatchedExecutor = class {
504
- constructor(interval = 1, batch = 30) {
505
- this.interval = interval;
506
- this.batch = batch;
507
- this.queue = [];
508
- this.task = setInterval(() => this.run());
509
- }
510
- submit(task) {
511
- this.queue.push(task);
512
- }
513
- run() {
514
- for (let i = 0; i < this.batch; i++) {
515
- this.runNext();
516
- }
517
- }
518
- runNext() {
519
- let next = this.queue.shift();
520
- if (next) next();
511
+ // src/request/Requests.ts
512
+ var p = prefix("Requests");
513
+ var RequestError = class extends Error {
514
+ constructor(message, response, cause) {
515
+ super(message);
516
+ this.response = response;
517
+ this.cause = cause;
518
+ this.name = "RequestError";
521
519
  }
522
520
  };
523
-
524
- // src/util/model.ts
525
- import { AxesHelper, BoxGeometry as BoxGeometry2, EdgesGeometry, LineBasicMaterial, LineSegments, Vector3 as Vector32 } from "three";
526
- function applyElementRotation(rotation, geometry) {
527
- const origin = new Vector32(rotation.origin[0], rotation.origin[1], rotation.origin[2]);
528
- geometry.translate(-origin.x, -origin.y, -origin.z);
529
- switch (rotation.axis) {
530
- case "X" /* X */:
531
- geometry.rotateX(toRadians(rotation.angle));
532
- break;
533
- case "y" /* Y */:
534
- geometry.rotateY(toRadians(rotation.angle));
535
- break;
536
- case "z" /* Z */:
537
- geometry.rotateZ(toRadians(rotation.angle));
538
- break;
539
- }
540
- if (rotation.rescale) {
541
- if (Math.abs(rotation.angle) === 22.5) {
542
- const s = 1 / Math.cos(0.39269909262657166);
543
- geometry.scale(s, 1, s);
544
- } else {
545
- const s = 1 / Math.cos(0.7853981852531433);
546
- geometry.scale(s, 1, s);
521
+ async function fetchRequest(config, defaultBaseURL) {
522
+ const { url, baseURL = defaultBaseURL, timeout = 5e3, responseType = "json", signal, ...options } = config;
523
+ const target = baseURL && !/^([a-z][a-z\d+.-]*:|\/\/)/i.test(url) ? `${baseURL.replace(/\/+$/, "")}/${url.replace(/^\/+/, "")}` : url;
524
+ const headers = new Headers(options.headers);
525
+ if (typeof window === "undefined" && !headers.has("User-Agent")) {
526
+ headers.set("User-Agent", "MineRender");
527
+ }
528
+ const attemptSignal = AbortSignal.any([
529
+ ...signal ? [signal] : [],
530
+ ...timeout === 0 ? [] : [AbortSignal.timeout(timeout)]
531
+ ]);
532
+ const request = new Request(target, { ...options, headers, signal: attemptSignal });
533
+ attemptSignal.throwIfAborted();
534
+ try {
535
+ const response = await fetch(request);
536
+ const result = {
537
+ data: void 0,
538
+ status: response.status,
539
+ statusText: response.statusText,
540
+ headers: response.headers,
541
+ url: response.url
542
+ };
543
+ if (!response.ok) {
544
+ await response.body?.cancel().catch(() => {
545
+ });
546
+ throw new RequestError(`Request failed with status ${response.status}`, result);
547
+ }
548
+ const emptyBody = request.method === "HEAD" || response.status === 204 || response.status === 205;
549
+ const data = responseType === "arraybuffer" ? await response.arrayBuffer() : emptyBody || response.body === null ? void 0 : await response.json();
550
+ return { ...result, data };
551
+ } catch (error) {
552
+ attemptSignal.throwIfAborted();
553
+ if (/^https?:/.test(request.url) && request.redirect !== "error" && error instanceof TypeError) {
554
+ throw new RequestError("Network request failed", void 0, error);
547
555
  }
556
+ throw error;
548
557
  }
549
- geometry.translate(origin.x, origin.y, origin.z);
550
- }
551
- function applyModelPartRotation(modelPart, geometry) {
552
- const origin = new Vector32(modelPart.pivotX, modelPart.pivotY, modelPart.pivotZ);
553
- geometry.translate(-origin.x, -origin.y, -origin.z);
554
- geometry.rotateX(toRadians(modelPart.pitch));
555
- geometry.rotateY(toRadians(modelPart.yaw));
556
- geometry.rotateZ(toRadians(modelPart.roll));
557
- geometry.translate(origin.x, origin.y, origin.z);
558
558
  }
559
- function applyGenericRotation(axis, rotation, obj) {
560
- switch (axis) {
561
- case "X" /* X */:
562
- obj.rotateOnAxis(new Vector32(1, 0, 0), toRadians(rotation));
563
- break;
564
- case "y" /* Y */:
565
- obj.rotateOnAxis(new Vector32(0, 1, 0), toRadians(rotation));
566
- break;
567
- case "z" /* Z */:
568
- obj.rotateOnAxis(new Vector32(0, 0, 1), toRadians(rotation));
569
- break;
559
+ async function abortable(promise, signal) {
560
+ if (!signal) return promise;
561
+ let abort;
562
+ const cancelled = new Promise((_, reject) => {
563
+ abort = () => reject(signal.reason);
564
+ signal.addEventListener("abort", abort, { once: true });
565
+ if (signal.aborted) abort();
566
+ });
567
+ try {
568
+ return await Promise.race([promise, cancelled]);
569
+ } finally {
570
+ signal.removeEventListener("abort", abort);
570
571
  }
571
572
  }
572
- function addBox3WireframeToObject(box, obj, color = 16777215, w = 2) {
573
- let size = new Vector32();
574
- box.getSize(size);
575
- let boxGeo = new BoxGeometry2(size.x, size.y, size.z, 1, 1, 1);
576
- let wireGeo = new EdgesGeometry(boxGeo);
577
- let wireMat = new LineBasicMaterial({ color, linewidth: w });
578
- let wireframe = new LineSegments(wireGeo, wireMat);
579
- obj.add(wireframe);
580
- let axes = new AxesHelper(1);
581
- obj.add(axes);
582
- }
583
- function addWireframeToObject(obj, color = 16777215, w = 2) {
584
- let boxGeo = new BoxGeometry2(16, 16, 16, 1, 1, 1);
585
- let wireGeo = new EdgesGeometry(boxGeo);
586
- let wireMat = new LineBasicMaterial({ color, linewidth: w });
587
- let wireframe = new LineSegments(wireGeo, wireMat);
588
- obj.add(wireframe);
589
- let axes = new AxesHelper(1);
590
- obj.add(axes);
591
- }
592
- function addWireframeToMesh(geo, mesh, color = 16777215, w = 2) {
593
- let wireGeo = new EdgesGeometry(geo);
594
- let wireMat = new LineBasicMaterial({ color, linewidth: w });
595
- let wireframe = new LineSegments(wireGeo, wireMat);
596
- mesh.add(wireframe);
597
- let axes = new AxesHelper(1);
598
- mesh.add(axes);
599
- }
600
-
601
- // src/util/performance.ts
602
- function captureException(e, ...extra) {
603
- console.error(e, extra);
604
- }
605
- function timingStart(marker) {
606
- console.time(marker);
607
- }
608
- function timingEnd(marker) {
609
- console.timeEnd(marker);
610
- }
611
-
612
- // src/util/three.ts
613
- function isObject3D(obj) {
614
- return obj.isObject3D;
615
- }
616
- function isMesh(obj) {
617
- return obj.isMesh;
618
- }
619
- function isInstancedMesh(obj) {
620
- return obj.isInstancedMesh;
621
- }
622
- function isPerspectiveCamera(obj) {
623
- return obj.isPerspectiveCamera;
624
- }
625
- function isOrthographicCamera(obj) {
626
- return obj.isOrthographicCamera;
627
- }
628
-
629
- // src/request/Requests.ts
630
- var p = prefix("Requests");
631
- if (typeof window === "undefined") {
632
- axios.defaults.headers["User-Agent"] = "MineRender";
633
- }
634
- var _Requests = class _Requests {
635
- static genericRequest(request) {
636
- return this.genericQueue.add(request).catch(async (e) => {
637
- console.debug(p, "generic catch", e.code, e);
638
- if (this.shouldRetry(e, request)) {
639
- await sleep(100);
640
- return this.genericRequest(request);
641
- }
642
- throw e;
643
- });
644
- }
645
- static mcAssetRequest(request) {
646
- return this.mcAssetRequestQueue.add(request).catch((e) => {
647
- console.debug(p, "mcAssetRequest catch", e.code, e);
648
- throw e;
649
- });
573
+ var RequestQueue = class {
574
+ constructor() {
575
+ this.pendingRequests = 0;
576
+ this.stopped = new AbortController();
577
+ this.queue = new JobQueue(
578
+ (request) => fetchRequest(request, this.baseURL),
579
+ { interval: 10, maxPerRun: 1, maxActive: 8 }
580
+ );
650
581
  }
651
- static shouldRetry(e, request) {
652
- if ("code" in e) {
653
- if ("ECONNABORTED" === e.code) {
654
- if ("minerenderRetry" in request) {
655
- if (request["minerenderRetry"] > 0) {
656
- request["minerenderRetry"]--;
657
- return true;
582
+ async request(request) {
583
+ this.pendingRequests++;
584
+ const signal = request.signal;
585
+ try {
586
+ for (let retries = 0; ; retries++) {
587
+ signal?.throwIfAborted();
588
+ try {
589
+ return await abortable(this.queue.add(request), signal);
590
+ } catch (error) {
591
+ console.debug(p, "Request failed", error);
592
+ const delay = this.retryDelay(error, request, retries);
593
+ if (delay === void 0) {
594
+ throw error;
658
595
  }
659
- } else {
660
- request["minerenderRetry"] = 3;
661
- return true;
596
+ await this.waitForRetry(delay, signal);
662
597
  }
663
598
  }
599
+ } finally {
600
+ if (signal?.aborted) {
601
+ this.queue.remove(request);
602
+ }
603
+ this.pendingRequests--;
664
604
  }
665
- return false;
605
+ }
606
+ retryDelay(error, request, retries) {
607
+ if (this.stopped.signal.aborted || retries >= 3 || (request.method ?? "get").toLowerCase() !== "get" || request.signal?.aborted) {
608
+ return void 0;
609
+ }
610
+ if (error instanceof RequestError) {
611
+ const status = error.response?.status;
612
+ if (status !== void 0 && status !== 408 && status !== 429 && !(status >= 500 && status < 600)) return void 0;
613
+ } else if (!(error instanceof DOMException && error.name === "TimeoutError")) {
614
+ return void 0;
615
+ }
616
+ let delay = 100 * 2 ** retries;
617
+ const retryAfter = error instanceof RequestError ? error.response?.headers.get("Retry-After") : void 0;
618
+ if (retryAfter !== void 0 && retryAfter !== null) {
619
+ const seconds = Number(retryAfter);
620
+ if (!retryAfter.trim() || !Number.isFinite(seconds) || seconds < 0) {
621
+ return void 0;
622
+ }
623
+ delay = Math.max(delay, seconds * 1e3);
624
+ }
625
+ return delay;
626
+ }
627
+ async waitForRetry(delay, signal) {
628
+ let timer;
629
+ const waiting = new Promise((resolve) => {
630
+ timer = setTimeout(resolve, delay);
631
+ });
632
+ try {
633
+ await abortable(waiting, AbortSignal.any([this.stopped.signal, ...signal ? [signal] : []]));
634
+ } finally {
635
+ clearTimeout(timer);
636
+ }
637
+ }
638
+ get pendingSize() {
639
+ return this.pendingRequests;
640
+ }
641
+ end() {
642
+ this.queue.end();
643
+ this.stopped.abort(new JobCancelledError("ended"));
644
+ }
645
+ };
646
+ var Requests = class {
647
+ static genericRequest(request) {
648
+ return this.genericQueue.request(request);
649
+ }
650
+ static mcAssetRequest(request) {
651
+ return this.mcAssetRequestQueue.request(request);
666
652
  }
667
653
  static setMcAssetRoot(root) {
668
- this.mcAssetInstance.defaults.baseURL = root;
654
+ this.mcAssetRequestQueue.baseURL = root;
669
655
  }
656
+ /** Counts unsettled calls, including retry delays and requests still running after shutdown. */
670
657
  static get queueSizes() {
671
658
  return {
672
- generic: this.genericQueue.size,
673
- mcAsset: this.mcAssetRequestQueue.size
659
+ generic: this.genericQueue.pendingSize,
660
+ mcAsset: this.mcAssetRequestQueue.pendingSize
674
661
  };
675
662
  }
676
663
  /**
677
- * Stops both queue timers. jobqu reschedules itself indefinitely, so a Node process will not
678
- * exit until this is called - see {@link shutdown}.
664
+ * Permanently stops both queues and rejects waiting, retrying, and future requests.
665
+ * Requests already running can finish. Idle queues hold no timer, so Node can exit without
666
+ * this cleanup. See {@link shutdown}.
679
667
  */
680
668
  static end() {
681
669
  this.genericQueue.end();
682
670
  this.mcAssetRequestQueue.end();
683
671
  }
684
672
  };
685
- _Requests.axiosInstance = axios.create({
686
- timeout: 5e3
687
- });
688
- _Requests.mcAssetInstance = axios.create({
689
- timeout: 5e3
690
- });
691
- _Requests.genericQueue = new JobQueue((request) => _Requests.axiosInstance.request(request), Time2.millis(10), 1);
692
- _Requests.mcAssetRequestQueue = new JobQueue((request) => _Requests.mcAssetInstance.request(request), Time2.millis(10), 1);
693
- var Requests = _Requests;
673
+ Requests.genericQueue = new RequestQueue();
674
+ Requests.mcAssetRequestQueue = new RequestQueue();
694
675
 
695
676
  // src/WrappedImage.ts
696
677
  var WrappedImage = class {
@@ -728,10 +709,10 @@ var WrappedImage = class {
728
709
  return this.animated ? this.width : this.height;
729
710
  }
730
711
  get frameCount() {
731
- return this.height / this.width;
712
+ return this.height / this.frameHeight;
732
713
  }
733
714
  getFrameY(frame) {
734
- return this.height / this.frameCount * Math.max(0, Math.min(this.frameCount, frame));
715
+ return this.frameHeight * Math.max(0, Math.min(this.frameCount - 1, frame));
735
716
  }
736
717
  getSectionData(sx, sy, sw, sh) {
737
718
  return this.dta.data.getImageData(sx, sy, sw, sh);
@@ -741,7 +722,7 @@ var WrappedImage = class {
741
722
  }
742
723
  getFrameSectionData(frame) {
743
724
  const y = this.getFrameY(frame);
744
- return this.getSectionData(0, y, this.width, this.width + y);
725
+ return this.getSectionData(0, y, this.frameWidth, this.frameHeight);
745
726
  }
746
727
  };
747
728
 
@@ -755,29 +736,41 @@ var ImageLoader = class _ImageLoader {
755
736
  static async loadAsync(src) {
756
737
  return new Promise((resolve, reject) => {
757
738
  const image = this._createImage();
758
- image.src = src;
759
739
  image.onload = () => resolve(image);
760
- image.onerror = (err) => reject(err);
740
+ image.onerror = (err) => reject(err instanceof Error ? err : new Error("Failed to decode image"));
741
+ image.src = src;
761
742
  });
762
743
  }
763
744
  static loadElement(src, onload, onerr) {
764
745
  const image = this._createImage();
765
- image.src = src;
766
746
  if (onload)
767
747
  image.onload = onload;
768
748
  if (onerr)
769
749
  image.onerror = onerr;
750
+ image.src = src;
770
751
  return image;
771
752
  }
772
753
  static async loadData(src) {
773
754
  console.debug(p2, "loadData", src);
774
- return await this.infoToData(await this.getInfo(src));
755
+ const image = await this.loadCanvasData(src);
756
+ return image.data.getImageData(0, 0, image.width, image.height);
775
757
  }
776
758
  static async loadCanvasData(src) {
777
- return await this.infoToCanvasData(await this.getInfo(src));
759
+ const keyStr = serializeImageKey({ src });
760
+ const info = this.getInfo(src);
761
+ const cached = Caching.rawImageCache.getIfPresent(keyStr);
762
+ try {
763
+ return await this.infoToCanvasData(await info);
764
+ } catch (err) {
765
+ if (cached && Caching.rawImageCache.getIfPresent(keyStr) === cached) {
766
+ Caching.rawImageCache.invalidate(keyStr);
767
+ }
768
+ throw err;
769
+ }
778
770
  }
779
771
  static async infoToCanvasData(info) {
780
- const image = await _ImageLoader.loadAsync(info.src);
772
+ const type = info.type === "jpg" ? "jpeg" : info.type ?? "png";
773
+ const image = await _ImageLoader.loadAsync(`data:image/${type};base64,${info.data.toString("base64")}`);
781
774
  const canvas = createCanvas(info.width, info.height);
782
775
  const context = canvas.getContext("2d");
783
776
  context.drawImage(image, 0, 0);
@@ -788,11 +781,8 @@ var ImageLoader = class _ImageLoader {
788
781
  };
789
782
  }
790
783
  static async infoToData(info) {
791
- const image = await _ImageLoader.loadAsync(info.src);
792
- const canvas = createCanvas(info.width, info.height);
793
- const context = canvas.getContext("2d");
794
- context.drawImage(image, 0, 0);
795
- return context.getImageData(0, 0, canvas.width, canvas.height);
784
+ const image = await this.infoToCanvasData(info);
785
+ return image.data.getImageData(0, 0, image.width, image.height);
796
786
  }
797
787
  static async getData(src) {
798
788
  const keyStr = serializeImageKey({ src });
@@ -807,38 +797,25 @@ var ImageLoader = class _ImageLoader {
807
797
  });
808
798
  }
809
799
  static async processResponse(response) {
810
- const src = response.config.url;
800
+ const src = response.url;
811
801
  const data = Buffer2.from(response.data);
812
802
  const { width, height, type } = Env.provider.imageSize(data);
803
+ if (!width || !height) {
804
+ throw new Error("Invalid or unsupported image dimensions");
805
+ }
813
806
  return {
814
807
  src,
815
- width: width || 0,
816
- height: height || 0,
808
+ width,
809
+ height,
817
810
  type,
818
811
  data
819
812
  };
820
813
  }
821
814
  static async loadInfo(src) {
822
- if (src.startsWith("data:image/png;base64")) {
823
- return this.processResponse({
824
- config: {
825
- url: src
826
- },
827
- data: Buffer2.from(src.substr("data:image/png;base64,".length), "base64")
828
- });
829
- }
830
815
  return Requests.genericRequest({
831
816
  url: src,
832
817
  responseType: "arraybuffer"
833
- }).then(this.processResponse).catch((err) => {
834
- return {
835
- src,
836
- width: 0,
837
- height: 0,
838
- type: void 0,
839
- data: Buffer2.from([0])
840
- };
841
- });
818
+ }).then(this.processResponse);
842
819
  }
843
820
  static async getInfo(src) {
844
821
  const keyStr = serializeImageKey({ src });
@@ -860,15 +837,22 @@ var ImageLoader = class _ImageLoader {
860
837
 
861
838
  // src/texture/TextureLoader.ts
862
839
  var TextureLoader = class {
840
+ /** Reports whether image loading failed, so cached textures can be retried. */
841
+ static hasFailed(texture) {
842
+ return this.failedTextures.has(texture);
843
+ }
863
844
  static createTexture() {
864
845
  return new Texture();
865
846
  }
866
847
  static loadInBackground(src, format = RGBAFormat, rotation = 0) {
867
848
  const texture = this.createTexture();
868
- const image = ImageLoader.loadElement(src);
869
- image.onload = function() {
849
+ texture.colorSpace = THREE.SRGBColorSpace;
850
+ const image = ImageLoader.loadElement(src, () => {
870
851
  texture.needsUpdate = true;
871
- };
852
+ }, (error) => {
853
+ this.failedTextures.add(texture);
854
+ console.warn("Failed to load texture", src, error);
855
+ });
872
856
  texture.image = image;
873
857
  texture.format = format;
874
858
  texture.rotation = rotation;
@@ -876,22 +860,27 @@ var TextureLoader = class {
876
860
  }
877
861
  static load(src, format = RGBAFormat, rotation = 0) {
878
862
  const texture = new Texture();
863
+ texture.colorSpace = THREE.SRGBColorSpace;
879
864
  ImageLoader.getData(src).then((image) => {
880
- texture.needsUpdate = true;
881
865
  texture.image = image;
866
+ texture.needsUpdate = true;
867
+ }).catch((error) => {
868
+ this.failedTextures.add(texture);
869
+ console.warn("Failed to load texture", src, error);
882
870
  });
883
871
  texture.format = format;
884
872
  texture.rotation = rotation;
885
873
  return Textures.initTextureProps(texture);
886
874
  }
887
875
  };
876
+ TextureLoader.failedTextures = /* @__PURE__ */ new WeakSet();
888
877
 
889
878
  // src/texture/Textures.ts
890
- import * as THREE from "three";
879
+ import * as THREE2 from "three";
891
880
  var Textures = class _Textures {
892
881
  static initTextureProps(texture) {
893
- texture.magFilter = THREE.NearestFilter;
894
- texture.minFilter = THREE.NearestFilter;
882
+ texture.magFilter = THREE2.NearestFilter;
883
+ texture.minFilter = THREE2.NearestFilter;
895
884
  texture.anisotropy = 0;
896
885
  return texture;
897
886
  }
@@ -902,10 +891,16 @@ var Textures = class _Textures {
902
891
  return new Texture2();
903
892
  }
904
893
  static createCanvasTexture(canvas) {
905
- return this.initTextureProps(new CanvasTexture(canvas));
894
+ const texture = new CanvasTexture(canvas);
895
+ texture.colorSpace = THREE2.SRGBColorSpace;
896
+ return this.initTextureProps(texture);
906
897
  }
907
898
  static getImage(key) {
908
899
  const keyStr = serializeTextureKey(key);
900
+ const cached = Caching.textureCache.peek(keyStr);
901
+ if (cached && TextureLoader.hasFailed(cached)) {
902
+ Caching.textureCache.invalidate(keyStr);
903
+ }
909
904
  return Caching.textureCache.get(keyStr, (k) => {
910
905
  return _Textures.createImage(key);
911
906
  });
@@ -949,6 +944,7 @@ var _Materials = class _Materials {
949
944
  uniform bool SHADE;
950
945
 
951
946
  varying vec2 vUv;
947
+ varying vec3 vTint;
952
948
  varying float light;
953
949
  varying float lift;
954
950
 
@@ -961,7 +957,11 @@ var _Materials = class _Materials {
961
957
 
962
958
  if (SHADE) {
963
959
 
964
- vec3 N = vec3( modelMatrix * instanceMatrix * vec4(normal, 0.0) );
960
+ #ifdef USE_INSTANCING
961
+ vec3 N = vec3( modelMatrix * instanceMatrix * vec4(normal, 0.0) );
962
+ #else
963
+ vec3 N = vec3( modelMatrix * vec4(normal, 0.0) );
964
+ #endif
965
965
 
966
966
  float yLight = (1.0+N.y) * 0.5;
967
967
  light = yLight * (1.0-AMBIENT) + N.x*N.x * XFAC + N.z*N.z * ZFAC + AMBIENT;
@@ -979,6 +979,7 @@ var _Materials = class _Materials {
979
979
  }
980
980
 
981
981
  vUv = uv;
982
+ vTint = color;
982
983
 
983
984
  #ifdef USE_INSTANCING
984
985
  gl_Position = projectionMatrix * viewMatrix * modelMatrix * instanceMatrix * vec4(position, 1.0);
@@ -1000,12 +1001,14 @@ var _Materials = class _Materials {
1000
1001
  uniform float BRIGHTNESS;
1001
1002
 
1002
1003
  varying vec2 vUv;
1004
+ varying vec3 vTint;
1003
1005
  varying float light;
1004
1006
  varying float lift;
1005
1007
 
1006
1008
  void main(void)
1007
1009
  {
1008
1010
  vec4 color = texture2D(map, vUv);
1011
+ color.rgb *= vTint;
1009
1012
 
1010
1013
  if (color.a < 0.01) discard;
1011
1014
 
@@ -1019,6 +1022,9 @@ var _Materials = class _Materials {
1019
1022
  gl_FragColor = vec4(lift + color.rgb * light2, 1.0);
1020
1023
 
1021
1024
  }
1025
+
1026
+ #include <tonemapping_fragment>
1027
+ #include <colorspace_fragment>
1022
1028
  }
1023
1029
  `;
1024
1030
  try {
@@ -1047,6 +1053,10 @@ var _Materials = class _Materials {
1047
1053
  }
1048
1054
  static getImage(key) {
1049
1055
  const keyStr = serializeMaterialKey(key);
1056
+ const map = Caching.materialCache.peek(keyStr)?.map;
1057
+ if (map && TextureLoader.hasFailed(map)) {
1058
+ Caching.materialCache.invalidate(keyStr);
1059
+ }
1050
1060
  return Caching.materialCache.get(keyStr, (k) => {
1051
1061
  return _Materials.createImage(key);
1052
1062
  });
@@ -1071,10 +1081,10 @@ _Materials.MISSING_TEXTURE_SRC = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA
1071
1081
  var Materials = _Materials;
1072
1082
 
1073
1083
  // src/Meshes.ts
1074
- import { Mesh as Mesh2 } from "three";
1084
+ import { Mesh } from "three";
1075
1085
  var Meshes = class _Meshes {
1076
1086
  static createBox(key) {
1077
- return new Mesh2(Geometries.getBox(key.geometry), Materials.getImage(key.material));
1087
+ return new Mesh(Geometries.getBox(key.geometry), Materials.getImage(key.material));
1078
1088
  }
1079
1089
  static getBox(key) {
1080
1090
  const keyStr = serializeMeshKey(key);
@@ -1085,7 +1095,7 @@ var Meshes = class _Meshes {
1085
1095
  };
1086
1096
 
1087
1097
  // src/UVMapper.ts
1088
- import { Float32BufferAttribute as Float32BufferAttribute2, Vector2 } from "three";
1098
+ import { Float32BufferAttribute as Float32BufferAttribute2, Vector2, Vector3 as Vector34 } from "three";
1089
1099
 
1090
1100
  // src/model/ModelMerger.ts
1091
1101
  var import_ts_deepmerge = __toESM(require_dist());
@@ -1121,6 +1131,7 @@ var AssetKey = class _AssetKey extends BasicAssetKey {
1121
1131
  this.rootType = rootType;
1122
1132
  this.extension = extension;
1123
1133
  this.root = root;
1134
+ this.isAssetKey = true;
1124
1135
  }
1125
1136
  static parse(assetType, str, origin) {
1126
1137
  let namespace = origin?.namespace || DEFAULT_NAMESPACE;
@@ -1136,12 +1147,15 @@ var AssetKey = class _AssetKey extends BasicAssetKey {
1136
1147
  split.shift();
1137
1148
  }
1138
1149
  let path = split.join("/");
1139
- let extension = assetType === "textures" ? ".png" : origin?.assetType || ".json";
1150
+ const extension = assetType === "textures" ? ".png" : origin?.extension ?? ".json";
1151
+ if (extension && path.endsWith(extension)) {
1152
+ path = path.slice(0, -extension.length);
1153
+ }
1140
1154
  return new _AssetKey(namespace, path, assetType, type, "assets", extension, origin?.root);
1141
1155
  }
1142
1156
  toString() {
1143
1157
  let a = [
1144
- typeof this.root !== "undefined" || AssetLoader.ROOT !== DEFAULT_ROOT ? this.root ?? AssetLoader.ROOT : "__root__",
1158
+ this.root ?? AssetLoader.ROOT,
1145
1159
  this.rootType ?? "__rootType__",
1146
1160
  this.assetType ?? "__assetType__",
1147
1161
  this.type ?? "__type__",
@@ -1151,12 +1165,12 @@ var AssetKey = class _AssetKey extends BasicAssetKey {
1151
1165
  return a.join("/");
1152
1166
  }
1153
1167
  getFullPath() {
1154
- let p13 = [];
1168
+ let p12 = [];
1155
1169
  if (this.type) {
1156
- p13.push(this.type);
1170
+ p12.push(this.type);
1157
1171
  }
1158
- p13.push(this.path);
1159
- return p13.join("/");
1172
+ p12.push(this.path);
1173
+ return p12.join("/");
1160
1174
  }
1161
1175
  toNamespacedString() {
1162
1176
  return this.namespace + ":" + this.getFullPath();
@@ -1166,22 +1180,84 @@ var AssetKey = class _AssetKey extends BasicAssetKey {
1166
1180
  }
1167
1181
  };
1168
1182
  function isBasicAssetKey(obj) {
1169
- return "namespace" in obj && "path" in obj;
1183
+ return obj !== null && typeof obj === "object" && typeof obj.namespace === "string" && typeof obj.path === "string";
1170
1184
  }
1171
1185
  function isAssetKey(obj) {
1172
- return isBasicAssetKey(obj) && "parse" in obj;
1186
+ return isBasicAssetKey(obj) && obj.isAssetKey === true;
1173
1187
  }
1174
1188
 
1189
+ // src/model/DisplayTransforms.ts
1190
+ import { Euler, Matrix4, Quaternion, Vector3 as Vector32 } from "three";
1191
+
1192
+ // src/model/DisplayPosition.ts
1193
+ var DisplayPosition = /* @__PURE__ */ ((DisplayPosition2) => {
1194
+ DisplayPosition2["THIRDPERSON_RIGHTHAND"] = "thirdperson_righthand";
1195
+ DisplayPosition2["THIRDPERSON_LEFTHAND"] = "thirdperson_lefthand";
1196
+ DisplayPosition2["FIRSTPERSON_RIGHTHAND"] = "firstperson_righthand";
1197
+ DisplayPosition2["FIRSTPERSON_LEFTHAND"] = "firstperson_lefthand";
1198
+ DisplayPosition2["GUI"] = "gui";
1199
+ DisplayPosition2["HEAD"] = "head";
1200
+ DisplayPosition2["GROUND"] = "ground";
1201
+ DisplayPosition2["FIXED"] = "fixed";
1202
+ DisplayPosition2["ON_SHELF"] = "on_shelf";
1203
+ return DisplayPosition2;
1204
+ })(DisplayPosition || {});
1205
+ var DISPLAY_POSITIONS = Object.values(DisplayPosition);
1206
+
1207
+ // src/model/DisplayTransforms.ts
1208
+ var DisplayTransforms = class {
1209
+ /** Resolve hand fallbacks within one model, before inheriting parent display entries. */
1210
+ static withHandFallbacks(display) {
1211
+ if (!display) return void 0;
1212
+ const resolved = { ...display };
1213
+ for (const [left, right] of [
1214
+ ["thirdperson_lefthand" /* THIRDPERSON_LEFTHAND */, "thirdperson_righthand" /* THIRDPERSON_RIGHTHAND */],
1215
+ ["firstperson_lefthand" /* FIRSTPERSON_LEFTHAND */, "firstperson_righthand" /* FIRSTPERSON_RIGHTHAND */]
1216
+ ]) {
1217
+ if (resolved[left] === void 0 && resolved[right] !== void 0) resolved[left] = resolved[right];
1218
+ }
1219
+ return resolved;
1220
+ }
1221
+ /** Build a display pose for geometry already centered at the model's [8, 8, 8] pivot. */
1222
+ static getMatrix(display, position) {
1223
+ const transform = this.withHandFallbacks(display)?.[position] ?? {};
1224
+ const left = position === "firstperson_lefthand" /* FIRSTPERSON_LEFTHAND */ || position === "thirdperson_lefthand" /* THIRDPERSON_LEFTHAND */;
1225
+ const sign = left ? -1 : 1;
1226
+ const [x, y, z] = transform.rotation ?? [0, 0, 0];
1227
+ const rotation = new Quaternion().setFromEuler(new Euler(toRadians(x), sign * toRadians(y), sign * toRadians(z), "XYZ"));
1228
+ const translation = new Vector32(...transform.translation ?? [0, 0, 0]).clampScalar(-80, 80);
1229
+ translation.x *= sign;
1230
+ const scale = new Vector32(...transform.scale ?? [1, 1, 1]).clampScalar(-4, 4);
1231
+ return new Matrix4().compose(translation, rotation, scale);
1232
+ }
1233
+ static apply(geometry, matrix) {
1234
+ geometry.applyMatrix4(matrix);
1235
+ if (matrix.determinant() < 0) {
1236
+ if (!geometry.index) geometry.setIndex(Array.from({ length: geometry.getAttribute("position").count }, (_, i) => i));
1237
+ const index = geometry.index;
1238
+ for (let i = 0; i < index.count; i += 3) {
1239
+ const second = index.getX(i + 1);
1240
+ index.setX(i + 1, index.getX(i + 2));
1241
+ index.setX(i + 2, second);
1242
+ }
1243
+ index.needsUpdate = true;
1244
+ }
1245
+ }
1246
+ };
1247
+
1175
1248
  // src/model/ModelMerger.ts
1176
1249
  var ModelMerger = class {
1177
1250
  static async mergeWithParents(model) {
1178
1251
  const models = await this.collectAllParents(model);
1179
1252
  let merged = {};
1180
- for (let parentModel of models) {
1181
- merged = (0, import_ts_deepmerge.default)(merged, parentModel);
1253
+ for (const source of [...models, model]) {
1254
+ const inheritedDisplay = merged.display;
1255
+ merged = import_ts_deepmerge.default.withOptions({ mergeArrays: false }, merged, source);
1256
+ if (source.display) {
1257
+ merged.display = { ...inheritedDisplay, ...DisplayTransforms.withHandFallbacks(source.display) };
1258
+ }
1182
1259
  }
1183
- merged = (0, import_ts_deepmerge.default)(merged, model);
1184
- merged.hierarchy = models.map((m) => m.parent).filter((p13) => `${p13}`);
1260
+ merged.hierarchy = models.map((m) => m.parent).filter((p12) => `${p12}`);
1185
1261
  merged.hierarchy.push(`${merged.parent}`);
1186
1262
  return merged;
1187
1263
  }
@@ -1191,6 +1267,7 @@ var ModelMerger = class {
1191
1267
  }
1192
1268
  const models = [];
1193
1269
  const parentKey = AssetKey.parse("models", model.parent);
1270
+ parentKey.root = model.key?.root;
1194
1271
  const parentModel = await Models.getRaw(parentKey);
1195
1272
  if (parentModel) {
1196
1273
  models.unshift(parentModel);
@@ -1200,9 +1277,6 @@ var ModelMerger = class {
1200
1277
  }
1201
1278
  };
1202
1279
 
1203
- // src/assets/Models.ts
1204
- import { Memoize } from "typescript-memoize";
1205
-
1206
1280
  // src/assets/source/parser/AssetParsers.ts
1207
1281
  var AssetParser = /* @__PURE__ */ ((AssetParser2) => {
1208
1282
  AssetParser2["MODEL"] = "model";
@@ -1216,30 +1290,65 @@ var AssetParser = /* @__PURE__ */ ((AssetParser2) => {
1216
1290
  })(AssetParser || {});
1217
1291
 
1218
1292
  // src/assets/Models.ts
1219
- var _Models = class _Models {
1293
+ var Models = class _Models {
1220
1294
  // opened lazily: touching the store at import time would hit IndexedDB/disk just for loading
1221
1295
  // the library, and would require an EnvProvider before any entry has had a chance to register
1222
1296
  static get PERSISTENT_CACHE() {
1223
1297
  return this._persistentCache ?? (this._persistentCache = PersistentCache.open("minerender-models"));
1224
1298
  }
1225
1299
  static async getItemList() {
1226
- return AssetLoader.get(new AssetKey(
1227
- DEFAULT_NAMESPACE,
1228
- "_list",
1229
- "models",
1230
- "item",
1231
- "assets",
1232
- ".json",
1233
- DEFAULT_ROOT
1234
- ), "list" /* LIST */).then((r) => r?.files ?? []);
1300
+ const key = new AssetKey(DEFAULT_NAMESPACE, "_list", "items");
1301
+ const legacyKey = new AssetKey(DEFAULT_NAMESPACE, "_list", "models", "item");
1302
+ return Caching.listAssetCache.get(key.serialize(), async () => {
1303
+ return (await AssetLoader.getFirst([key, legacyKey], "list" /* LIST */))?.asset;
1304
+ }).then((r) => r?.files ?? []);
1235
1305
  }
1236
1306
  static async loadAndMerge(key) {
1237
- const model = await this.getRaw(key);
1307
+ const model = key.type === "item" ? await this.getItemModel(key) : await this.getRaw(key);
1238
1308
  if (!model) {
1239
1309
  return void 0;
1240
1310
  }
1241
1311
  return ModelMerger.mergeWithParents(model);
1242
1312
  }
1313
+ static async getItemModel(key) {
1314
+ const itemKey = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
1315
+ const model = await this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(itemKey.serialize()), async () => {
1316
+ const result = await AssetLoader.getFirst([itemKey, key], "json" /* JSON */);
1317
+ if (!result) return void 0;
1318
+ if (result.key.assetType !== "items") return { ...result.asset, key };
1319
+ const reference = this.defaultItemModel(result.asset.model, key);
1320
+ const modelKey = AssetKey.parse("models", reference);
1321
+ modelKey.root = key.root;
1322
+ const model2 = await this.getRaw(modelKey);
1323
+ if (!model2) throw new Error(`Item ${key.toNamespacedString()} references missing model ${reference}`);
1324
+ return model2;
1325
+ });
1326
+ return model ? { ...model, key: Object.assign(new AssetKey("", ""), model.key) } : void 0;
1327
+ }
1328
+ // Item previews use the GUI context, false conditions, and zero numeric properties.
1329
+ static defaultItemModel(node, key) {
1330
+ if (!node || typeof node.type !== "string") {
1331
+ throw new Error(`Unsupported item model definition for ${key.toNamespacedString()}`);
1332
+ }
1333
+ switch (node.type.replace(/^minecraft:/, "")) {
1334
+ case "model":
1335
+ if (typeof node.model === "string" && node.model) return node.model;
1336
+ break;
1337
+ case "condition":
1338
+ return this.defaultItemModel(node.on_false, key);
1339
+ case "select": {
1340
+ const selected = node.property?.replace(/^minecraft:/, "") === "display_context" ? node.cases?.find((entry) => Array.isArray(entry.when) ? entry.when.includes("gui") : entry.when === "gui")?.model : void 0;
1341
+ return this.defaultItemModel(selected ?? node.fallback, key);
1342
+ }
1343
+ case "range_dispatch": {
1344
+ const selected = node.entries?.reduce((match, entry) => {
1345
+ return entry.threshold <= 0 && (!match || entry.threshold >= match.threshold) ? entry : match;
1346
+ }, void 0);
1347
+ return this.defaultItemModel(selected?.model ?? node.fallback, key);
1348
+ }
1349
+ }
1350
+ throw new Error(`Unsupported item model ${node?.type ?? "definition"} for ${key.toNamespacedString()}`);
1351
+ }
1243
1352
  static async getRaw(key) {
1244
1353
  if (!key.assetType) {
1245
1354
  key.assetType = "models";
@@ -1249,7 +1358,7 @@ var _Models = class _Models {
1249
1358
  }
1250
1359
  const keyStr = key.serialize();
1251
1360
  return Caching.rawModelCache.get(keyStr, (k) => {
1252
- return this.PERSISTENT_CACHE.getOrLoad(keyStr, (k1) => {
1361
+ return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1253
1362
  return AssetLoader.get(key, "model" /* MODEL */);
1254
1363
  });
1255
1364
  }).then((asset) => {
@@ -1278,31 +1387,32 @@ var _Models = class _Models {
1278
1387
  return this.PERSISTENT_CACHE.clear();
1279
1388
  }
1280
1389
  };
1281
- __decorateClass([
1282
- Memoize()
1283
- ], _Models, "getItemList", 1);
1284
- var Models = _Models;
1285
1390
 
1286
1391
  // src/assets/BlockStates.ts
1287
- import { Memoize as Memoize2 } from "typescript-memoize";
1288
1392
  var BlockStates = class {
1289
1393
  static get PERSISTENT_CACHE() {
1290
1394
  return this._persistentCache ?? (this._persistentCache = PersistentCache.open("minerender-blockstates"));
1291
1395
  }
1396
+ // BlockState names are hardcoded
1292
1397
  static async getList() {
1293
- return AssetLoader.get(new AssetKey(
1398
+ const key = new AssetKey(
1294
1399
  DEFAULT_NAMESPACE,
1295
1400
  "_list",
1296
1401
  "blockstates",
1297
1402
  void 0,
1298
1403
  "assets",
1299
1404
  ".json",
1300
- DEFAULT_ROOT
1301
- ), "list" /* LIST */).then((r) => r?.files ?? []);
1405
+ AssetLoader.ROOT
1406
+ );
1407
+ return Caching.listAssetCache.get(key.serialize(), () => {
1408
+ return AssetLoader.get(key, "list" /* LIST */);
1409
+ }).then((r) => r?.files ?? []);
1302
1410
  }
1303
1411
  static async getDefaultStates() {
1304
1412
  const key = AssetKey.parse("blockstates", "minerender:defaultBlockStates");
1305
- return AssetLoader.get(key, "json" /* JSON */);
1413
+ return Caching.defaultBlockStatesCache.get(key.serialize(), () => {
1414
+ return AssetLoader.get(key, "json" /* JSON */);
1415
+ });
1306
1416
  }
1307
1417
  static async getDefaultState(key) {
1308
1418
  const defaultStates = await this.getDefaultStates();
@@ -1320,7 +1430,7 @@ var BlockStates = class {
1320
1430
  }
1321
1431
  const keyStr = key.serialize();
1322
1432
  return Caching.blockStateCache.get(keyStr, (k) => {
1323
- return this.PERSISTENT_CACHE.getOrLoad(keyStr, (k1) => {
1433
+ return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1324
1434
  return AssetLoader.get(key, "blockstate" /* BLOCKSTATE */);
1325
1435
  });
1326
1436
  }).then((asset) => {
@@ -1341,17 +1451,11 @@ var BlockStates = class {
1341
1451
  await this.PERSISTENT_CACHE.clear();
1342
1452
  }
1343
1453
  };
1344
- __decorateClass([
1345
- Memoize2()
1346
- ], BlockStates, "getList", 1);
1347
- __decorateClass([
1348
- Memoize2()
1349
- ], BlockStates, "getDefaultStates", 1);
1350
1454
  var DefaultBlockStates = class {
1351
1455
  };
1352
1456
 
1353
1457
  // src/assets/Assets.ts
1354
- var DEFAULT_ROOT = "https://assets.mcasset.cloud/1.17.1";
1458
+ var DEFAULT_ROOT = "https://assets.mcasset.cloud/1.21.11";
1355
1459
  var DEFAULT_NAMESPACE = "minecraft";
1356
1460
  var Assets = class {
1357
1461
  /**
@@ -1363,16 +1467,153 @@ var Assets = class {
1363
1467
  static async getModel(key) {
1364
1468
  return Models.getMerged(key);
1365
1469
  }
1366
- static async getBlockState(key) {
1367
- return BlockStates.get(key);
1470
+ static async getBlockState(key) {
1471
+ return BlockStates.get(key);
1472
+ }
1473
+ };
1474
+
1475
+ // src/assets/source/AssetSource.ts
1476
+ var AssetSource = class {
1477
+ constructor() {
1478
+ }
1479
+ /**
1480
+ * Identifies this source's content across sessions for persistent caching. Sources that
1481
+ * cannot tell whether their content changed (an unnamed archive, for example) return
1482
+ * undefined, which keeps their results out of long-lived caches.
1483
+ */
1484
+ get cacheId() {
1485
+ return void 0;
1486
+ }
1487
+ };
1488
+ var AssetLoadError = class extends Error {
1489
+ constructor(source, key, path, cause) {
1490
+ super(`Failed to load ${key.toNamespacedString()} from ${path}${cause instanceof Error ? `: ${cause.message}` : ""}`);
1491
+ this.source = source;
1492
+ this.key = key;
1493
+ this.path = path;
1494
+ this.cause = cause;
1495
+ this.name = "AssetLoadError";
1496
+ }
1497
+ };
1498
+
1499
+ // src/util/BatchedExecutor.ts
1500
+ var BatchedExecutor = class {
1501
+ constructor(interval = 1, batch = 30) {
1502
+ this.interval = interval;
1503
+ this.batch = batch;
1504
+ this.queue = [];
1505
+ this.task = setInterval(() => this.run());
1506
+ }
1507
+ submit(task) {
1508
+ this.queue.push(task);
1509
+ }
1510
+ run() {
1511
+ for (let i = 0; i < this.batch; i++) {
1512
+ this.runNext();
1513
+ }
1514
+ }
1515
+ runNext() {
1516
+ let next = this.queue.shift();
1517
+ if (next) next();
1518
+ }
1519
+ };
1520
+
1521
+ // src/util/model.ts
1522
+ import { AxesHelper, BoxGeometry as BoxGeometry2, EdgesGeometry, LineBasicMaterial, LineSegments, Vector3 as Vector33 } from "three";
1523
+ function applyElementRotation(rotation, geometry) {
1524
+ const origin = new Vector33(rotation.origin[0], rotation.origin[1], rotation.origin[2]);
1525
+ geometry.translate(-origin.x, -origin.y, -origin.z);
1526
+ switch (rotation.axis) {
1527
+ case "x" /* X */:
1528
+ geometry.rotateX(toRadians(rotation.angle));
1529
+ break;
1530
+ case "y" /* Y */:
1531
+ geometry.rotateY(toRadians(rotation.angle));
1532
+ break;
1533
+ case "z" /* Z */:
1534
+ geometry.rotateZ(toRadians(rotation.angle));
1535
+ break;
1536
+ }
1537
+ if (rotation.rescale) {
1538
+ if (Math.abs(rotation.angle) === 22.5) {
1539
+ const s = 1 / Math.cos(0.39269909262657166);
1540
+ geometry.scale(s, 1, s);
1541
+ } else {
1542
+ const s = 1 / Math.cos(0.7853981852531433);
1543
+ geometry.scale(s, 1, s);
1544
+ }
1368
1545
  }
1369
- };
1370
-
1371
- // src/assets/source/AssetSource.ts
1372
- var AssetSource = class {
1373
- constructor() {
1546
+ geometry.translate(origin.x, origin.y, origin.z);
1547
+ }
1548
+ function applyGenericRotation(axis, rotation, obj) {
1549
+ switch (axis) {
1550
+ case "x" /* X */:
1551
+ obj.rotateOnAxis(new Vector33(1, 0, 0), toRadians(rotation));
1552
+ break;
1553
+ case "y" /* Y */:
1554
+ obj.rotateOnAxis(new Vector33(0, 1, 0), toRadians(rotation));
1555
+ break;
1556
+ case "z" /* Z */:
1557
+ obj.rotateOnAxis(new Vector33(0, 0, 1), toRadians(rotation));
1558
+ break;
1374
1559
  }
1375
- };
1560
+ }
1561
+ function addBox3WireframeToObject(box, obj, color = 16777215, w = 2) {
1562
+ let size = new Vector33();
1563
+ box.getSize(size);
1564
+ let boxGeo = new BoxGeometry2(size.x, size.y, size.z, 1, 1, 1);
1565
+ let wireGeo = new EdgesGeometry(boxGeo);
1566
+ let wireMat = new LineBasicMaterial({ color, linewidth: w });
1567
+ let wireframe = new LineSegments(wireGeo, wireMat);
1568
+ obj.add(wireframe);
1569
+ let axes = new AxesHelper(1);
1570
+ obj.add(axes);
1571
+ }
1572
+ function addWireframeToObject(obj, color = 16777215, w = 2) {
1573
+ let boxGeo = new BoxGeometry2(16, 16, 16, 1, 1, 1);
1574
+ let wireGeo = new EdgesGeometry(boxGeo);
1575
+ let wireMat = new LineBasicMaterial({ color, linewidth: w });
1576
+ let wireframe = new LineSegments(wireGeo, wireMat);
1577
+ obj.add(wireframe);
1578
+ let axes = new AxesHelper(1);
1579
+ obj.add(axes);
1580
+ }
1581
+ function addWireframeToMesh(geo, mesh, color = 16777215, w = 2) {
1582
+ let wireGeo = new EdgesGeometry(geo);
1583
+ let wireMat = new LineBasicMaterial({ color, linewidth: w });
1584
+ let wireframe = new LineSegments(wireGeo, wireMat);
1585
+ mesh.add(wireframe);
1586
+ let axes = new AxesHelper(1);
1587
+ mesh.add(axes);
1588
+ }
1589
+
1590
+ // src/util/performance.ts
1591
+ function captureException(e, ...extra) {
1592
+ console.error(e, extra);
1593
+ }
1594
+ function timingStart(marker) {
1595
+ console.time(marker);
1596
+ }
1597
+ function timingEnd(marker) {
1598
+ console.timeEnd(marker);
1599
+ }
1600
+
1601
+ // src/util/three.ts
1602
+ function isObject3D(obj) {
1603
+ return obj.isObject3D;
1604
+ }
1605
+ function isMesh(obj) {
1606
+ return obj.isMesh;
1607
+ }
1608
+ function isInstancedMesh(obj) {
1609
+ return obj.isInstancedMesh;
1610
+ }
1611
+ function isPerspectiveCamera(obj) {
1612
+ return obj.isPerspectiveCamera;
1613
+ }
1614
+ function isOrthographicCamera(obj) {
1615
+ return obj.isOrthographicCamera;
1616
+ }
1376
1617
 
1377
1618
  // src/nbt/NBTHelper.ts
1378
1619
  var NBTHelper = class {
@@ -1402,17 +1643,17 @@ var _HostedAssetSource = class _HostedAssetSource extends AssetSource {
1402
1643
  if (typeof namespaced !== "undefined") {
1403
1644
  return namespaced;
1404
1645
  }
1405
- if (typeof key.root !== "undefined" && key.root !== DEFAULT_ROOT || typeof this.root !== "undefined" && this.root !== DEFAULT_ROOT) {
1646
+ if (typeof key.root !== "undefined" && key.root !== AssetLoader.ROOT || typeof this.root !== "undefined" && this.root !== AssetLoader.ROOT) {
1406
1647
  console.info(p3, "Retrying", key, "with default root+namespace");
1407
- const namespacedRootedKey = new AssetKey(DEFAULT_NAMESPACE, key.path, key.assetType, key.type, key.rootType, key.extension, DEFAULT_ROOT);
1648
+ const namespacedRootedKey = new AssetKey(DEFAULT_NAMESPACE, key.path, key.assetType, key.type, key.rootType, key.extension, AssetLoader.ROOT);
1408
1649
  const namespacedRooted = await this.load(namespacedRootedKey, parser);
1409
1650
  if (typeof namespacedRooted !== "undefined") {
1410
1651
  return namespacedRooted;
1411
1652
  }
1412
1653
  }
1413
- } else if (typeof key.root !== "undefined" && key.root !== DEFAULT_ROOT || typeof this.root !== "undefined" && this.root !== DEFAULT_ROOT) {
1654
+ } else if (typeof key.root !== "undefined" && key.root !== AssetLoader.ROOT || typeof this.root !== "undefined" && this.root !== AssetLoader.ROOT) {
1414
1655
  console.info(p3, "Retrying", key, "with default root");
1415
- const rootKey = new AssetKey(key.namespace, key.path, key.assetType, key.type, key.rootType, key.extension, DEFAULT_ROOT);
1656
+ const rootKey = new AssetKey(key.namespace, key.path, key.assetType, key.type, key.rootType, key.extension, AssetLoader.ROOT);
1416
1657
  const rooted = await this.load(rootKey, parser);
1417
1658
  if (typeof rooted !== "undefined") {
1418
1659
  return rooted;
@@ -1424,31 +1665,25 @@ var _HostedAssetSource = class _HostedAssetSource extends AssetSource {
1424
1665
  console.info(p3, "Loading", key);
1425
1666
  const url = `${this.assetBasePath(key)}${key.type !== void 0 ? key.type + "/" : ""}${key.path}${key.extension}`;
1426
1667
  console.debug(p3, url);
1427
- let req = {
1428
- url
1429
- };
1430
- parser.config(req);
1431
- return await Requests.mcAssetRequest(req).then((response) => {
1432
- if (response && response.data) {
1433
- try {
1434
- return parser.parse(response);
1435
- } catch (e) {
1436
- console.warn("Parser failed to process response", response, e);
1437
- throw e;
1438
- }
1439
- }
1440
- return void 0;
1441
- }).catch((err) => {
1442
- if (err.response) {
1443
- let response = err.response;
1444
- if (response.status === 404) {
1445
- console.debug(p3, key, "not found");
1668
+ const request = { url };
1669
+ try {
1670
+ parser.config(request);
1671
+ let response;
1672
+ try {
1673
+ response = await Requests.mcAssetRequest(request);
1674
+ } catch (cause) {
1675
+ if (cause instanceof RequestError && cause.response?.status === 404) {
1446
1676
  return void 0;
1447
1677
  }
1678
+ throw cause;
1448
1679
  }
1449
- console.debug(p3, "Failed to load", key, err?.message);
1450
- return void 0;
1451
- });
1680
+ if (response.data === void 0) {
1681
+ throw new Error("Asset response has no data");
1682
+ }
1683
+ return await parser.parse(response);
1684
+ } catch (cause) {
1685
+ throw new AssetLoadError(this, key, request.url, cause);
1686
+ }
1452
1687
  }
1453
1688
  assetBasePath(key) {
1454
1689
  return `${key.root ?? this.root ?? AssetLoader.ROOT}/${key.rootType !== void 0 ? key.rootType + "/" : ""}${key.namespace !== void 0 ? key.namespace + "/" : ""}${key.assetType !== void 0 ? key.assetType + "/" : ""}`;
@@ -1461,6 +1696,9 @@ var _HostedAssetSource = class _HostedAssetSource extends AssetSource {
1461
1696
  get root() {
1462
1697
  return this._root;
1463
1698
  }
1699
+ get cacheId() {
1700
+ return `hosted:${this._root}`;
1701
+ }
1464
1702
  get options() {
1465
1703
  return this._options;
1466
1704
  }
@@ -1537,9 +1775,10 @@ var HostedAssetSource = _HostedAssetSource;
1537
1775
  // src/assets/source/archive/BrowserArchiveProxy.ts
1538
1776
  import { BlobReader, BlobWriter, ZipReader } from "@zip.js/zip.js";
1539
1777
  var BrowserArchiveProxy = class {
1540
- constructor(blob) {
1778
+ constructor(blob, id) {
1541
1779
  this._blob = blob;
1542
1780
  this._reader = new ZipReader(new BlobReader(this._blob));
1781
+ this.id = id ?? (typeof File !== "undefined" && blob instanceof File ? `${blob.name}:${blob.size}:${blob.lastModified}` : void 0);
1543
1782
  }
1544
1783
  async getEntries() {
1545
1784
  return (await this._reader.getEntries()).map((e) => {
@@ -1555,7 +1794,6 @@ var BrowserArchiveProxy = class {
1555
1794
  };
1556
1795
 
1557
1796
  // src/assets/source/archive/ArchiveAssetSource.ts
1558
- var p4 = prefix("ArchiveAssetSource");
1559
1797
  var ArchiveAssetSource = class _ArchiveAssetSource extends AssetSource {
1560
1798
  constructor(archiveProxy) {
1561
1799
  super();
@@ -1565,6 +1803,10 @@ var ArchiveAssetSource = class _ArchiveAssetSource extends AssetSource {
1565
1803
  static blob(blob) {
1566
1804
  return new _ArchiveAssetSource(new BrowserArchiveProxy(blob));
1567
1805
  }
1806
+ get cacheId() {
1807
+ const id = this._archiveProxy.id;
1808
+ return id === void 0 ? void 0 : `archive:${id}`;
1809
+ }
1568
1810
  async getEntries() {
1569
1811
  return this._archiveProxy.getEntries();
1570
1812
  }
@@ -1574,74 +1816,31 @@ var ArchiveAssetSource = class _ArchiveAssetSource extends AssetSource {
1574
1816
  }
1575
1817
  async get(key, parser) {
1576
1818
  const responseParser = HostedAssetSource.PARSER_MAP.get(parser);
1577
- console.log("ArchiveAssetSource.get", key, parser);
1578
- let entries = await this.getEntries();
1579
- const path = `${this.assetBasePath(key)}${key.type !== void 0 ? key.type + "/" : ""}${key.path}${key.extension}`;
1580
- console.log(path);
1581
- const entry = await this.getEntry(path);
1582
- console.log(entry);
1583
- if (!entry) {
1584
- return void 0;
1585
- }
1586
- let blob = await entry.getData();
1587
- const url = URL.createObjectURL(blob);
1588
- console.log(url);
1589
- let req = {
1590
- url
1591
- };
1592
- responseParser.config(req);
1593
- return Requests.genericRequest(req).then((response) => {
1594
- if (response && response.data) {
1595
- try {
1596
- return responseParser.parse(response);
1597
- } catch (e) {
1598
- console.warn("Parser failed to process response", response, e);
1599
- throw e;
1600
- }
1601
- }
1602
- return void 0;
1603
- }).catch((err) => {
1604
- if (err.response) {
1605
- let response = err.response;
1606
- if (response.status === 404) {
1607
- console.debug(p4, key, "not found");
1608
- return void 0;
1609
- }
1610
- }
1611
- console.debug(p4, "Failed to load", key, err?.message);
1612
- return void 0;
1613
- });
1819
+ return this.load(key, responseParser);
1614
1820
  }
1615
- //TODO: abstract this
1616
1821
  async load(key, parser) {
1617
- console.info(p4, "Loading", key);
1618
- const url = `${this.assetBasePath(key)}${key.type !== void 0 ? key.type + "/" : ""}${key.path}${key.extension}`;
1619
- console.debug(p4, url);
1620
- let req = {
1621
- url
1622
- };
1623
- parser.config(req);
1624
- return await Requests.genericRequest(req).then((response) => {
1625
- if (response && response.data) {
1626
- try {
1627
- return parser.parse(response);
1628
- } catch (e) {
1629
- console.warn("Parser failed to process response", response, e);
1630
- throw e;
1631
- }
1822
+ const path = `${this.assetBasePath(key)}${key.type !== void 0 ? key.type + "/" : ""}${key.path}${key.extension}`;
1823
+ let url;
1824
+ try {
1825
+ const entry = await this.getEntry(path);
1826
+ if (!entry) {
1827
+ return void 0;
1632
1828
  }
1633
- return void 0;
1634
- }).catch((err) => {
1635
- if (err.response) {
1636
- let response = err.response;
1637
- if (response.status === 404) {
1638
- console.debug(p4, key, "not found");
1639
- return void 0;
1640
- }
1829
+ url = URL.createObjectURL(await entry.getData());
1830
+ const request = { url };
1831
+ parser.config(request);
1832
+ const response = await Requests.genericRequest(request);
1833
+ if (response.data === void 0) {
1834
+ throw new Error("Asset response has no data");
1641
1835
  }
1642
- console.debug(p4, "Failed to load", key, err?.message);
1643
- return void 0;
1644
- });
1836
+ return await parser.parse(response);
1837
+ } catch (cause) {
1838
+ throw new AssetLoadError(this, key, path, cause);
1839
+ } finally {
1840
+ if (url !== void 0) {
1841
+ URL.revokeObjectURL(url);
1842
+ }
1843
+ }
1645
1844
  }
1646
1845
  assetBasePath(key) {
1647
1846
  return `${key.rootType !== void 0 ? key.rootType + "/" : ""}${key.namespace !== void 0 ? key.namespace + "/" : ""}${key.assetType !== void 0 ? key.assetType + "/" : ""}`;
@@ -1649,18 +1848,54 @@ var ArchiveAssetSource = class _ArchiveAssetSource extends AssetSource {
1649
1848
  };
1650
1849
 
1651
1850
  // src/assets/AssetLoader.ts
1652
- var import_ts_deepmerge3 = __toESM(require_dist());
1653
- var p5 = prefix("AssetLoader");
1851
+ var p4 = prefix("AssetLoader");
1852
+ var FALLBACK_ROOT = "https://raw.githubusercontent.com/InventivetalentDev/minerender-fallback-assets/master";
1654
1853
  var _AssetLoader = class _AssetLoader {
1854
+ static get version() {
1855
+ return this.ROOT.substring(this.ROOT.lastIndexOf("/") + 1);
1856
+ }
1857
+ static setVersion(version) {
1858
+ this.ROOT = `https://assets.mcasset.cloud/${version}`;
1859
+ const source = new HostedAssetSource(this.ROOT, { retryDefaults: false });
1860
+ const index = this._SOURCES.findIndex((s) => s.key === "mcassets");
1861
+ if (index !== -1) {
1862
+ this._SOURCES[index] = { key: "mcassets", source };
1863
+ } else {
1864
+ this.addSource("mcassets", source);
1865
+ }
1866
+ Caching.clear();
1867
+ }
1868
+ /**
1869
+ * Scope for persistent cache keys. Empty with only the default vanilla sources, so their
1870
+ * entries stay valid across sessions; otherwise it names every added source (resource packs,
1871
+ * mirrors) so their results never masquerade as vanilla assets after a reload.
1872
+ */
1873
+ static get persistentScope() {
1874
+ const parts = [];
1875
+ for (const { key, source } of this._SOURCES) {
1876
+ const id = source.cacheId;
1877
+ if (key === "mcassets" && id === `hosted:${this.ROOT}`) continue;
1878
+ if (key === "mcassets-fallback" && id === `hosted:${FALLBACK_ROOT}`) continue;
1879
+ if (id === void 0) return this.SESSION_SCOPE;
1880
+ parts.push(`${key}=${id}`);
1881
+ }
1882
+ return parts.join("|");
1883
+ }
1884
+ /** Prefixes a persistent cache key with the current source scope. */
1885
+ static persistentKey(key) {
1886
+ const scope = this.persistentScope;
1887
+ return scope ? `${scope}
1888
+ ${key}` : key;
1889
+ }
1655
1890
  static addSource(key, source, override = true) {
1656
1891
  if (override) {
1657
1892
  const existing = this.removeSource(key);
1658
1893
  if (existing) {
1659
- console.log(p5, "Removed existing AssetSource", key);
1894
+ console.log(p4, "Removed existing AssetSource", key);
1660
1895
  }
1661
1896
  }
1662
1897
  this._SOURCES.unshift({ key, source });
1663
- console.log(p5, "Added AssetSource", key);
1898
+ console.log(p4, "Added AssetSource", key);
1664
1899
  }
1665
1900
  static removeSource(key) {
1666
1901
  const index = this._SOURCES.findIndex((s) => s.key === key);
@@ -1673,7 +1908,6 @@ var _AssetLoader = class _AssetLoader {
1673
1908
  return void 0;
1674
1909
  }
1675
1910
  static async getAll(key, parser) {
1676
- console.log(this._SOURCES);
1677
1911
  let promises = [];
1678
1912
  for (const source of this._SOURCES) {
1679
1913
  promises.push(source.source.get(key, parser));
@@ -1682,30 +1916,42 @@ var _AssetLoader = class _AssetLoader {
1682
1916
  return results.filter((r) => r != void 0);
1683
1917
  });
1684
1918
  }
1919
+ /** Returns the first defined result in source-priority order, without merging assets. */
1685
1920
  static async get(key, parser) {
1686
- console.log(this._SOURCES);
1687
- let promises = [];
1688
- for (const source of this._SOURCES) {
1689
- promises.push(source.source.get(key, parser));
1690
- }
1691
- return Promise.all(promises).then((results) => {
1692
- const fallback = results[results.length - 1];
1693
- for (const result of results) {
1694
- if (result) {
1695
- if (fallback) {
1696
- return (0, import_ts_deepmerge3.default)({}, fallback, result);
1697
- }
1698
- return result;
1921
+ const keys = [key];
1922
+ if (key.rootType === "data" && (key.assetType === "structure" || key.assetType === "structures")) {
1923
+ keys.push(new AssetKey(
1924
+ key.namespace,
1925
+ key.path,
1926
+ key.assetType === "structure" ? "structures" : "structure",
1927
+ key.type,
1928
+ key.rootType,
1929
+ key.extension,
1930
+ key.root
1931
+ ));
1932
+ }
1933
+ return (await this.getFirst(keys, parser))?.asset;
1934
+ }
1935
+ /** Tries each path within a source before considering lower-priority sources. */
1936
+ static async getFirst(keys, parser) {
1937
+ const sources = [...this._SOURCES];
1938
+ for (const source of sources) {
1939
+ for (const key of keys) {
1940
+ const result = await source.source.get(key, parser);
1941
+ if (result !== void 0) {
1942
+ return { key, asset: result };
1699
1943
  }
1700
1944
  }
1701
- return void 0;
1702
- });
1945
+ }
1946
+ return void 0;
1703
1947
  }
1704
1948
  };
1705
1949
  _AssetLoader.ROOT = DEFAULT_ROOT;
1706
1950
  _AssetLoader._SOURCES = [];
1707
- _AssetLoader.addSource("mcassets-fallback", new HostedAssetSource("https://raw.githubusercontent.com/InventivetalentDev/minerender-fallback-assets/master", { retryDefaults: false }));
1708
- _AssetLoader.addSource("mcassets", new HostedAssetSource(DEFAULT_ROOT, { retryDefaults: false }));
1951
+ /** Used when a registered source cannot identify its content, so nothing persists past this session. */
1952
+ _AssetLoader.SESSION_SCOPE = `session:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
1953
+ _AssetLoader.addSource("mcassets-fallback", new HostedAssetSource(FALLBACK_ROOT, { retryDefaults: false }));
1954
+ _AssetLoader.addSource("mcassets", new HostedAssetSource(_AssetLoader.ROOT, { retryDefaults: false }));
1709
1955
  var AssetLoader = _AssetLoader;
1710
1956
 
1711
1957
  // src/assets/ModelTextures.ts
@@ -1714,11 +1960,21 @@ var ModelTextures = class {
1714
1960
  return this._persistentMetaCache ?? (this._persistentMetaCache = PersistentCache.open("minerender-texturemeta"));
1715
1961
  }
1716
1962
  static async get(key) {
1717
- const asset = await this.preload(key);
1718
- if (asset) {
1719
- return ImageLoader.infoToCanvasData(asset);
1963
+ const keyStr = key.serialize();
1964
+ const pending = this.preload(key);
1965
+ const cached = Caching.textureAssetCache.getIfPresent(keyStr);
1966
+ try {
1967
+ const asset = await pending;
1968
+ if (asset) {
1969
+ return await ImageLoader.infoToCanvasData(asset);
1970
+ }
1971
+ return void 0;
1972
+ } catch (err) {
1973
+ if (cached && Caching.textureAssetCache.getIfPresent(keyStr) === cached) {
1974
+ Caching.textureAssetCache.invalidate(keyStr);
1975
+ }
1976
+ throw err;
1720
1977
  }
1721
- return void 0;
1722
1978
  }
1723
1979
  static async preload(key) {
1724
1980
  const keyStr = key.serialize();
@@ -1737,7 +1993,7 @@ var ModelTextures = class {
1737
1993
  }
1738
1994
  const keyStr = key.serialize();
1739
1995
  return Caching.textureMetaCache.get(keyStr, (k) => {
1740
- return this.PERSISTENT_META_CACHE.getOrLoad(keyStr, (k1) => {
1996
+ return this.PERSISTENT_META_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1741
1997
  return AssetLoader.get(key, "meta" /* META */).then((asset) => {
1742
1998
  if (asset)
1743
1999
  asset.key = key;
@@ -1888,17 +2144,20 @@ var TextureAtlas = class {
1888
2144
  // src/model/ModelGenerator.ts
1889
2145
  var ModelGenerator = class {
1890
2146
  static generateItemModel(imageData, layer) {
2147
+ const tintindex = this.ITEM_LAYERS.indexOf(layer);
1891
2148
  const mainElement = {
1892
2149
  from: [0, 0, 7.5],
1893
2150
  to: [16, 16, 8.5],
1894
2151
  faces: {
1895
2152
  "south": {
1896
2153
  uv: [0, 0, 16, 16],
1897
- texture: `#${layer}`
2154
+ texture: `#${layer}`,
2155
+ tintindex
1898
2156
  },
1899
2157
  "north": {
1900
2158
  uv: [16, 0, 0, 16],
1901
- texture: `#${layer}`
2159
+ texture: `#${layer}`,
2160
+ tintindex
1902
2161
  }
1903
2162
  },
1904
2163
  shade: true
@@ -1909,11 +2168,19 @@ var ModelGenerator = class {
1909
2168
  ModelGenerator.ITEM_LAYERS = ["layer0", "layer1", "layer2", "layer3", "layer4"];
1910
2169
 
1911
2170
  // src/UVMapper.ts
1912
- var p6 = prefix("UVMapper");
2171
+ var p5 = prefix("UVMapper");
1913
2172
  var DEFAULT_UV = [0, 0, 16, 16];
1914
2173
  var X = 0;
1915
2174
  var Y = 1;
1916
2175
  var Z = 2;
2176
+ var FACE_UV_AXES = {
2177
+ east: [new Vector34(0, 0, -1), new Vector34(0, -1, 0)],
2178
+ west: [new Vector34(0, 0, 1), new Vector34(0, -1, 0)],
2179
+ up: [new Vector34(1, 0, 0), new Vector34(0, 0, 1)],
2180
+ down: [new Vector34(1, 0, 0), new Vector34(0, 0, -1)],
2181
+ south: [new Vector34(1, 0, 0), new Vector34(0, -1, 0)],
2182
+ north: [new Vector34(-1, 0, 0), new Vector34(0, -1, 0)]
2183
+ };
1917
2184
  var UVMapper = class {
1918
2185
  static makeUv(uvCoord, originalTextureSize, actualTextureSize) {
1919
2186
  if (uvCoord === 0) return 0;
@@ -2011,6 +2278,42 @@ var UVMapper = class {
2011
2278
  static facesToUv(faces, originalTextureSize, actualTextureSize) {
2012
2279
  return new Float32BufferAttribute2(this.facesToUvArray(faces, originalTextureSize, actualTextureSize), 2);
2013
2280
  }
2281
+ static lockUvs(geometry, faces, atlas, rotation) {
2282
+ const uv = geometry.getAttribute("uv");
2283
+ for (let faceIndex = 0; faceIndex < CUBE_FACES.length; faceIndex++) {
2284
+ const faceName = CUBE_FACES[faceIndex];
2285
+ const texture = faces[faceName]?.texture?.substring(1);
2286
+ const position = texture && atlas.positions[texture];
2287
+ const size = texture && atlas.sizes[texture];
2288
+ if (!position || !size) continue;
2289
+ const [sourceU, sourceV] = FACE_UV_AXES[faceName];
2290
+ const uAxis = sourceU.clone().applyEuler(rotation);
2291
+ const vAxis = sourceV.clone().applyEuler(rotation);
2292
+ const normal = vAxis.clone().cross(uAxis);
2293
+ const targetFace = CUBE_FACES.find((name) => {
2294
+ const [u, v] = FACE_UV_AXES[name];
2295
+ return v.clone().cross(u).dot(normal) > 0.5;
2296
+ });
2297
+ const [targetU, targetV] = FACE_UV_AXES[targetFace];
2298
+ const uu = Math.round(targetU.dot(uAxis));
2299
+ const uvCross = Math.round(targetU.dot(vAxis));
2300
+ const vu = Math.round(targetV.dot(uAxis));
2301
+ const vv = Math.round(targetV.dot(vAxis));
2302
+ for (let corner = 0; corner < 4; corner++) {
2303
+ const index = faceIndex * 4 + corner;
2304
+ const u = (uv.getX(index) * atlas.image.width - position[0]) / size[0] - 0.5;
2305
+ const v = ((1 - uv.getY(index)) * atlas.image.height - position[1]) / size[1] - 0.5;
2306
+ const lockedU = uu * u + uvCross * v + 0.5;
2307
+ const lockedV = vu * u + vv * v + 0.5;
2308
+ uv.setXY(
2309
+ index,
2310
+ (position[0] + lockedU * size[0]) / atlas.image.width,
2311
+ 1 - (position[1] + lockedV * size[1]) / atlas.image.height
2312
+ );
2313
+ }
2314
+ }
2315
+ uv.needsUpdate = true;
2316
+ }
2014
2317
  static async getAtlas(model) {
2015
2318
  const keyStr = model.key.serialize();
2016
2319
  return Caching.modelTextureAtlasCache.get(keyStr, (k) => {
@@ -2086,7 +2389,7 @@ var UVMapper = class {
2086
2389
  const metaMap = {};
2087
2390
  const model = { ...originalModel };
2088
2391
  const isItemModel = !("elements" in model);
2089
- console.debug(p6, "Creating Atlas for", model.key);
2392
+ console.debug(p5, "Creating Atlas for", model.key);
2090
2393
  if (model.textures) {
2091
2394
  const promises = [];
2092
2395
  const uniqueTextureNames = [];
@@ -2100,7 +2403,7 @@ var UVMapper = class {
2100
2403
  const assetKey = AssetKey.parse("textures", textureValue, model.key);
2101
2404
  promises.push(ModelTextures.get(assetKey).then((asset) => {
2102
2405
  if (!asset) {
2103
- console.warn(p6, "Missing texture", assetKey);
2406
+ console.warn(p5, "Missing texture", assetKey);
2104
2407
  return;
2105
2408
  }
2106
2409
  textureMap[textureKey] = new WrappedImage(asset);
@@ -2112,7 +2415,7 @@ var UVMapper = class {
2112
2415
  }
2113
2416
  await Promise.all(promises);
2114
2417
  const textureCount = uniqueTextureNames.length;
2115
- console.debug(p6, "Creating Atlas for", textureCount, "textures");
2418
+ console.debug(p5, "Creating Atlas for", textureCount, "textures");
2116
2419
  this.fillMissingTextureKeys(model.textures, textureMap);
2117
2420
  this.fillMissingTextureKeys(model.textures, metaMap);
2118
2421
  const sizes = {};
@@ -2132,7 +2435,7 @@ var UVMapper = class {
2132
2435
  this.fillMissingTextureKeys(model.textures, sizes);
2133
2436
  const s = Math.ceil(Math.sqrt(textureCount + 1));
2134
2437
  const size = Math.ceil(s * maxWidth);
2135
- console.debug(p6, "Atlas size:", size);
2438
+ console.debug(p5, "Atlas size:", size);
2136
2439
  const squaredSize = size * size;
2137
2440
  const missing = await ImageLoader.getData("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABAAQMAAACQp+OdAAAABlBMVEX/AP8AAACfphTyAAAAFUlEQVQoz2MIhQKGVVAwKjIqQrwIAHRz/wFI17TEAAAAAElFTkSuQmCC");
2138
2441
  const empty = await ImageLoader.getData("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABAAQMAAACQp+OdAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAA5JREFUKM9jGAWjgAIAAAJAAAFSSwuTAAAAAElFTkSuQmCC");
@@ -2187,10 +2490,10 @@ var UVMapper = class {
2187
2490
  }
2188
2491
  }
2189
2492
  const atlasImageData = image.toDataURL();
2190
- console.log(p6, "Atlas Image for", model.key, atlasImageData);
2493
+ console.log(p5, "Atlas Image for", model.key, atlasImageData);
2191
2494
  this.fillMissingTextureKeys(model.textures, positions);
2192
2495
  if (isItemModel) {
2193
- console.debug(p6, "Generating item model for", model);
2496
+ console.debug(p5, "Generating item model for", model);
2194
2497
  model.elements = [];
2195
2498
  for (let layerName of ModelGenerator.ITEM_LAYERS) {
2196
2499
  const textureImage = textureMap[layerName];
@@ -2198,7 +2501,7 @@ var UVMapper = class {
2198
2501
  model.elements.push(...ModelGenerator.generateItemModel(textureImage.data, layerName));
2199
2502
  }
2200
2503
  }
2201
- console.debug(p6, "Item model elements", model.elements);
2504
+ console.debug(p5, "Item model elements", model.elements);
2202
2505
  }
2203
2506
  if (!model.elements) {
2204
2507
  }
@@ -2216,17 +2519,13 @@ var UVMapper = class {
2216
2519
  this.setCubeFaceUvInArray(uv, faceIndex * 4, [0, 1, 8 / size, (size - 8) / size]);
2217
2520
  continue;
2218
2521
  }
2522
+ if (!face.texture) continue;
2219
2523
  if (!face.uv) {
2220
2524
  face.uv = this.getFallbackUv(element, faceName);
2221
2525
  }
2222
- let faceTexture = face.texture;
2223
- let texPosition;
2224
- let texSize;
2225
- if (faceTexture) {
2226
- faceTexture = faceTexture.substr(1);
2227
- texPosition = positions[faceTexture] || [0, 0];
2228
- texSize = sizes[faceTexture] || [16, 16];
2229
- }
2526
+ const faceTexture = face.texture.substr(1);
2527
+ const texPosition = positions[faceTexture] || [0, 0];
2528
+ const texSize = sizes[faceTexture] || [16, 16];
2230
2529
  let texPosV = new Vector2(texPosition[0], texPosition[1]);
2231
2530
  const [tl, tr, bl, br] = this.expandAndRotateMcUvs(face.uv, face.rotation, texSize[0], texSize[1]);
2232
2531
  tl.add(texPosV);
@@ -2258,7 +2557,7 @@ var UVMapper = class {
2258
2557
  hasTransparency
2259
2558
  );
2260
2559
  } else {
2261
- console.warn(p6, "Model does not have any textures", model);
2560
+ console.warn(p5, "Model does not have any textures", model);
2262
2561
  }
2263
2562
  return void 0;
2264
2563
  }
@@ -2300,18 +2599,18 @@ var MinecraftCubeTexture = class {
2300
2599
  }
2301
2600
  getUvEast(w, h, l) {
2302
2601
  return [
2303
- [this.mapW(this.u), this.mapH(this.v + l + h)],
2304
- [this.mapW(this.u + l), this.mapH(this.v + l + h)],
2305
- [this.mapW(this.u), this.mapH(this.v + l)],
2306
- [this.mapW(this.u + l), this.mapH(this.v + l)]
2602
+ [this.mapW(this.u + l + w + l), this.mapH(this.v + l + h)],
2603
+ [this.mapW(this.u + l + w), this.mapH(this.v + l + h)],
2604
+ [this.mapW(this.u + l + w + l), this.mapH(this.v + l)],
2605
+ [this.mapW(this.u + l + w), this.mapH(this.v + l)]
2307
2606
  ];
2308
2607
  }
2309
2608
  getUvWest(w, h, l) {
2310
2609
  return [
2311
- [this.mapW(this.u + l + w), this.mapH(this.v + l + h)],
2312
- [this.mapW(this.u + l + w + l), this.mapH(this.v + l + h)],
2313
- [this.mapW(this.u + l + w), this.mapH(this.v + l)],
2314
- [this.mapW(this.u + l + w + l), this.mapH(this.v + l)]
2610
+ [this.mapW(this.u + l), this.mapH(this.v + l + h)],
2611
+ [this.mapW(this.u), this.mapH(this.v + l + h)],
2612
+ [this.mapW(this.u + l), this.mapH(this.v + l)],
2613
+ [this.mapW(this.u), this.mapH(this.v + l)]
2315
2614
  ];
2316
2615
  }
2317
2616
  getUvUp(w, h, l) {
@@ -2324,26 +2623,26 @@ var MinecraftCubeTexture = class {
2324
2623
  }
2325
2624
  getUvDown(w, h, l) {
2326
2625
  return [
2327
- [this.mapW(this.u + l), this.mapH(this.v + l)],
2328
- [this.mapW(this.u + l + w), this.mapH(this.v + l)],
2329
2626
  [this.mapW(this.u + l), this.mapH(this.v)],
2330
- [this.mapW(this.u + l + w), this.mapH(this.v)]
2627
+ [this.mapW(this.u + l + w), this.mapH(this.v)],
2628
+ [this.mapW(this.u + l), this.mapH(this.v + l)],
2629
+ [this.mapW(this.u + l + w), this.mapH(this.v + l)]
2331
2630
  ];
2332
2631
  }
2333
2632
  getUvSouth(w, h, l) {
2334
2633
  return [
2335
- [this.mapW(this.u + l + w + l), this.mapH(this.v + l + h)],
2336
2634
  [this.mapW(this.u + l + w + l + w), this.mapH(this.v + l + h)],
2337
- [this.mapW(this.u + l + w + l), this.mapH(this.v + l)],
2338
- [this.mapW(this.u + l + w + l + w), this.mapH(this.v + l)]
2635
+ [this.mapW(this.u + l + w + l), this.mapH(this.v + l + h)],
2636
+ [this.mapW(this.u + l + w + l + w), this.mapH(this.v + l)],
2637
+ [this.mapW(this.u + l + w + l), this.mapH(this.v + l)]
2339
2638
  ];
2340
2639
  }
2341
2640
  getUvNorth(w, h, l) {
2342
2641
  return [
2343
- [this.mapW(this.u + l), this.mapH(this.v + l + h)],
2344
2642
  [this.mapW(this.u + l + w), this.mapH(this.v + l + h)],
2345
- [this.mapW(this.u + l), this.mapH(this.v + l)],
2346
- [this.mapW(this.u + l + w), this.mapH(this.v + l)]
2643
+ [this.mapW(this.u + l), this.mapH(this.v + l + h)],
2644
+ [this.mapW(this.u + l + w), this.mapH(this.v + l)],
2645
+ [this.mapW(this.u + l), this.mapH(this.v + l)]
2347
2646
  ];
2348
2647
  }
2349
2648
  getFaceUv(face, w, h, l) {
@@ -2362,10 +2661,12 @@ var MinecraftCubeTexture = class {
2362
2661
  return this.getUvNorth(w, h, l);
2363
2662
  }
2364
2663
  }
2365
- toUvArray(w, h, l) {
2664
+ toUvArray(w, h, l, mirror = false) {
2366
2665
  const uv = [];
2367
2666
  for (let i = 0; i < CUBE_FACES.length; i++) {
2368
- UVMapper.setFaceUvInArray(uv, i * 4, this.getFaceUv(CUBE_FACES[i], w, h, l));
2667
+ const face = mirror && i < 2 ? CUBE_FACES[1 - i] : CUBE_FACES[i];
2668
+ const corners = this.getFaceUv(face, w, h, l);
2669
+ UVMapper.setFaceUvInArray(uv, i * 4, mirror ? [corners[1], corners[0], corners[3], corners[2]] : corners);
2369
2670
  }
2370
2671
  return uv;
2371
2672
  }
@@ -2395,76 +2696,85 @@ function shutdown() {
2395
2696
  Caching.end();
2396
2697
  }
2397
2698
 
2699
+ // src/error/MineRenderError.ts
2700
+ var MineRenderError = class _MineRenderError extends Error {
2701
+ constructor(msg) {
2702
+ super(msg);
2703
+ Object.setPrototypeOf(this, _MineRenderError.prototype);
2704
+ }
2705
+ get name() {
2706
+ return "MineRenderError";
2707
+ }
2708
+ };
2709
+
2398
2710
  // src/assets/Entities.ts
2399
- import { Memoize as Memoize3 } from "typescript-memoize";
2400
2711
  var Entities = class {
2401
- static async getBlockEntityModels() {
2402
- const key = AssetKey.parse("models", "minerender:blockEntityModels");
2403
- console.log(key);
2404
- return AssetLoader.get(key, "json" /* JSON */);
2405
- }
2406
- static async getEntityModels() {
2407
- const key = AssetKey.parse("models", "minerender:entityModels");
2408
- console.log(key);
2409
- return AssetLoader.get(key, "json" /* JSON */);
2410
- }
2411
- // BlockEntity names are hardcoded
2412
- static async getBlockList() {
2413
- const models = await this.getBlockEntityModels();
2414
- if (!models) {
2415
- return [];
2416
- }
2417
- return Object.keys(models);
2418
- }
2419
2712
  static async getEntityList() {
2420
- const models = await this.getEntityModels();
2421
- if (!models) {
2422
- return [];
2423
- }
2424
- return Object.keys(models);
2425
- }
2426
- static async getBlock(modelKey, textureKey) {
2427
- const models = await this.getBlockEntityModels();
2428
- if (!models) {
2429
- return void 0;
2430
- }
2431
- if (!textureKey) {
2432
- textureKey = modelKey;
2433
- }
2434
- const baseKey = modelKey.path.includes("/") ? new BasicAssetKey(modelKey.namespace, modelKey.path.split("/")[0]) : modelKey;
2435
- return {
2436
- key: textureKey,
2437
- parts: models[baseKey.toNamespacedString()]
2713
+ const collect = async (path) => {
2714
+ const prefix2 = path ? `${path}/` : "";
2715
+ const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json");
2716
+ const list = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
2717
+ if (!list) return [];
2718
+ const files = list.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
2719
+ const children = await Promise.all(list.directories.map((directory) => collect(`${prefix2}${directory}`)));
2720
+ return [...files, ...children.flat()];
2438
2721
  };
2722
+ return collect("");
2723
+ }
2724
+ static async getEntity(modelKey, textureKey, options) {
2725
+ const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
2726
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json");
2727
+ const model = await Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
2728
+ if (!model) return void 0;
2729
+ const layerName = options?.layer ?? "main";
2730
+ const layer = model.layers[layerName];
2731
+ if (!layer) {
2732
+ throw new MineRenderError(`Entity ${model.id} has no layer "${layerName}". Available layers: ${Object.keys(model.layers).join(", ")}`);
2733
+ }
2734
+ let texture;
2735
+ if (textureKey) {
2736
+ const path2 = isAssetKey(textureKey) ? textureKey.getFullPath() : textureKey.path;
2737
+ texture = isAssetKey(textureKey) && textureKey.assetType === "textures" && path2.startsWith("entity/") ? textureKey : new AssetKey(textureKey.namespace, path2, "textures", "entity", "assets", ".png", isAssetKey(textureKey) ? textureKey.root : void 0);
2738
+ } else if (layer.textureLocation !== void 0) {
2739
+ texture = AssetKey.parse("textures", layer.textureLocation.replace(/^([^:]+:)?textures\//, "$1"));
2740
+ } else {
2741
+ texture = await this.resolveTexture(modelKey);
2742
+ }
2743
+ return { key: textureKey ?? modelKey, texture, layer, id: model.id };
2744
+ }
2745
+ static async resolveTexture(modelKey) {
2746
+ const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
2747
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json");
2748
+ return Caching.entityTextureCache.get(key.serialize(), async () => {
2749
+ const name = path.split("/").pop();
2750
+ for (const candidate of [path, `${path}/${name}`]) {
2751
+ const texture = new AssetKey(modelKey.namespace, candidate, "textures", "entity", "assets", ".png");
2752
+ if (await ModelTextures.get(texture)) return texture;
2753
+ }
2754
+ const listKey = new AssetKey(modelKey.namespace, "_list", `${name}_variant`, void 0, "data", ".json");
2755
+ const list = await AssetLoader.get(listKey, "list" /* LIST */);
2756
+ const files = list?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
2757
+ const file = files?.find((file2) => file2 === "temperate.json") ?? files?.[0];
2758
+ if (!file) return void 0;
2759
+ const variantKey = new AssetKey(modelKey.namespace, file.slice(0, -5), `${name}_variant`, void 0, "data", ".json");
2760
+ const variant = await AssetLoader.get(variantKey, "json" /* JSON */);
2761
+ const assets = variant?.assets;
2762
+ const textureId = variant?.asset_id ?? (typeof assets?.wild === "string" ? assets.wild : Object.values(assets ?? {}).find((value) => typeof value === "string"));
2763
+ return typeof textureId === "string" ? AssetKey.parse("textures", textureId) : void 0;
2764
+ });
2439
2765
  }
2440
- static async getEntity(modelKey, textureKey) {
2441
- const models = await this.getEntityModels();
2442
- if (!models) {
2443
- return void 0;
2444
- }
2445
- if (!textureKey) {
2446
- textureKey = modelKey;
2447
- }
2448
- const baseKey = modelKey.path.includes("/") ? new BasicAssetKey(modelKey.namespace, modelKey.path.split("/")[0]) : modelKey;
2449
- return {
2450
- key: textureKey,
2451
- parts: models[baseKey.toNamespacedString()]
2452
- };
2766
+ /** @deprecated Use getEntityList(); block entities share the entity dataset. */
2767
+ static getBlockList() {
2768
+ return this.getEntityList();
2769
+ }
2770
+ /** @deprecated Use getEntity(); block entities share the entity dataset. */
2771
+ static getBlock(modelKey, textureKey, options) {
2772
+ return this.getEntity(modelKey, textureKey, options);
2453
2773
  }
2454
- };
2455
- __decorateClass([
2456
- Memoize3()
2457
- ], Entities, "getBlockEntityModels", 1);
2458
- __decorateClass([
2459
- Memoize3()
2460
- ], Entities, "getEntityModels", 1);
2461
- var EntityModels = class {
2462
- };
2463
- var BlockEntityModels = class {
2464
2774
  };
2465
2775
 
2466
2776
  // src/renderer/SceneObject.ts
2467
- import { Euler, InstancedMesh, MathUtils, Matrix4 as Matrix42, Mesh as Mesh3, Object3D as Object3D2, Quaternion as Quaternion2, Vector3 as Vector33 } from "three";
2777
+ import { Euler as Euler3, InstancedMesh, MathUtils, Matrix4 as Matrix43, Mesh as Mesh3, Object3D as Object3D2, Quaternion as Quaternion3, Vector3 as Vector35 } from "three";
2468
2778
 
2469
2779
  // src/instance/InstanceReference.ts
2470
2780
  var InstanceReference = class {
@@ -2509,20 +2819,9 @@ function isInstanceReference(obj) {
2509
2819
  return obj.isInstanceReference;
2510
2820
  }
2511
2821
 
2512
- // src/error/MineRenderError.ts
2513
- var MineRenderError = class _MineRenderError extends Error {
2514
- constructor(msg) {
2515
- super(msg);
2516
- Object.setPrototypeOf(this, _MineRenderError.prototype);
2517
- }
2518
- get name() {
2519
- return "MineRenderError";
2520
- }
2521
- };
2522
-
2523
2822
  // src/renderer/SceneObject.ts
2524
- var import_ts_deepmerge4 = __toESM(require_dist());
2525
- var p7 = prefix("SceneObject");
2823
+ var import_ts_deepmerge3 = __toESM(require_dist());
2824
+ var p6 = prefix("SceneObject");
2526
2825
  var _SceneObject = class _SceneObject extends Object3D2 {
2527
2826
  constructor(options) {
2528
2827
  super();
@@ -2530,7 +2829,7 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2530
2829
  this.materialCallbacks = {};
2531
2830
  this._isInstanced = false;
2532
2831
  this._instanceCounter = 0;
2533
- this.options = (0, import_ts_deepmerge4.default)({}, _SceneObject.DEFAULT_OPTIONS, options ?? {});
2832
+ this.options = (0, import_ts_deepmerge3.default)({}, _SceneObject.DEFAULT_OPTIONS, options ?? {});
2534
2833
  console.log("SceneObject options", this.options);
2535
2834
  }
2536
2835
  set scene(scene) {
@@ -2624,6 +2923,7 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2624
2923
  }
2625
2924
  createInstancedMesh(name, geometry, material, count) {
2626
2925
  const mesh = new InstancedMesh(geometry, material, count);
2926
+ mesh.count = 0;
2627
2927
  if (name) {
2628
2928
  mesh.name = `mesh:${name}`;
2629
2929
  }
@@ -2644,15 +2944,8 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2644
2944
  return this.toggleObjectVisibility(this.getMeshByName(name), visible);
2645
2945
  }
2646
2946
  iterateAllMeshes(cb) {
2647
- this.children.forEach((obj) => {
2648
- if (obj.isMesh) {
2649
- cb(obj);
2650
- }
2651
- obj.children.forEach((obj1) => {
2652
- if (obj1.isMesh) {
2653
- cb(obj1);
2654
- }
2655
- });
2947
+ this.traverse((obj) => {
2948
+ if (isMesh(obj)) cb(obj);
2656
2949
  });
2657
2950
  }
2658
2951
  //</editor-fold>
@@ -2700,10 +2993,14 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2700
2993
  nextInstance() {
2701
2994
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2702
2995
  const i = this._instanceCounter++;
2703
- this.setMatrixAt(i, new Matrix42());
2704
- console.debug(p7, "nextInstance " + i);
2996
+ this.setMatrixAt(i, new Matrix43());
2997
+ const mesh = this.children[0];
2998
+ if (mesh && isInstancedMesh(mesh)) {
2999
+ mesh.count = Math.min(this._instanceCounter, mesh.instanceMatrix.count);
3000
+ }
3001
+ console.debug(p6, "nextInstance " + i);
2705
3002
  if (i === this.options.maxInstanceCount) {
2706
- console.warn(p7, "Max instance count reached for " + this);
3003
+ console.warn(p6, "Max instance count reached for " + this);
2707
3004
  }
2708
3005
  if (this.scene) {
2709
3006
  this.scene.stats.instanceCount++;
@@ -2713,7 +3010,7 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2713
3010
  }
2714
3011
  //</editor-fold>
2715
3012
  //<editor-fold desc="TRANSFORMATION">
2716
- getMatrixAt(index, matrix = new Matrix42()) {
3013
+ getMatrixAt(index, matrix = new Matrix43()) {
2717
3014
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2718
3015
  const child = this.children[0];
2719
3016
  if (child && isInstancedMesh(child)) {
@@ -2732,16 +3029,16 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2732
3029
  }
2733
3030
  setPositionRotationScaleAt(index, position, rotation, scale) {
2734
3031
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2735
- const oldPosition = new Vector33();
2736
- const oldRotation = new Quaternion2();
2737
- const oldScale = new Vector33();
2738
- const matrix = new Matrix42();
3032
+ const oldPosition = new Vector35();
3033
+ const oldRotation = new Quaternion3();
3034
+ const oldScale = new Vector35();
3035
+ const matrix = new Matrix43();
2739
3036
  if (!scale || !rotation || !position) {
2740
3037
  this.getMatrixAt(index, matrix).decompose(oldPosition, oldRotation, oldScale);
2741
3038
  }
2742
3039
  matrix.compose(
2743
3040
  position ? position : oldPosition,
2744
- rotation ? new Quaternion2().setFromEuler(rotation) : oldRotation,
3041
+ rotation ? new Quaternion3().setFromEuler(rotation) : oldRotation,
2745
3042
  scale ? scale : oldScale
2746
3043
  );
2747
3044
  this.setMatrixAt(index, matrix);
@@ -2750,7 +3047,7 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2750
3047
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2751
3048
  this.setPositionRotationScaleAt(index, position);
2752
3049
  }
2753
- getPositionAt(index, vector = new Vector33()) {
3050
+ getPositionAt(index, vector = new Vector35()) {
2754
3051
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2755
3052
  const matrix = this.getMatrixAt(index);
2756
3053
  vector.setFromMatrixPosition(matrix);
@@ -2760,7 +3057,7 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2760
3057
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2761
3058
  this.setPositionRotationScaleAt(index, void 0, rotation, void 0);
2762
3059
  }
2763
- getRotationAt(index, euler = new Euler()) {
3060
+ getRotationAt(index, euler = new Euler3()) {
2764
3061
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2765
3062
  const matrix = this.getMatrixAt(index);
2766
3063
  euler.setFromRotationMatrix(matrix);
@@ -2770,7 +3067,7 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2770
3067
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2771
3068
  this.setPositionRotationScaleAt(index, void 0, void 0, scale);
2772
3069
  }
2773
- getScaleAt(index, vector = new Vector33()) {
3070
+ getScaleAt(index, vector = new Vector35()) {
2774
3071
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2775
3072
  const matrix = this.getMatrixAt(index);
2776
3073
  vector.setFromMatrixScale(matrix);
@@ -2879,7 +3176,7 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2879
3176
  }
2880
3177
  //</editor-fold>
2881
3178
  };
2882
- _SceneObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge4.default)({}, {
3179
+ _SceneObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge3.default)({}, {
2883
3180
  instanceMeshes: true,
2884
3181
  maxInstanceCount: 50,
2885
3182
  mergeMeshes: true,
@@ -2891,16 +3188,15 @@ function isSceneObject(obj) {
2891
3188
  }
2892
3189
 
2893
3190
  // src/entity/scene/EntityObject.ts
2894
- var import_ts_deepmerge5 = __toESM(require_dist());
3191
+ var import_ts_deepmerge4 = __toESM(require_dist());
2895
3192
  import { Object3D as Object3D3 } from "three";
2896
- import * as THREE2 from "three";
2897
- var EntityObject = class extends SceneObject {
3193
+ var _EntityObject = class _EntityObject extends SceneObject {
2898
3194
  constructor(entity, options) {
2899
3195
  super();
2900
3196
  this.entity = entity;
2901
3197
  this.isEntityObject = true;
2902
3198
  this.meshesCreated = false;
2903
- this.options = (0, import_ts_deepmerge5.default)({}, SceneObject.DEFAULT_OPTIONS, options ?? {});
3199
+ this.options = (0, import_ts_deepmerge4.default)({}, _EntityObject.DEFAULT_OPTIONS, options ?? {});
2904
3200
  }
2905
3201
  async init() {
2906
3202
  this.createMeshes();
@@ -2909,65 +3205,78 @@ var EntityObject = class extends SceneObject {
2909
3205
  dispose() {
2910
3206
  super.dispose();
2911
3207
  }
2912
- async loadTextures() {
2913
- this.imageData = await ModelTextures.get(new AssetKey(
2914
- this.entity.key?.namespace ?? DEFAULT_NAMESPACE,
2915
- this.entity.key.path,
2916
- //TODO: texture may differ from entity name; most of them are in subdirectories for multiple variants etc.
3208
+ get textureKey() {
3209
+ if (this.entity.texture) return this.entity.texture;
3210
+ const key = this.entity.key;
3211
+ return new AssetKey(
3212
+ key.namespace,
3213
+ isAssetKey(key) ? key.getFullPath() : key.path,
2917
3214
  "textures",
2918
3215
  "entity",
2919
3216
  "assets",
2920
3217
  ".png"
2921
- ));
3218
+ );
3219
+ }
3220
+ async loadTextures() {
3221
+ this.imageData = await ModelTextures.get(this.textureKey);
2922
3222
  }
2923
- //TODO: abstract this, since extracted model data can be used for entities, blocks, players
2924
3223
  createMeshes(force = false) {
2925
3224
  if (this.meshesCreated && !force) return;
2926
- const mat = Materials.MISSING_TEXTURE;
2927
- for (let partName in this.entity.parts) {
2928
- const part = this.entity.parts[partName];
2929
- const texture = new MinecraftCubeTexture(part.textureOffsetU, part.textureOffsetV, part.textureWidth, part.textureHeight);
2930
- const partAnchor = new Object3D3();
2931
- partAnchor.position.x = part.pivotX;
2932
- partAnchor.position.y = part.pivotY;
2933
- partAnchor.position.z = part.pivotZ;
2934
- for (let cube of part.cubes) {
2935
- const w = cube.maxX - cube.minX;
2936
- const h = cube.maxY - cube.minY;
2937
- const l = cube.maxZ - cube.minZ;
2938
- const uv = texture.toUvArray(w, h, l);
2939
- const cubeGeo = this._getBoxGeometryForDimensionsAndUv(w, h, l, uv).clone();
2940
- cubeGeo.applyMatrix4(new THREE2.Matrix4().makeTranslation(w / 2, h / 2, l / 2));
2941
- cubeGeo.applyMatrix4(new THREE2.Matrix4().makeTranslation(cube.minX, cube.minY, cube.minZ));
2942
- applyModelPartRotation(part, cubeGeo);
2943
- const mesh = this.createMesh(partName, cubeGeo, mat);
2944
- partAnchor.add(mesh);
2945
- if (this.options.wireframe) {
2946
- addWireframeToMesh(cubeGeo, mesh);
2947
- }
2948
- }
2949
- this.add(partAnchor);
2950
- }
3225
+ const modelRoot = new Object3D3();
3226
+ if (this.options.flip) modelRoot.scale.set(-1, -1, 1);
3227
+ this.add(modelRoot);
3228
+ this.createPart("root", this.entity.layer.root, modelRoot, this.entity.layer.texture, Materials.MISSING_TEXTURE);
2951
3229
  this.meshesCreated = true;
2952
3230
  }
2953
- async applyTextures() {
2954
- const textureAsset = await ModelTextures.preload(new AssetKey(
2955
- this.entity.key?.namespace ?? DEFAULT_NAMESPACE,
2956
- this.entity.key.path,
2957
- "textures",
2958
- "entity",
2959
- "assets",
2960
- ".png"
2961
- ));
2962
- if (textureAsset) {
2963
- let mat = Materials.getImage({ texture: { src: textureAsset.src } });
2964
- this.iterateAllMeshes((mesh) => {
2965
- mesh.material = mat;
2966
- });
3231
+ createPart(name, part, parent, textureSize, material) {
3232
+ const anchor = this.createGroup(name);
3233
+ anchor.position.fromArray(part.pose.offset);
3234
+ anchor.rotation.set(...part.pose.rotation, "ZYX");
3235
+ anchor.scale.fromArray(part.pose.scale ?? [1, 1, 1]);
3236
+ parent.add(anchor);
3237
+ const size = part.texture ?? textureSize;
3238
+ for (const cube of part.cubes) {
3239
+ const [width, height, depth] = cube.size;
3240
+ const [growX, growY, growZ] = cube.grow ?? [0, 0, 0];
3241
+ const texture = new MinecraftCubeTexture(...cube.uv, ...size);
3242
+ const uv = size[0] === 0 || size[1] === 0 ? new Array(48).fill(0) : texture.toUvArray(width, height, depth, cube.mirror);
3243
+ const geometry = this._getBoxGeometryForDimensionsAndUv(
3244
+ width + growX * 2,
3245
+ height + growY * 2,
3246
+ depth + growZ * 2,
3247
+ uv
3248
+ ).clone();
3249
+ geometry.translate(cube.origin[0] + width / 2, cube.origin[1] + height / 2, cube.origin[2] + depth / 2);
3250
+ const mesh = this.createMesh(name, geometry, material);
3251
+ anchor.add(mesh);
3252
+ if (this.options.wireframe) addWireframeToMesh(geometry, mesh);
3253
+ }
3254
+ for (const [childName, child] of Object.entries(part.children)) {
3255
+ this.createPart(childName, child, anchor, size, material);
2967
3256
  }
2968
3257
  }
3258
+ async applyTextures() {
3259
+ const assetKeyStr = this.textureKey.serialize();
3260
+ const keyStr = `entity:${assetKeyStr}`;
3261
+ let mat = Caching.materialCache.getIfPresent(keyStr);
3262
+ if (!mat) {
3263
+ const pending = this.loadTextures();
3264
+ const cachedAsset = Caching.textureAssetCache.getIfPresent(assetKeyStr);
3265
+ await pending;
3266
+ if (!this.imageData) return;
3267
+ const canvas = this.imageData.data.canvas;
3268
+ const createMaterial = () => Materials.createBasicCanvasMaterial(canvas);
3269
+ mat = cachedAsset && Caching.textureAssetCache.getIfPresent(assetKeyStr) === cachedAsset ? Caching.materialCache.get(keyStr, createMaterial) : createMaterial();
3270
+ this.imageData = void 0;
3271
+ }
3272
+ this.iterateAllMeshes((mesh) => {
3273
+ mesh.material = mat;
3274
+ });
3275
+ this.notifyDirty();
3276
+ }
2969
3277
  };
2970
- EntityObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge5.default)({}, SceneObject.DEFAULT_OPTIONS, {});
3278
+ _EntityObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge4.default)({}, SceneObject.DEFAULT_OPTIONS, { flip: true });
3279
+ var EntityObject = _EntityObject;
2971
3280
  function isEntityObject(obj) {
2972
3281
  return obj.isEntityObject;
2973
3282
  }
@@ -2979,7 +3288,7 @@ var GuiObject = class extends SceneObject {
2979
3288
 
2980
3289
  // src/inspector/SceneInspector.ts
2981
3290
  import { Raycaster, Vector2 as Vector22 } from "three";
2982
- var p8 = prefix("SceneInspector");
3291
+ var p7 = prefix("SceneInspector");
2983
3292
  var help = "<span>Ctrl+Click to Select<br/></span><br/>";
2984
3293
  var SceneInspector = class {
2985
3294
  constructor(renderer) {
@@ -3048,7 +3357,7 @@ var SceneInspector = class {
3048
3357
  }
3049
3358
  selectObject(targetObject, targetIntersection) {
3050
3359
  this.selectedObject = targetObject;
3051
- console.log(p8, "selected", targetObject);
3360
+ console.log(p7, "selected", targetObject);
3052
3361
  this.redraw(targetObject, targetIntersection);
3053
3362
  }
3054
3363
  redraw(targetObject, targetIntersection) {
@@ -3346,7 +3655,7 @@ var SceneStatsDisplay = class {
3346
3655
  };
3347
3656
 
3348
3657
  // src/instance/InstanceManager.ts
3349
- var p9 = prefix("InstanceManager");
3658
+ var p8 = prefix("InstanceManager");
3350
3659
  var InstanceManager = class {
3351
3660
  constructor() {
3352
3661
  this.isInstanceManager = true;
@@ -3354,7 +3663,7 @@ var InstanceManager = class {
3354
3663
  }
3355
3664
  async get(key) {
3356
3665
  if (key in this.instanceCache) {
3357
- console.debug(p9, "key in cache", key);
3666
+ console.debug(p8, "key in cache", key);
3358
3667
  return (await this.instanceCache[key]).nextInstance();
3359
3668
  }
3360
3669
  return void 0;
@@ -3363,13 +3672,17 @@ var InstanceManager = class {
3363
3672
  if (key in this.instanceCache) {
3364
3673
  return await this.get(key);
3365
3674
  }
3366
- console.debug(p9, "key not in cache", key);
3367
- this.instanceCache[key] = new Promise(async (resolve) => {
3368
- const obj = await supplier();
3369
- const instance = obj.nextInstance();
3370
- resolve(instance);
3371
- });
3372
- return await this.instanceCache[key];
3675
+ console.debug(p8, "key not in cache", key);
3676
+ const pending = Promise.resolve().then(supplier).then((obj) => obj.nextInstance());
3677
+ this.instanceCache[key] = pending;
3678
+ try {
3679
+ return await pending;
3680
+ } catch (error) {
3681
+ if (this.instanceCache[key] === pending) {
3682
+ delete this.instanceCache[key];
3683
+ }
3684
+ throw error;
3685
+ }
3373
3686
  }
3374
3687
  reset() {
3375
3688
  for (let instanceCacheKey in this.instanceCache) {
@@ -3386,20 +3699,6 @@ function isInstanceable(obj) {
3386
3699
  return "nextInstance" in obj;
3387
3700
  }
3388
3701
 
3389
- // src/model/DisplayPosition.ts
3390
- var DisplayPosition = /* @__PURE__ */ ((DisplayPosition2) => {
3391
- DisplayPosition2["THIRDPERSON_RIGHTHAND"] = "thirdperson_righthand";
3392
- DisplayPosition2["THIRDPERSON_LEFTHAND"] = "thirdperson_lefthand";
3393
- DisplayPosition2["FIRSTPERSON_RIGHTHAND"] = "firstperson_righthand";
3394
- DisplayPosition2["FIRSTPERSON_LEFTHAND"] = "firstperson_lefthand";
3395
- DisplayPosition2["GUI"] = "gui";
3396
- DisplayPosition2["HEAD"] = "head";
3397
- DisplayPosition2["GROUND"] = "ground";
3398
- DisplayPosition2["FIXED"] = "fixed";
3399
- return DisplayPosition2;
3400
- })(DisplayPosition || {});
3401
- var DISPLAY_POSITIONS = Object.values(DisplayPosition);
3402
-
3403
3702
  // src/model/GuiLight.ts
3404
3703
  var GuiLight = /* @__PURE__ */ ((GuiLight2) => {
3405
3704
  GuiLight2["FRONT"] = "front";
@@ -3422,8 +3721,93 @@ function isQuadArray(obj) {
3422
3721
  return Array.isArray(obj) && obj.length === 4;
3423
3722
  }
3424
3723
 
3724
+ // src/model/block/blockTints.json
3725
+ var blockTints_default = {
3726
+ "minecraft:large_fern": { source: "grass" },
3727
+ "minecraft:tall_grass": { source: "grass" },
3728
+ "minecraft:grass_block": { source: "grass" },
3729
+ "minecraft:fern": { source: "grass" },
3730
+ "minecraft:grass": { source: "grass" },
3731
+ "minecraft:short_grass": { source: "grass" },
3732
+ "minecraft:potted_fern": { source: "grass" },
3733
+ "minecraft:bush": { source: "grass" },
3734
+ "minecraft:pink_petals": { source: "grass", untinted: [0] },
3735
+ "minecraft:wildflowers": { source: "grass", untinted: [0] },
3736
+ "minecraft:spruce_leaves": { source: "constant", color: 6396257 },
3737
+ "minecraft:birch_leaves": { source: "constant", color: 8431445 },
3738
+ "minecraft:oak_leaves": { source: "constant", color: 4764952 },
3739
+ "minecraft:jungle_leaves": { source: "constant", color: 4764952 },
3740
+ "minecraft:acacia_leaves": { source: "constant", color: 4764952 },
3741
+ "minecraft:dark_oak_leaves": { source: "constant", color: 4764952 },
3742
+ "minecraft:vine": { source: "constant", color: 4764952 },
3743
+ "minecraft:mangrove_leaves": { source: "constant", color: 4764952 },
3744
+ "minecraft:leaf_litter": { source: "constant", color: 6044722 },
3745
+ "minecraft:water_cauldron": { source: "constant", color: 4159204 },
3746
+ "minecraft:redstone_wire": { source: "redstone" },
3747
+ "minecraft:attached_melon_stem": { source: "constant", color: 14731036 },
3748
+ "minecraft:attached_pumpkin_stem": { source: "constant", color: 14731036 },
3749
+ "minecraft:melon_stem": { source: "stem" },
3750
+ "minecraft:pumpkin_stem": { source: "stem" },
3751
+ "minecraft:lily_pad": { source: "constant", color: 7455580 }
3752
+ };
3753
+
3754
+ // src/model/block/BlockTints.ts
3755
+ var rules = blockTints_default;
3756
+ var BlockTints = class {
3757
+ /** Resolves block preview colors without biome context; explicit tints take precedence. */
3758
+ static async get(key, state, model, tints) {
3759
+ const rule = key && rules[key.toNamespacedString()];
3760
+ if (!key || !rule) return tints;
3761
+ const indices = /* @__PURE__ */ new Set();
3762
+ for (const element of model.elements ?? []) {
3763
+ for (const face of Object.values(element.faces)) {
3764
+ const index = face?.tintindex;
3765
+ if (index !== void 0 && index >= 0 && tints?.[index] === void 0 && !rule.untinted?.includes(index)) {
3766
+ indices.add(index);
3767
+ }
3768
+ }
3769
+ }
3770
+ if (!indices.size) return tints;
3771
+ let color;
3772
+ switch (rule.source) {
3773
+ case "constant":
3774
+ color = rule.color;
3775
+ break;
3776
+ case "grass":
3777
+ color = await this.grassColor(key);
3778
+ break;
3779
+ case "redstone": {
3780
+ const power = Number(state.power ?? 0) / 15;
3781
+ const red = power * 0.6 + (power > 0 ? 0.4 : 0.3);
3782
+ const green = Math.max(0, power * power * 0.7 - 0.5);
3783
+ const blue = Math.max(0, power * power * 0.6 - 0.7);
3784
+ color = Math.floor(red * 255) << 16 | Math.floor(green * 255) << 8 | Math.floor(blue * 255);
3785
+ break;
3786
+ }
3787
+ case "stem": {
3788
+ const age = Number(state.age ?? 0);
3789
+ color = age * 32 << 16 | 255 - age * 8 << 8 | age * 4;
3790
+ break;
3791
+ }
3792
+ }
3793
+ return { ...Object.fromEntries([...indices].map((index) => [index, color])), ...tints };
3794
+ }
3795
+ static async grassColor(origin) {
3796
+ const key = AssetKey.parse("textures", "minecraft:colormap/grass", origin);
3797
+ return await Caching.blockTintCache.get(key.serialize(), async () => {
3798
+ const image = await ModelTextures.get(key);
3799
+ if (!image) throw new MineRenderError(`Missing grass colormap: ${key.toNamespacedString()}`);
3800
+ if (image.width !== 256 || image.height !== 256) {
3801
+ throw new MineRenderError("Grass colormaps must be 256 \xD7 256 pixels");
3802
+ }
3803
+ const [red, green, blue] = image.data.getImageData(127, 127, 1, 1).data;
3804
+ return red << 16 | green << 8 | blue;
3805
+ });
3806
+ }
3807
+ };
3808
+
3425
3809
  // src/model/scene/ModelObject.ts
3426
- import { BoxGeometry as BoxGeometry5, Matrix4 as Matrix45 } from "three";
3810
+ import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute4, Color as Color3, Euler as Euler4, Matrix4 as Matrix44 } from "three";
3427
3811
 
3428
3812
  // src/three/BufferGeometryUtils.js
3429
3813
  import {
@@ -3435,7 +3819,7 @@ import {
3435
3819
  TriangleFanDrawMode,
3436
3820
  TriangleStripDrawMode,
3437
3821
  TrianglesDrawMode,
3438
- Vector3 as Vector35
3822
+ Vector3 as Vector36
3439
3823
  } from "three";
3440
3824
  function computeTangents(geometry) {
3441
3825
  geometry.computeTangents();
@@ -3754,15 +4138,15 @@ function computeMorphedAttributes(object) {
3754
4138
  console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
3755
4139
  return null;
3756
4140
  }
3757
- const _vA = new Vector35();
3758
- const _vB = new Vector35();
3759
- const _vC = new Vector35();
3760
- const _tempA = new Vector35();
3761
- const _tempB = new Vector35();
3762
- const _tempC = new Vector35();
3763
- const _morphA = new Vector35();
3764
- const _morphB = new Vector35();
3765
- const _morphC = new Vector35();
4141
+ const _vA = new Vector36();
4142
+ const _vB = new Vector36();
4143
+ const _vC = new Vector36();
4144
+ const _tempA = new Vector36();
4145
+ const _tempB = new Vector36();
4146
+ const _tempC = new Vector36();
4147
+ const _morphA = new Vector36();
4148
+ const _morphB = new Vector36();
4149
+ const _morphC = new Vector36();
3766
4150
  function _calculateMorphedAttributeData(object2, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
3767
4151
  _vA.fromBufferAttribute(attribute, a2);
3768
4152
  _vB.fromBufferAttribute(attribute, b2);
@@ -3968,15 +4352,16 @@ function computeMorphedAttributes(object) {
3968
4352
  }
3969
4353
 
3970
4354
  // src/model/scene/ModelObject.ts
3971
- var import_ts_deepmerge6 = __toESM(require_dist());
3972
- var p10 = prefix("ModelObject");
4355
+ var import_ts_deepmerge5 = __toESM(require_dist());
4356
+ var p9 = prefix("ModelObject");
3973
4357
  var _ModelObject = class _ModelObject extends SceneObject {
3974
4358
  constructor(originalModel, options) {
3975
4359
  super(options);
3976
4360
  this.originalModel = originalModel;
3977
4361
  this.isModelObject = true;
3978
4362
  this.meshesCreated = false;
3979
- this.options = (0, import_ts_deepmerge6.default)({}, _ModelObject.DEFAULT_OPTIONS, options ?? {});
4363
+ this.options = (0, import_ts_deepmerge5.default)({}, _ModelObject.DEFAULT_OPTIONS, options ?? {});
4364
+ if (this.options.tints) this.options.tints = { ...this.options.tints };
3980
4365
  console.log("ModelObject options", this.options);
3981
4366
  }
3982
4367
  async init() {
@@ -3998,17 +4383,36 @@ var _ModelObject = class _ModelObject extends SceneObject {
3998
4383
  createMeshes(force = false) {
3999
4384
  if (this.meshesCreated && !force) return;
4000
4385
  const mat = Materials.MISSING_TEXTURE;
4386
+ const displayTransform = this.options.displayPosition ? DisplayTransforms.getMatrix(this.originalModel.display, this.options.displayPosition) : void 0;
4001
4387
  let allGeos = [];
4002
4388
  if (this.atlas) {
4003
4389
  if (this.atlas.model.elements) {
4004
4390
  this.atlas.model.elements?.forEach((el) => {
4005
4391
  const elGeo = this._getBoxGeometryFromElement(el).clone();
4006
- elGeo.applyMatrix4(new Matrix45().makeTranslation((el.to[0] - el.from[0]) / 2, (el.to[1] - el.from[1]) / 2, (el.to[2] - el.from[2]) / 2));
4007
- elGeo.applyMatrix4(new Matrix45().makeTranslation(el.from[0], el.from[1], el.from[2]));
4392
+ if (this.options.uvLockRotation) {
4393
+ UVMapper.lockUvs(elGeo, el.faces, this.atlas, new Euler4(...this.options.uvLockRotation));
4394
+ }
4395
+ if (this.options.tints) {
4396
+ const colors = new Float32Array(elGeo.getAttribute("position").count * 3).fill(1);
4397
+ for (const [faceIndex, faceName] of CUBE_FACES.entries()) {
4398
+ const tintIndex = el.faces[faceName]?.tintindex;
4399
+ if (tintIndex === void 0 || tintIndex < 0) continue;
4400
+ const tint = this.options.tints[tintIndex];
4401
+ if (tint === void 0) continue;
4402
+ const color = new Color3(tint);
4403
+ for (let vertex = 0; vertex < 4; vertex++) {
4404
+ color.toArray(colors, (faceIndex * 4 + vertex) * 3);
4405
+ }
4406
+ }
4407
+ elGeo.setAttribute("color", new BufferAttribute4(colors, 3));
4408
+ }
4409
+ elGeo.applyMatrix4(new Matrix44().makeTranslation((el.to[0] - el.from[0]) / 2, (el.to[1] - el.from[1]) / 2, (el.to[2] - el.from[2]) / 2));
4410
+ elGeo.applyMatrix4(new Matrix44().makeTranslation(el.from[0], el.from[1], el.from[2]));
4008
4411
  if (el.rotation) {
4009
4412
  applyElementRotation(el.rotation, elGeo);
4010
4413
  }
4011
- elGeo.applyMatrix4(new Matrix45().makeTranslation(-8, -8, -8));
4414
+ elGeo.applyMatrix4(new Matrix44().makeTranslation(-8, -8, -8));
4415
+ if (displayTransform) DisplayTransforms.apply(elGeo, displayTransform);
4012
4416
  if (this.options.mergeMeshes) {
4013
4417
  allGeos.push(elGeo);
4014
4418
  } else {
@@ -4019,10 +4423,10 @@ var _ModelObject = class _ModelObject extends SceneObject {
4019
4423
  }
4020
4424
  });
4021
4425
  } else {
4022
- console.debug(p10, this.atlas.model, "has no elements");
4426
+ console.debug(p9, this.atlas.model, "has no elements");
4023
4427
  }
4024
4428
  } else {
4025
- console.debug(p10, "Missing atlas for", this);
4429
+ console.debug(p9, "Missing atlas for", this);
4026
4430
  }
4027
4431
  if (this.options.mergeMeshes) {
4028
4432
  let combinedGeo;
@@ -4030,6 +4434,7 @@ var _ModelObject = class _ModelObject extends SceneObject {
4030
4434
  combinedGeo = mergeBufferGeometries(allGeos);
4031
4435
  } else {
4032
4436
  combinedGeo = new BoxGeometry5(16, 16, 16);
4437
+ if (displayTransform) DisplayTransforms.apply(combinedGeo, displayTransform);
4033
4438
  }
4034
4439
  combinedGeo.computeBoundingBox();
4035
4440
  let mesh;
@@ -4059,6 +4464,7 @@ var _ModelObject = class _ModelObject extends SceneObject {
4059
4464
  /*TODO: get this from render options*/
4060
4465
  );
4061
4466
  this.iterateAllMeshes((mesh) => {
4467
+ if (mesh.geometry.hasAttribute("color")) mat.vertexColors = true;
4062
4468
  mesh.material = mat;
4063
4469
  });
4064
4470
  if (this.atlas.hasAnimation) {
@@ -4074,16 +4480,26 @@ var _ModelObject = class _ModelObject extends SceneObject {
4074
4480
  }
4075
4481
  }
4076
4482
  };
4077
- _ModelObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge6.default)({}, SceneObject.DEFAULT_OPTIONS, {});
4483
+ _ModelObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge5.default)({}, SceneObject.DEFAULT_OPTIONS, {});
4078
4484
  var ModelObject = _ModelObject;
4079
4485
  function isModelObject(obj) {
4080
4486
  return obj.isModelObject;
4081
4487
  }
4082
4488
 
4083
4489
  // src/model/block/scene/BlockObject.ts
4084
- var import_ts_deepmerge7 = __toESM(require_dist());
4085
- import { Euler as Euler3, Matrix4 as Matrix46, Vector3 as Vector36 } from "three";
4086
- var p11 = prefix("BlockObject");
4490
+ var import_ts_deepmerge6 = __toESM(require_dist());
4491
+ import { Euler as Euler5, Matrix4 as Matrix45, Vector3 as Vector37 } from "three";
4492
+ var p10 = prefix("BlockObject");
4493
+ function matchesCondition(condition, state) {
4494
+ return Object.entries(condition).every(([key, value]) => {
4495
+ if (Array.isArray(value)) {
4496
+ if (key === "OR") return value.some((child) => matchesCondition(child, state));
4497
+ if (key === "AND") return value.every((child) => matchesCondition(child, state));
4498
+ return false;
4499
+ }
4500
+ return typeof value === "string" && state[key] !== void 0 && value.split("|").includes(`${state[key]}`);
4501
+ });
4502
+ }
4087
4503
  var _BlockObject = class _BlockObject extends SceneObject {
4088
4504
  constructor(blockState, options) {
4089
4505
  super(options);
@@ -4097,11 +4513,11 @@ var _BlockObject = class _BlockObject extends SceneObject {
4097
4513
  this._instanceModel = [];
4098
4514
  this._variants = [];
4099
4515
  this._models = [];
4100
- this.options = (0, import_ts_deepmerge7.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
4516
+ this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
4101
4517
  }
4102
4518
  async init() {
4103
4519
  if (this.options.applyDefaultState) {
4104
- const defaultState = this.blockState.key ? BlockStates.getDefaultState(this.blockState.key) : void 0;
4520
+ const defaultState = this.blockState.key ? await BlockStates.getDefaultState(this.blockState.key) : void 0;
4105
4521
  if (defaultState && Object.keys(defaultState).length > 0) {
4106
4522
  const state = {};
4107
4523
  for (let k in defaultState) {
@@ -4112,15 +4528,9 @@ var _BlockObject = class _BlockObject extends SceneObject {
4112
4528
  if (this.blockState.variants) {
4113
4529
  await this.setState(Object.keys(this.blockState.variants)[0]);
4114
4530
  } else if (this.blockState.multipart) {
4115
- for (let part of this.blockState.multipart) {
4116
- if (part.when && !("OR" in part.when)) {
4117
- const k = Object.keys(part.when)[0];
4118
- if (k) {
4119
- await this.setState(k, part.when[k].split("|")[0]);
4120
- break;
4121
- }
4122
- }
4123
- }
4531
+ const condition = this.blockState.multipart.map((part) => part.when).find((when) => when !== void 0 && Object.values(when).every((value) => typeof value === "string"));
4532
+ const state = Object.fromEntries(Object.entries(condition ?? {}).map(([key, value]) => [key, value.split("|")[0]]));
4533
+ await this.setState(state);
4124
4534
  }
4125
4535
  }
4126
4536
  } else {
@@ -4135,7 +4545,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4135
4545
  if (isModelObject(model)) {
4136
4546
  model.removeFromScene();
4137
4547
  } else if (isInstanceReference(model)) {
4138
- model.setScale(new Vector36(0, 0, 0));
4548
+ model.setScale(new Vector37(0, 0, 0));
4139
4549
  }
4140
4550
  }
4141
4551
  super.removeFromScene();
@@ -4177,55 +4587,11 @@ var _BlockObject = class _BlockObject extends SceneObject {
4177
4587
  } else if (this.blockState.multipart) {
4178
4588
  for (let part of this.blockState.multipart) {
4179
4589
  if (!part.apply) {
4180
- console.debug(p11, "Missing apply for blockState part", part);
4590
+ console.debug(p10, "Missing apply for blockState part", part);
4181
4591
  continue;
4182
4592
  }
4183
- if (!part.when) {
4593
+ if (!part.when || matchesCondition(part.when, state)) {
4184
4594
  out.push(this.getSingleVariant(part.apply));
4185
- } else {
4186
- let matches = true;
4187
- if ("OR" in part.when) {
4188
- const or = part.when.OR;
4189
- let anyMatch = false;
4190
- for (let o of or) {
4191
- for (let k in o) {
4192
- const split = o[k].split("|");
4193
- let m = false;
4194
- for (let s of split) {
4195
- if (`${state[k]}` === `${s}`) {
4196
- m = true;
4197
- break;
4198
- }
4199
- }
4200
- if (m) {
4201
- anyMatch = true;
4202
- break;
4203
- }
4204
- }
4205
- }
4206
- if (!anyMatch) {
4207
- matches = false;
4208
- }
4209
- } else {
4210
- for (let k in part.when) {
4211
- const split = part.when[k].split("|");
4212
- let m = false;
4213
- for (let s of split) {
4214
- if (`${state[k]}` === `${s}`) {
4215
- m = true;
4216
- break;
4217
- }
4218
- }
4219
- if (!m) {
4220
- matches = false;
4221
- break;
4222
- }
4223
- }
4224
- }
4225
- if (matches) {
4226
- const variants = part.apply;
4227
- out.push(this.getSingleVariant(variants));
4228
- }
4229
4595
  }
4230
4596
  }
4231
4597
  }
@@ -4240,7 +4606,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4240
4606
  }
4241
4607
  for (let model of this._models) {
4242
4608
  if (isInstanceReference(model)) {
4243
- model.setScale(new Vector36(0, 0, 0));
4609
+ model.setScale(new Vector37(0, 0, 0));
4244
4610
  } else {
4245
4611
  model.dispose();
4246
4612
  }
@@ -4255,11 +4621,21 @@ var _BlockObject = class _BlockObject extends SceneObject {
4255
4621
  }
4256
4622
  }
4257
4623
  getSingleVariant(variants) {
4258
- if (Array.isArray(variants)) {
4259
- return variants[0];
4260
- } else {
4261
- return variants;
4262
- }
4624
+ if (!Array.isArray(variants)) return variants;
4625
+ if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
4626
+ const total = variants.reduce((sum, variant) => {
4627
+ const weight = variant.weight ?? 1;
4628
+ if (!Number.isInteger(weight) || weight < 1) {
4629
+ throw new MineRenderError(`Invalid blockstate variant weight: ${weight}`);
4630
+ }
4631
+ return sum + weight;
4632
+ }, 0);
4633
+ let choice = Math.random() * total;
4634
+ for (const variant of variants) {
4635
+ choice -= variant.weight ?? 1;
4636
+ if (choice < 0) return variant;
4637
+ }
4638
+ return variants[variants.length - 1];
4263
4639
  }
4264
4640
  // @deprecated
4265
4641
  async createAVariant(variants, instanceInfo) {
@@ -4267,28 +4643,27 @@ var _BlockObject = class _BlockObject extends SceneObject {
4267
4643
  await this.createVariant(variant);
4268
4644
  }
4269
4645
  async createVariant(variant) {
4270
- const model = await Models.getMerged(AssetKey.parse("models", variant.model));
4271
- const obj = await this.scene.addModel(model, this.options);
4272
- if (isInstanceReference(obj) || obj.isInstanced) {
4273
- this._isInstanced = true;
4274
- this._instanceCounter = 1;
4275
- }
4276
- let rotation = new Euler3(0, 0, 0);
4646
+ const rotation = new Euler5();
4277
4647
  if (typeof variant.x !== "undefined") {
4278
4648
  rotation.x = toRadians(clampRotationDegrees(variant.x));
4279
4649
  }
4280
4650
  if (typeof variant.y !== "undefined") {
4281
- if (typeof variant.x !== "undefined") {
4282
- rotation.y = toRadians(clampRotationDegrees(variant.y));
4283
- } else {
4284
- rotation.y = toRadians(clampRotationDegrees(360 - variant.y));
4285
- }
4651
+ rotation.y = toRadians(clampRotationDegrees(typeof variant.x !== "undefined" ? variant.y : 360 - variant.y));
4652
+ }
4653
+ const modelKey = AssetKey.parse("models", variant.model);
4654
+ modelKey.root = this.blockState.key?.root;
4655
+ const model = await Models.getMerged(modelKey);
4656
+ const options = {
4657
+ ...this.options,
4658
+ tints: model ? await BlockTints.get(this.blockState.key, this.state, model, this.options.tints) : this.options.tints,
4659
+ uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
4660
+ };
4661
+ const obj = await this.scene.addModel(model, options);
4662
+ if (isInstanceReference(obj) || obj.isInstanced) {
4663
+ this._isInstanced = true;
4664
+ this._instanceCounter = 1;
4286
4665
  }
4287
4666
  obj.setRotation(rotation);
4288
- setTimeout(() => {
4289
- obj.setRotation(rotation);
4290
- }, 150);
4291
- console.log(obj);
4292
4667
  return obj;
4293
4668
  }
4294
4669
  async resetState() {
@@ -4357,7 +4732,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4357
4732
  setPositionRotationScaleAt(index, position, rotation, scale) {
4358
4733
  super.setPositionRotationScaleAt(index, position, rotation, scale);
4359
4734
  }
4360
- getMatrixAt(index, matrix = new Matrix46()) {
4735
+ getMatrixAt(index, matrix = new Matrix45()) {
4361
4736
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
4362
4737
  const child = this._models[0];
4363
4738
  if (child && isModelObject(child)) {
@@ -4378,15 +4753,16 @@ var _BlockObject = class _BlockObject extends SceneObject {
4378
4753
  }
4379
4754
  }
4380
4755
  setPosition(position) {
4381
- super.setPosition(position);
4756
+ this.position.copy(position);
4382
4757
  for (let model of this._models) {
4383
4758
  model.setPosition(position);
4384
4759
  }
4760
+ this.notifyDirty();
4385
4761
  }
4386
4762
  //TODO: should override rotation + scale methods
4387
4763
  //TODO
4388
4764
  };
4389
- _BlockObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge7.default)({}, ModelObject.DEFAULT_OPTIONS, {
4765
+ _BlockObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge6.default)({}, ModelObject.DEFAULT_OPTIONS, {
4390
4766
  applyDefaultState: true
4391
4767
  });
4392
4768
  var BlockObject = _BlockObject;
@@ -4436,7 +4812,7 @@ var StructureParser = class {
4436
4812
 
4437
4813
  // src/renderer/MineRenderScene.ts
4438
4814
  import { Scene as Scene2 } from "three";
4439
- var import_ts_deepmerge9 = __toESM(require_dist());
4815
+ var import_ts_deepmerge8 = __toESM(require_dist());
4440
4816
 
4441
4817
  // src/skin/SkinPart.ts
4442
4818
  var SkinPart = /* @__PURE__ */ ((SkinPart2) => {
@@ -5054,90 +5430,80 @@ var baseSkinCoordinates = {
5054
5430
  ]
5055
5431
  },
5056
5432
  up: {
5057
- uv: [
5058
- 4,
5059
- 36,
5060
- 8,
5061
- 32
5062
- ]
5063
- },
5064
- down: {
5065
- uv: [
5066
- 8,
5067
- 32,
5068
- 12,
5069
- 36
5070
- ],
5071
- rotation: 180
5072
- }
5073
- },
5074
- cape: {
5075
- //TODO
5076
- east: {
5077
- uv: [
5078
- 0,
5079
- 36,
5080
- 4,
5081
- 48
5082
- ]
5083
- },
5084
- west: {
5085
5433
  uv: [
5086
5434
  8,
5087
5435
  36,
5088
- 12,
5089
- 48
5090
- ]
5091
- },
5092
- north: {
5093
- uv: [
5094
5436
  4,
5095
- 36,
5096
- 8,
5097
- 48
5437
+ 32
5098
5438
  ]
5099
5439
  },
5100
- south: {
5440
+ down: {
5101
5441
  uv: [
5102
5442
  12,
5103
- 36,
5104
- 16,
5105
- 48
5106
- ]
5107
- },
5108
- up: {
5109
- uv: [
5110
- 4,
5111
5443
  32,
5112
5444
  8,
5113
5445
  36
5114
- ]
5115
- },
5116
- down: {
5117
- uv: [
5118
- 8,
5119
- 32,
5120
- 12,
5121
- 36
5122
5446
  ],
5123
5447
  rotation: 180
5124
5448
  }
5449
+ },
5450
+ cape: {
5451
+ east: { uv: [0, 1, 1, 17] },
5452
+ west: { uv: [11, 1, 12, 17] },
5453
+ north: { uv: [1, 1, 11, 17] },
5454
+ south: { uv: [12, 1, 22, 17] },
5455
+ up: { uv: [11, 1, 1, 0] },
5456
+ down: { uv: [21, 0, 11, 1] }
5125
5457
  }
5126
5458
  };
5127
5459
  var classicSkinTextureCoordinates = {
5128
5460
  ...baseSkinCoordinates
5129
5461
  };
5130
- var slimSkinTextureCoordinates = { ...baseSkinCoordinates };
5462
+ var slimSkinTextureCoordinates = {
5463
+ ...baseSkinCoordinates,
5464
+ leftArm: {
5465
+ ...baseSkinCoordinates.leftArm,
5466
+ west: { uv: [39, 52, 43, 64] },
5467
+ north: { uv: [36, 52, 39, 64] },
5468
+ south: { uv: [43, 52, 46, 64] },
5469
+ up: { uv: [39, 52, 36, 48] },
5470
+ down: { uv: [42, 48, 39, 52] }
5471
+ },
5472
+ rightArm: {
5473
+ ...baseSkinCoordinates.rightArm,
5474
+ west: { uv: [47, 20, 51, 32] },
5475
+ north: { uv: [44, 20, 47, 32] },
5476
+ south: { uv: [51, 20, 54, 32] },
5477
+ up: { uv: [47, 20, 44, 16] },
5478
+ down: { uv: [50, 16, 47, 20] }
5479
+ },
5480
+ leftSleeve: {
5481
+ ...baseSkinCoordinates.leftSleeve,
5482
+ west: { uv: [55, 52, 59, 64] },
5483
+ north: { uv: [52, 52, 55, 64] },
5484
+ south: { uv: [59, 52, 62, 64] },
5485
+ up: { uv: [55, 52, 52, 48] },
5486
+ down: { uv: [58, 48, 55, 52], rotation: 180 }
5487
+ },
5488
+ rightSleeve: {
5489
+ ...baseSkinCoordinates.rightSleeve,
5490
+ west: { uv: [47, 36, 51, 48] },
5491
+ north: { uv: [44, 36, 47, 48] },
5492
+ south: { uv: [51, 36, 54, 48] },
5493
+ up: { uv: [47, 36, 44, 32] },
5494
+ down: { uv: [50, 32, 47, 36], rotation: 180 }
5495
+ }
5496
+ };
5131
5497
 
5132
5498
  // src/skin/SkinGeometries.ts
5133
5499
  var box8inner = [8, 8, 8];
5134
- var box8outer = [8.504, 8.504, 8.504];
5500
+ var box8outer = [9, 9, 9];
5135
5501
  var box8_12_4inner = [8, 12, 4];
5136
- var box8_12_4outer = [8.504, 12.504, 4.504];
5502
+ var box8_12_4outer = [8.5, 12.5, 4.5];
5137
5503
  var box4_12_4inner = [4, 12, 4];
5138
- var box4_12_4outer = [4.504, 12.504, 4.504];
5504
+ var box4_12_4outer = [4.5, 12.5, 4.5];
5139
5505
  var box3_12_4inner = [3, 12, 4];
5140
- var box3_12_4outer = [3.504, 12.504, 4.504];
5506
+ var box3_12_4outer = [3.5, 12.5, 4.5];
5141
5507
  var box10_16_1 = [10, 16, 1];
5142
5508
  var baseSkinGeometries = {
5143
5509
  head: box8inner,
@@ -5167,114 +5533,313 @@ var slimSkinGeometries = {
5167
5533
  };
5168
5534
 
5169
5535
  // src/skin/scene/SkinObject.ts
5170
- var import_ts_deepmerge8 = __toESM(require_dist());
5536
+ var import_ts_deepmerge7 = __toESM(require_dist());
5537
+
5538
+ // src/skin/SkinTextures.ts
5539
+ import { DataTexture, DoubleSide as DoubleSide2, FrontSide as FrontSide2, MeshBasicMaterial as MeshBasicMaterial3, SRGBColorSpace as SRGBColorSpace3 } from "three";
5540
+
5541
+ // src/skin/SkinImage.ts
5542
+ var SkinImage = class {
5543
+ static async getData(src) {
5544
+ const key = serializeImageKey({ src });
5545
+ return await Caching.imageDataCache.get(`skin:${key}`, async () => {
5546
+ const info = await ImageLoader.getInfo(src);
5547
+ if (info.type !== "png") return ImageLoader.getData(src);
5548
+ try {
5549
+ return await this.decodePng(info.data);
5550
+ } catch (error) {
5551
+ Caching.rawImageCache.invalidate(key);
5552
+ throw error;
5553
+ }
5554
+ });
5555
+ }
5556
+ static async decodePng(bytes) {
5557
+ const { decode, convertIndexedToRgb } = await import("fast-png");
5558
+ const png = decode(bytes);
5559
+ let { data, channels, depth } = png;
5560
+ if (channels === 1 && (png.palette || depth < 8)) {
5561
+ const max = (1 << depth) - 1;
5562
+ const palette = png.palette ?? Array.from({ length: max + 1 }, (_, value) => {
5563
+ const gray = value * 255 / max;
5564
+ return [gray, gray, gray, png.transparency?.[0] === value ? 0 : 255];
5565
+ });
5566
+ data = convertIndexedToRgb({ ...png, palette });
5567
+ channels = palette[0].length;
5568
+ depth = 8;
5569
+ }
5570
+ const pixels = new Uint8ClampedArray(png.width * png.height * 4);
5571
+ for (let pixel = 0; pixel < png.width * png.height; pixel++) {
5572
+ const source = pixel * channels;
5573
+ const target = pixel * 4;
5574
+ const sample = (channel) => depth === 16 ? data[source + channel] >>> 8 : data[source + channel];
5575
+ pixels[target] = sample(0);
5576
+ pixels[target + 1] = sample(channels < 3 ? 0 : 1);
5577
+ pixels[target + 2] = sample(channels < 3 ? 0 : 2);
5578
+ pixels[target + 3] = channels % 2 === 0 ? sample(channels - 1) : png.transparency?.every((value, channel) => data[source + channel] === value) ? 0 : 255;
5579
+ }
5580
+ return { width: png.width, height: png.height, data: pixels, colorSpace: "srgb" };
5581
+ }
5582
+ };
5583
+
5584
+ // src/skin/SkinTextures.ts
5585
+ var SkinTextures = class {
5586
+ static async get(src, legacy) {
5587
+ const image = await SkinImage.getData(src);
5588
+ const scale = image.width / 64;
5589
+ if (!Number.isInteger(scale) || scale < 1 || image.height !== image.width && image.height !== image.width / 2) {
5590
+ throw new MineRenderError(`Invalid skin dimensions ${image.width}x${image.height}; expected 64x64 or 64x32, or an integer-scaled equivalent`);
5591
+ }
5592
+ const isLegacy = legacy ?? image.height === image.width / 2;
5593
+ if (!isLegacy && image.height !== image.width) {
5594
+ throw new MineRenderError(`Invalid skin dimensions ${image.width}x${image.height}; non-legacy skins must be square`);
5595
+ }
5596
+ const slim = !isLegacy && this.isSlim(image, scale);
5597
+ const key = `skin:${serializeImageKey({ src })}:${isLegacy}`;
5598
+ const material = Caching.materialCache.get(key, () => {
5599
+ const pixels = isLegacy ? this.normalizeLegacy(image, scale) : new Uint8Array(image.data);
5600
+ this.makeBaseOpaque(pixels, image.width, scale);
5601
+ return this.createMaterial(pixels, image.width, image.width);
5602
+ });
5603
+ return { material, slim, legacy: isLegacy };
5604
+ }
5605
+ static async getCape(src, layout = "minecraft") {
5606
+ const image = await SkinImage.getData(src);
5607
+ const scale = image.width / 64;
5608
+ if (layout === "minecraft" && (!Number.isInteger(scale) || scale < 1 || image.height !== image.width / 2)) {
5609
+ throw new MineRenderError(`Invalid cape dimensions ${image.width}x${image.height}; expected 64x32 or an integer-scaled equivalent`);
5610
+ }
5611
+ return Caching.materialCache.get(`cape:${serializeImageKey({ src })}`, () => {
5612
+ return this.createMaterial(new Uint8Array(image.data), image.width, image.height, false);
5613
+ });
5614
+ }
5615
+ static createMaterial(pixels, width, height, transparent = true) {
5616
+ const texture = Textures.initTextureProps(new DataTexture(pixels, width, height));
5617
+ texture.colorSpace = SRGBColorSpace3;
5618
+ texture.flipY = true;
5619
+ texture.needsUpdate = true;
5620
+ return new MeshBasicMaterial3({ map: texture, transparent, side: transparent ? DoubleSide2 : FrontSide2, alphaTest: transparent ? 0.1 : 0 });
5621
+ }
5622
+ static isSlim(image, scale) {
5623
+ for (const [x, y] of [[46, 52], [54, 20]]) {
5624
+ for (let row = y * scale; row < (y + 12) * scale; row++) {
5625
+ for (let column = x * scale; column < (x + 1) * scale; column++) {
5626
+ if (image.data[(row * image.width + column) * 4 + 3] !== 0) return false;
5627
+ }
5628
+ }
5629
+ }
5630
+ return true;
5631
+ }
5632
+ static normalizeLegacy(image, scale) {
5633
+ const pixels = new Uint8Array(image.width * image.width * 4);
5634
+ pixels.set(image.data.subarray(0, image.width * image.width / 2 * 4));
5635
+ for (const [sx, sy, dx, dy, width, height] of [
5636
+ [4, 16, 20, 48, 4, 4],
5637
+ [8, 16, 24, 48, 4, 4],
5638
+ [0, 20, 24, 52, 4, 12],
5639
+ [4, 20, 20, 52, 4, 12],
5640
+ [8, 20, 16, 52, 4, 12],
5641
+ [12, 20, 28, 52, 4, 12],
5642
+ [44, 16, 36, 48, 4, 4],
5643
+ [48, 16, 40, 48, 4, 4],
5644
+ [40, 20, 40, 52, 4, 12],
5645
+ [44, 20, 36, 52, 4, 12],
5646
+ [48, 20, 32, 52, 4, 12],
5647
+ [52, 20, 44, 52, 4, 12]
5648
+ ]) {
5649
+ for (let y = 0; y < height * scale; y++) {
5650
+ for (let x = 0; x < width * scale; x++) {
5651
+ const source = ((sy * scale + y) * image.width + sx * scale + x) * 4;
5652
+ const target = ((dy * scale + y) * image.width + (dx + width) * scale - 1 - x) * 4;
5653
+ pixels.set(image.data.subarray(source, source + 4), target);
5654
+ }
5655
+ }
5656
+ }
5657
+ let clearHat = true;
5658
+ for (let y = 0; y < 32 * scale; y++) {
5659
+ for (let x = 32 * scale; x < image.width; x++) {
5660
+ if (pixels[(y * image.width + x) * 4 + 3] < 128) clearHat = false;
5661
+ }
5662
+ }
5663
+ if (clearHat) {
5664
+ for (let y = 0; y < 32 * scale; y++) {
5665
+ for (let x = 32 * scale; x < image.width; x++) {
5666
+ pixels[(y * image.width + x) * 4 + 3] = 0;
5667
+ }
5668
+ }
5669
+ }
5670
+ return pixels;
5671
+ }
5672
+ static makeBaseOpaque(pixels, width, scale) {
5673
+ for (const [x0, y0, x1, y1] of [[0, 0, 32, 16], [0, 16, 64, 32], [16, 48, 48, 64]]) {
5674
+ for (let y = y0 * scale; y < y1 * scale; y++) {
5675
+ for (let x = x0 * scale; x < x1 * scale; x++) pixels[(y * width + x) * 4 + 3] = 255;
5676
+ }
5677
+ }
5678
+ }
5679
+ };
5680
+
5681
+ // src/skin/CapeLayout.ts
5682
+ var capeTextureSizes = {
5683
+ minecraft: [64, 32],
5684
+ optifine: [46, 22],
5685
+ labymod: [22, 17]
5686
+ };
5687
+
5688
+ // src/skin/scene/SkinObject.ts
5171
5689
  var SkinObject = class extends SceneObject {
5172
5690
  constructor(options) {
5173
5691
  super();
5174
5692
  this.slim = false;
5175
- this.legacy = false;
5176
- this.skinTextureWidth = 64;
5177
- this.skinTextureHeight = 64;
5178
- this.options = (0, import_ts_deepmerge8.default)({}, SceneObject.DEFAULT_OPTIONS, options ?? {});
5693
+ this.detectedSlim = false;
5694
+ this.skinLoad = 0;
5695
+ this.capeLoad = 0;
5696
+ this.options = (0, import_ts_deepmerge7.default)({}, SceneObject.DEFAULT_OPTIONS, options ?? {});
5697
+ this.slim = this.options.slim ?? false;
5179
5698
  }
5180
5699
  async init() {
5700
+ const cape = this.getGroupByName("cape" /* CAPE */);
5181
5701
  this.createMeshes();
5702
+ if (cape) this.getGroupByName("body" /* BODY */).attach(cape);
5182
5703
  }
5183
5704
  removeMeshes() {
5184
5705
  }
5185
5706
  createMeshes() {
5186
- console.log("#createMeshes");
5187
- const mat = Materials.MISSING_TEXTURE;
5707
+ const mat = this.skinMaterial ?? Materials.MISSING_TEXTURE;
5188
5708
  {
5189
5709
  const headGroup = this.createAndAddGroup("head", 0, 28, 0, "y" /* Y */, -4);
5190
5710
  const headGeo = this.getBoxGeometry("head" /* HEAD */);
5191
5711
  const head = this.createAndAddMesh("head", headGroup, headGeo, mat, "y" /* Y */, 4);
5192
- if (!this.legacy) {
5193
- const hatGeo = this.getBoxGeometry("hat" /* HAT */);
5194
- const hat = this.createAndAddMesh("hat", headGroup, hatGeo, mat, "y" /* Y */, 4);
5195
- }
5712
+ const hatGeo = this.getBoxGeometry("hat" /* HAT */);
5713
+ const hat = this.createAndAddMesh("hat", headGroup, hatGeo, mat, "y" /* Y */, 4);
5196
5714
  }
5197
5715
  {
5198
- const bodyGroup = this.createAndAddGroup("body", 0, 18, 0);
5716
+ const bodyGroup = this.createAndAddGroup("body", 0, 24, 0);
5199
5717
  const bodyGeo = this.getBoxGeometry("body" /* BODY */);
5200
- const body = this.createAndAddMesh("body", bodyGroup, bodyGeo, mat);
5718
+ const body = this.createAndAddMesh("body", bodyGroup, bodyGeo, mat, "y" /* Y */, -6);
5201
5719
  const jacketGeo = this.getBoxGeometry("jacket" /* JACKET */);
5202
- const jacket = this.createAndAddMesh("jacket", bodyGroup, jacketGeo, mat);
5720
+ const jacket = this.createAndAddMesh("jacket", bodyGroup, jacketGeo, mat, "y" /* Y */, -6);
5203
5721
  }
5204
5722
  {
5205
5723
  {
5206
- const leftArmGroup = this.createAndAddGroup("leftArm", this.slim ? -5.5 : -6, 18, 0, "y" /* Y */, 4);
5724
+ const leftArmGroup = this.createAndAddGroup("leftArm", -5, 22, 0);
5207
5725
  const leftArmGeo = this.getBoxGeometry("leftArm" /* LEFT_ARM */);
5208
5726
  const leftArm = this.createAndAddMesh("leftArm", leftArmGroup, leftArmGeo, mat, "y" /* Y */, -4);
5727
+ leftArm.position.x = this.slim ? -0.5 : -1;
5209
5728
  const leftSleeveGeo = this.getBoxGeometry("leftSleeve" /* LEFT_SLEEVE */);
5210
5729
  const leftSleeve = this.createAndAddMesh("leftSleeve", leftArmGroup, leftSleeveGeo, mat, "y" /* Y */, -4);
5730
+ leftSleeve.position.x = this.slim ? -0.5 : -1;
5211
5731
  }
5212
5732
  {
5213
- const rightArmGroup = this.createAndAddGroup("rightArm", this.slim ? 5.5 : 6, 18, 0, "y" /* Y */, 4);
5733
+ const rightArmGroup = this.createAndAddGroup("rightArm", 5, 22, 0);
5214
5734
  const rightArmGeo = this.getBoxGeometry("rightArm" /* RIGHT_ARM */);
5215
5735
  const rightArm = this.createAndAddMesh("rightArm", rightArmGroup, rightArmGeo, mat, "y" /* Y */, -4);
5736
+ rightArm.position.x = this.slim ? 0.5 : 1;
5216
5737
  const rightSleeveGeo = this.getBoxGeometry("rightSleeve" /* RIGHT_SLEEVE */);
5217
5738
  const rightSleeve = this.createAndAddMesh("rightSleeve", rightArmGroup, rightSleeveGeo, mat, "y" /* Y */, -4);
5739
+ rightSleeve.position.x = this.slim ? 0.5 : 1;
5218
5740
  }
5219
5741
  }
5220
5742
  {
5221
5743
  {
5222
- const leftLegGroup = this.createAndAddGroup("leftLeg", -2, 6, 0, "y" /* Y */, 4);
5744
+ const leftLegGroup = this.createAndAddGroup("leftLeg", -1.9, 12, 0);
5223
5745
  const leftLegGeo = this.getBoxGeometry("leftLeg" /* LEFT_LEG */);
5224
- const leftLeg = this.createAndAddMesh("leftLeg", leftLegGroup, leftLegGeo, mat, "y" /* Y */, -4);
5746
+ const leftLeg = this.createAndAddMesh("leftLeg", leftLegGroup, leftLegGeo, mat, "y" /* Y */, -6);
5225
5747
  const leftTrousersGeo = this.getBoxGeometry("leftTrousers" /* LEFT_TROUSERS */);
5226
- const leftTrousers = this.createAndAddMesh("leftTrousers", leftLegGroup, leftTrousersGeo, mat, "y" /* Y */, -4);
5748
+ const leftTrousers = this.createAndAddMesh("leftTrousers", leftLegGroup, leftTrousersGeo, mat, "y" /* Y */, -6);
5227
5749
  }
5228
5750
  {
5229
- const rightLegGroup = this.createAndAddGroup("rightLeg", 2, 6, 0, "y" /* Y */, 4);
5751
+ const rightLegGroup = this.createAndAddGroup("rightLeg", 1.9, 12, 0);
5230
5752
  const rightLegGeo = this.getBoxGeometry("rightLeg" /* RIGHT_LEG */);
5231
- const rightLeg = this.createAndAddMesh("rightLeg", rightLegGroup, rightLegGeo, mat, "y" /* Y */, -4);
5753
+ const rightLeg = this.createAndAddMesh("rightLeg", rightLegGroup, rightLegGeo, mat, "y" /* Y */, -6);
5232
5754
  const rightTrousersGeo = this.getBoxGeometry("rightTrousers" /* RIGHT_TROUSERS */);
5233
- const rightTrousers = this.createAndAddMesh("rightTrousers", rightLegGroup, rightTrousersGeo, mat, "y" /* Y */, -4);
5755
+ const rightTrousers = this.createAndAddMesh("rightTrousers", rightLegGroup, rightTrousersGeo, mat, "y" /* Y */, -6);
5234
5756
  }
5235
5757
  }
5236
- console.log("#createMeshes done");
5237
5758
  }
5238
- setSkinTexture(src) {
5759
+ async setSkinTexture(src) {
5239
5760
  if (typeof src === "undefined") return;
5240
5761
  this.skinTextureSrc = src;
5241
- const mat = Materials.getImage({
5242
- texture: { src },
5243
- transparent: true
5244
- });
5245
- for (let part of SKIN_PARTS) {
5246
- let mesh = this.getMeshByName(part);
5762
+ const load = ++this.skinLoad;
5763
+ const texture = await SkinTextures.get(src, this.options.legacy);
5764
+ if (load !== this.skinLoad) return;
5765
+ this.skinMaterial = texture.material;
5766
+ this.detectedSlim = texture.slim;
5767
+ this.updateSlim(this.options.slim ?? this.detectedSlim);
5768
+ for (const part of SKIN_PARTS) {
5769
+ if (part === "cape" /* CAPE */) continue;
5770
+ const mesh = this.getMeshByName(part);
5247
5771
  if (mesh) {
5248
- mesh.material = mat;
5772
+ mesh.material = texture.material;
5249
5773
  }
5250
5774
  }
5775
+ this.notifyDirty();
5776
+ }
5777
+ /** Load a cape texture in the selected layout, or pass undefined to remove the cape. */
5778
+ async setCapeTexture(src, layout = "minecraft") {
5779
+ const load = ++this.capeLoad;
5780
+ if (src === void 0) {
5781
+ const group = this.getGroupByName("cape" /* CAPE */);
5782
+ group?.removeFromParent();
5783
+ this.notifyDirty();
5784
+ return;
5785
+ }
5786
+ const material = await SkinTextures.getCape(src, layout);
5787
+ if (load !== this.capeLoad) return;
5788
+ const size = capeTextureSizes[layout];
5789
+ const geometry = this._getBoxGeometryFromDimensions(
5790
+ classicSkinGeometries.cape,
5791
+ classicSkinTextureCoordinates.cape,
5792
+ size,
5793
+ size
5794
+ );
5795
+ const mesh = this.getMeshByName("cape" /* CAPE */);
5796
+ if (mesh) {
5797
+ mesh.material = material;
5798
+ mesh.geometry = geometry;
5799
+ } else {
5800
+ const body = this.getGroupByName("body" /* BODY */);
5801
+ const group = this.createGroup("cape" /* CAPE */, 0, body ? 0 : 24, 2);
5802
+ (body ?? this).add(group);
5803
+ group.rotation.set(toRadians(-6), Math.PI, 0);
5804
+ const cape = this.createAndAddMesh("cape" /* CAPE */, group, geometry, material);
5805
+ cape.position.set(0, -8, -0.5);
5806
+ }
5807
+ this.notifyDirty();
5251
5808
  }
5252
- //TODO: cape
5809
+ /** Select the arm model, or pass undefined to use texture detection. */
5253
5810
  setSlim(slim) {
5254
- console.log("#setSlim");
5255
- const changed = slim !== this.slim;
5811
+ this.options.slim = slim;
5812
+ this.updateSlim(slim ?? this.detectedSlim);
5813
+ }
5814
+ updateSlim(slim) {
5815
+ if (slim === this.slim) return;
5256
5816
  this.slim = slim;
5257
- if (changed) {
5258
- this.createMeshes();
5259
- }
5260
- if (changed) {
5261
- if (this.skinTextureSrc) {
5262
- this.setSkinTexture(this.skinTextureSrc);
5817
+ const offset = slim ? 0.5 : -0.5;
5818
+ for (const part of ["leftArm" /* LEFT_ARM */, "leftSleeve" /* LEFT_SLEEVE */, "rightArm" /* RIGHT_ARM */, "rightSleeve" /* RIGHT_SLEEVE */]) {
5819
+ const mesh = this.getMeshByName(part);
5820
+ if (mesh) {
5821
+ mesh.geometry = this.getBoxGeometry(part);
5822
+ mesh.position.x += part === "leftArm" /* LEFT_ARM */ || part === "leftSleeve" /* LEFT_SLEEVE */ ? offset : -offset;
5263
5823
  }
5264
5824
  }
5265
- console.log("#setSlim done");
5825
+ this.notifyDirty();
5266
5826
  }
5267
- setLegacy(legacy) {
5268
- if (legacy !== this.legacy) {
5269
- }
5270
- this.legacy = legacy;
5827
+ /** Select the texture layout, or pass undefined to detect it from image dimensions. */
5828
+ async setLegacy(legacy) {
5829
+ if (legacy === this.options.legacy) return;
5830
+ this.options.legacy = legacy;
5831
+ if (this.skinTextureSrc) await this.setSkinTexture(this.skinTextureSrc);
5832
+ }
5833
+ dispose() {
5834
+ this.skinLoad++;
5835
+ this.capeLoad++;
5836
+ super.dispose();
5271
5837
  }
5272
5838
  //TODO: layer toggles
5273
5839
  getBoxGeometry(part) {
5274
- console.log("slim", this.slim);
5275
5840
  const coordinates = this.slim ? slimSkinTextureCoordinates : classicSkinTextureCoordinates;
5276
5841
  const geometries = this.slim ? slimSkinGeometries : classicSkinGeometries;
5277
- return this._getBoxGeometryFromDimensions(geometries[part], coordinates[part], [64, 64], [this.skinTextureWidth, this.skinTextureHeight]);
5842
+ return this._getBoxGeometryFromDimensions(geometries[part], coordinates[part], [64, 64], [64, 64]);
5278
5843
  }
5279
5844
  };
5280
5845
 
@@ -5292,7 +5857,7 @@ var _MineRenderScene = class _MineRenderScene extends Scene2 {
5292
5857
  this.dirty = true;
5293
5858
  }
5294
5859
  };
5295
- this.options = (0, import_ts_deepmerge9.default)({}, _MineRenderScene.DEFAULT_OPTIONS, options ?? {});
5860
+ this.options = (0, import_ts_deepmerge8.default)({}, _MineRenderScene.DEFAULT_OPTIONS, options ?? {});
5296
5861
  }
5297
5862
  add(...objects) {
5298
5863
  for (const object of objects) {
@@ -5350,7 +5915,14 @@ var _MineRenderScene = class _MineRenderScene extends Scene2 {
5350
5915
  const obj = await objectSupplier();
5351
5916
  if (obj?.options?.instanceMeshes && asset.key && asset.key?.assetType === "models") {
5352
5917
  console.log("instanceMeshes + key");
5353
- const key = asset.key.serialize();
5918
+ let key = asset.key.serialize();
5919
+ if (isModelObject(obj)) {
5920
+ const { displayPosition, uvLockRotation, tints } = obj.options;
5921
+ if (displayPosition) key += `|display:${displayPosition}`;
5922
+ if (uvLockRotation) key += `|uvlock:${uvLockRotation.join(",")}`;
5923
+ const palette = Object.entries(tints ?? {}).sort(([a], [b]) => Number(a) - Number(b));
5924
+ if (palette.length) key += `|tints:${JSON.stringify(palette)}`;
5925
+ }
5354
5926
  return this.instanceManager.getOrCreate(key, async () => {
5355
5927
  obj.scene = this;
5356
5928
  await obj.init();
@@ -5377,10 +5949,10 @@ var _MineRenderScene = class _MineRenderScene extends Scene2 {
5377
5949
  this.dirty = true;
5378
5950
  const obj = new SkinObject(options);
5379
5951
  obj.scene = this;
5380
- await obj.init();
5381
5952
  if (skin) {
5382
- obj.setSkinTexture(skin);
5953
+ await obj.setSkinTexture(skin);
5383
5954
  }
5955
+ await obj.init();
5384
5956
  parent.add(obj);
5385
5957
  return obj;
5386
5958
  }
@@ -5388,15 +5960,15 @@ var _MineRenderScene = class _MineRenderScene extends Scene2 {
5388
5960
  return this.addSceneObject(entity, () => new EntityObject(entity, options), options, parent);
5389
5961
  }
5390
5962
  };
5391
- _MineRenderScene.DEFAULT_OPTIONS = (0, import_ts_deepmerge9.default)({}, {});
5963
+ _MineRenderScene.DEFAULT_OPTIONS = (0, import_ts_deepmerge8.default)({}, {});
5392
5964
  var MineRenderScene = _MineRenderScene;
5393
5965
  function isMineRenderScene(obj) {
5394
5966
  return obj.isMineRenderScene;
5395
5967
  }
5396
5968
 
5397
5969
  // src/renderer/Renderer.ts
5398
- import { AxesHelper as AxesHelper3, GridHelper, LinearEncoding, OrthographicCamera, PCFSoftShadowMap, PerspectiveCamera, Vector3 as Vector38, WebGLRenderer } from "three";
5399
- var import_ts_deepmerge10 = __toESM(require_dist());
5970
+ import { AxesHelper as AxesHelper2, GridHelper, OrthographicCamera, PCFSoftShadowMap, PerspectiveCamera, SRGBColorSpace as SRGBColorSpace4, Vector3 as Vector39, WebGLRenderer } from "three";
5971
+ var import_ts_deepmerge9 = __toESM(require_dist());
5400
5972
  import Stats from "stats.js";
5401
5973
  import { EffectComposer, RenderPass } from "postprocessing";
5402
5974
 
@@ -5404,11 +5976,11 @@ import { EffectComposer, RenderPass } from "postprocessing";
5404
5976
  import {
5405
5977
  EventDispatcher,
5406
5978
  MOUSE,
5407
- Quaternion as Quaternion3,
5979
+ Quaternion as Quaternion4,
5408
5980
  Spherical,
5409
5981
  TOUCH,
5410
5982
  Vector2 as Vector23,
5411
- Vector3 as Vector37
5983
+ Vector3 as Vector38
5412
5984
  } from "three";
5413
5985
  var _changeEvent = { type: "change" };
5414
5986
  var _startEvent = { type: "start" };
@@ -5422,7 +5994,7 @@ var OrbitControls = class extends EventDispatcher {
5422
5994
  this.domElement = domElement;
5423
5995
  this.domElement.style.touchAction = "none";
5424
5996
  this.enabled = true;
5425
- this.target = new Vector37();
5997
+ this.target = new Vector38();
5426
5998
  this.minDistance = 0;
5427
5999
  this.maxDistance = Infinity;
5428
6000
  this.minZoom = 0;
@@ -5474,11 +6046,11 @@ var OrbitControls = class extends EventDispatcher {
5474
6046
  state = STATE.NONE;
5475
6047
  };
5476
6048
  this.update = function() {
5477
- const offset = new Vector37();
5478
- const quat = new Quaternion3().setFromUnitVectors(object.up, new Vector37(0, 1, 0));
6049
+ const offset = new Vector38();
6050
+ const quat = new Quaternion4().setFromUnitVectors(object.up, new Vector38(0, 1, 0));
5479
6051
  const quatInverse = quat.clone().invert();
5480
- const lastPosition = new Vector37();
5481
- const lastQuaternion = new Quaternion3();
6052
+ const lastPosition = new Vector38();
6053
+ const lastQuaternion = new Quaternion4();
5482
6054
  const twoPI = 2 * Math.PI;
5483
6055
  return function update() {
5484
6056
  const position = scope.object.position;
@@ -5567,7 +6139,7 @@ var OrbitControls = class extends EventDispatcher {
5567
6139
  const spherical = new Spherical();
5568
6140
  const sphericalDelta = new Spherical();
5569
6141
  let scale = 1;
5570
- const panOffset = new Vector37();
6142
+ const panOffset = new Vector38();
5571
6143
  let zoomChanged = false;
5572
6144
  const rotateStart = new Vector23();
5573
6145
  const rotateEnd = new Vector23();
@@ -5593,7 +6165,7 @@ var OrbitControls = class extends EventDispatcher {
5593
6165
  sphericalDelta.phi -= angle;
5594
6166
  }
5595
6167
  const panLeft = function() {
5596
- const v = new Vector37();
6168
+ const v = new Vector38();
5597
6169
  return function panLeft2(distance, objectMatrix) {
5598
6170
  v.setFromMatrixColumn(objectMatrix, 0);
5599
6171
  v.multiplyScalar(-distance);
@@ -5601,7 +6173,7 @@ var OrbitControls = class extends EventDispatcher {
5601
6173
  };
5602
6174
  }();
5603
6175
  const panUp = function() {
5604
- const v = new Vector37();
6176
+ const v = new Vector38();
5605
6177
  return function panUp2(distance, objectMatrix) {
5606
6178
  if (scope.screenSpacePanning === true) {
5607
6179
  v.setFromMatrixColumn(objectMatrix, 1);
@@ -5614,7 +6186,7 @@ var OrbitControls = class extends EventDispatcher {
5614
6186
  };
5615
6187
  }();
5616
6188
  const pan = function() {
5617
- const offset = new Vector37();
6189
+ const offset = new Vector38();
5618
6190
  return function pan2(deltaX, deltaY) {
5619
6191
  const element = scope.domElement;
5620
6192
  if (scope.object.isPerspectiveCamera) {
@@ -6054,8 +6626,6 @@ var MapControls = class extends OrbitControls {
6054
6626
  var _Renderer = class _Renderer {
6055
6627
  constructor(options) {
6056
6628
  this._dirty = true;
6057
- this._animationTimer = void 0;
6058
- this._animationFrame = void 0;
6059
6629
  this._resizeListener = void 0;
6060
6630
  this._disposed = false;
6061
6631
  this._debugHelpers = [];
@@ -6063,9 +6633,9 @@ var _Renderer = class _Renderer {
6063
6633
  this._changeListener = () => {
6064
6634
  this._dirty = true;
6065
6635
  };
6066
- this.options = (0, import_ts_deepmerge10.default)({}, _Renderer.DEFAULT_OPTIONS, options ?? {});
6636
+ this.options = (0, import_ts_deepmerge9.default)({}, _Renderer.DEFAULT_OPTIONS, options ?? {});
6067
6637
  this._animationLoop = this.animate.bind(this);
6068
- this._fpsTimer = this.options.render.fpsLimit > 0 ? 1e3 / this.options.render.fpsLimit : void 0;
6638
+ this._frameInterval = this.options.render.fpsLimit > 0 ? 1e3 / this.options.render.fpsLimit : void 0;
6069
6639
  this._scene = this.createScene();
6070
6640
  this._camera = this.createCamera();
6071
6641
  this._renderer = this.createRenderer();
@@ -6112,13 +6682,13 @@ var _Renderer = class _Renderer {
6112
6682
  renderer.setClearColor(0, 0);
6113
6683
  renderer.shadowMap.enabled = true;
6114
6684
  renderer.shadowMap.type = PCFSoftShadowMap;
6115
- renderer.outputEncoding = LinearEncoding;
6685
+ renderer.outputColorSpace = SRGBColorSpace4;
6116
6686
  renderer.setSize(this.viewWidth, this.viewHeight);
6117
6687
  return renderer;
6118
6688
  }
6119
6689
  createComposer() {
6690
+ if (!this.options.composer.enabled) return void 0;
6120
6691
  const composer = new EffectComposer(this.renderer);
6121
- if (!this.options.composer.enabled) return composer;
6122
6692
  composer.setSize(this.viewWidth, this.viewHeight);
6123
6693
  composer.addPass(new RenderPass(this.scene, this.camera));
6124
6694
  return composer;
@@ -6159,7 +6729,7 @@ var _Renderer = class _Renderer {
6159
6729
  this.scene.add(gridHelper3);
6160
6730
  }
6161
6731
  if (this.options.debug.axes) {
6162
- const axesHelper = new AxesHelper3(64);
6732
+ const axesHelper = new AxesHelper2(64);
6163
6733
  this._debugHelpers.push(axesHelper);
6164
6734
  this.scene.add(axesHelper);
6165
6735
  }
@@ -6227,7 +6797,7 @@ var _Renderer = class _Renderer {
6227
6797
  this.camera.updateProjectionMatrix();
6228
6798
  }
6229
6799
  this.renderer.setSize(width, height);
6230
- this.composer.setSize(width, height);
6800
+ this.composer?.setSize(width, height);
6231
6801
  this._dirty = true;
6232
6802
  }
6233
6803
  //<editor-fold desc="RENDER">
@@ -6246,12 +6816,7 @@ var _Renderer = class _Renderer {
6246
6816
  stop() {
6247
6817
  if (this._disposed) return;
6248
6818
  this.renderer.setAnimationLoop(null);
6249
- if (this._animationTimer)
6250
- clearTimeout(this._animationTimer);
6251
- this._animationTimer = void 0;
6252
- if (this._animationFrame)
6253
- cancelAnimationFrame(this._animationFrame);
6254
- this._animationFrame = void 0;
6819
+ this._nextFrameTime = void 0;
6255
6820
  }
6256
6821
  /**
6257
6822
  * Stops rendering and releases owned resources. Repeated calls have no effect, and a disposed
@@ -6291,25 +6856,30 @@ var _Renderer = class _Renderer {
6291
6856
  }
6292
6857
  }
6293
6858
  this._debugHelpers.length = 0;
6294
- this.composer.dispose();
6859
+ this.composer?.dispose();
6295
6860
  this.renderer.dispose();
6296
6861
  this.renderer.forceContextLoss();
6297
6862
  }
6298
- animate(t) {
6863
+ animate(t = performance.now()) {
6299
6864
  if (this._disposed) return;
6300
- if (this._stats) {
6301
- this._stats.begin();
6302
- }
6303
6865
  if (this._controls?.enabled) {
6304
6866
  this._controls.update();
6305
6867
  }
6306
6868
  if (this._disposed) return;
6307
- if (this.dirty || this.options.render.renderAlways) {
6308
- if (this.options.composer.enabled) {
6309
- this.composer.render();
6310
- } else {
6311
- this.renderer.render(this.scene, this.camera);
6312
- }
6869
+ if (!this.dirty && !this.options.render.renderAlways) return;
6870
+ const interval = this._frameInterval;
6871
+ if (interval) {
6872
+ const next = this._nextFrameTime;
6873
+ if (next !== void 0 && t + 0.1 < next) return;
6874
+ this._nextFrameTime = next !== void 0 && t - next < interval ? next + interval : t + interval;
6875
+ }
6876
+ if (this._stats) {
6877
+ this._stats.begin();
6878
+ }
6879
+ if (this.options.composer.enabled && this.composer) {
6880
+ this.composer.render();
6881
+ } else {
6882
+ this.renderer.render(this.scene, this.camera);
6313
6883
  }
6314
6884
  this.dirty = false;
6315
6885
  if (this._stats) {
@@ -6348,7 +6918,7 @@ var _Renderer = class _Renderer {
6348
6918
  }
6349
6919
  ///
6350
6920
  };
6351
- _Renderer.DEFAULT_OPTIONS = (0, import_ts_deepmerge10.default)({}, {
6921
+ _Renderer.DEFAULT_OPTIONS = (0, import_ts_deepmerge9.default)({}, {
6352
6922
  camera: {
6353
6923
  type: "perspective",
6354
6924
  near: 1,
@@ -6363,8 +6933,8 @@ _Renderer.DEFAULT_OPTIONS = (0, import_ts_deepmerge10.default)({}, {
6363
6933
  top: void 0,
6364
6934
  bottom: void 0
6365
6935
  },
6366
- position: new Vector38(50, 50, 50),
6367
- lookingAt: new Vector38(0, 0, 0)
6936
+ position: new Vector39(50, 50, 50),
6937
+ lookingAt: new Vector39(0, 0, 0)
6368
6938
  },
6369
6939
  render: {
6370
6940
  fpsLimit: 60,
@@ -6397,7 +6967,7 @@ var Skins = class {
6397
6967
  return this.fromUsername(uuidOrUsername);
6398
6968
  }
6399
6969
  static async fromUuid(uuid) {
6400
- return this.getSkinProxySkinUrl(uuid);
6970
+ return this.getMcProxySkinUrl(uuid);
6401
6971
  }
6402
6972
  static async fromUsername(username) {
6403
6973
  return this.usernameToUuid(username).then((uuid) => {
@@ -6424,7 +6994,7 @@ var Skins = class {
6424
6994
  return this.capeFromUsername(uuidOrUsername);
6425
6995
  }
6426
6996
  static async capeFromUuid(uuid) {
6427
- return this.getSkinProxyCapeUrl(uuid);
6997
+ return this.getMcProxyCapeUrl(uuid);
6428
6998
  }
6429
6999
  static async capeFromUsername(username) {
6430
7000
  return this.usernameToUuid(username).then((uuid) => {
@@ -6434,19 +7004,31 @@ var Skins = class {
6434
7004
  return void 0;
6435
7005
  });
6436
7006
  }
6437
- static async capeFromCapesDev(uuid, capeType = "minecraft") {
6438
- return this.getCapesDev(uuid, capeType).then((cape) => {
6439
- if (cape) {
6440
- return cape?.stillImageUrl;
6441
- }
6442
- return void 0;
6443
- });
7007
+ /** Returns a full static cape texture for a username or UUID. */
7008
+ static async capeFromCapesDev(player, capeType = "minecraft") {
7009
+ const cape = await this.getCapesDev(player, capeType);
7010
+ return cape?.exists ? cape.imageUrls?.still?.full : void 0;
6444
7011
  }
6445
7012
  //</editor-fold>
6446
7013
  //<editor-fold desc="Helpers">
6447
7014
  static async usernameToUuid(username) {
6448
- return this.usernameToUuidSkinProxy(username);
7015
+ return this.usernameToUuidMcProxy(username);
7016
+ }
7017
+ static async usernameToUuidMcProxy(username) {
7018
+ return Requests.genericRequest({
7019
+ baseURL: "https://mcproxy.dev",
7020
+ url: "/uuid/" + username
7021
+ }).then((res) => {
7022
+ if (res.status === 200) {
7023
+ return res.data["data"]["id"];
7024
+ }
7025
+ return void 0;
7026
+ }).catch((err) => {
7027
+ console.warn(err);
7028
+ return void 0;
7029
+ });
6449
7030
  }
7031
+ /** @deprecated **/
6450
7032
  static async usernameToUuidSkinProxy(username) {
6451
7033
  return Requests.genericRequest({
6452
7034
  baseURL: "https://minecraft-skin-proxy.inventive.workers.dev",
@@ -6499,18 +7081,15 @@ var Skins = class {
6499
7081
  return void 0;
6500
7082
  });
6501
7083
  }
6502
- static async getCapesDev(uuid, type = "all") {
7084
+ static async getCapesDev(player, type = "all") {
6503
7085
  return Requests.genericRequest({
6504
7086
  baseURL: "https://api.capes.dev",
6505
- url: "/load/" + uuid + "/" + type
7087
+ url: "/load/" + encodeURIComponent(player) + "/" + encodeURIComponent(type)
6506
7088
  }).then((res) => {
6507
7089
  if (res.status === 200) {
6508
7090
  return res.data;
6509
7091
  }
6510
7092
  return void 0;
6511
- }).catch((err) => {
6512
- console.warn(err);
6513
- return void 0;
6514
7093
  });
6515
7094
  }
6516
7095
  static getCrafatarSkinUrl(uuid) {
@@ -6525,20 +7104,28 @@ var Skins = class {
6525
7104
  static getMineSkinPlayerSkinUrl(uuid) {
6526
7105
  return "https://mineskin.org/skin/player/" + uuid;
6527
7106
  }
7107
+ /** @deprecated **/
6528
7108
  static getSkinProxySkinUrl(uuid) {
6529
7109
  return "https://minecraft-skin-proxy.inventive.workers.dev/skin/" + uuid;
6530
7110
  }
7111
+ /** @deprecated **/
6531
7112
  static getSkinProxyCapeUrl(uuid) {
6532
7113
  return "https://minecraft-skin-proxy.inventive.workers.dev/cape/" + uuid;
6533
7114
  }
7115
+ static getMcProxySkinUrl(uuid) {
7116
+ return "https://mcproxy.dev/skin/" + uuid;
7117
+ }
7118
+ static getMcProxyCapeUrl(uuid) {
7119
+ return "https://mcproxy.dev/cape/" + uuid;
7120
+ }
6534
7121
  //</editor-fold>
6535
7122
  };
6536
7123
 
6537
7124
  // src/world/Chunk.ts
6538
- import { Box3 as Box32, Object3D as Object3D6, Vector3 as Vector310 } from "three";
7125
+ import { Box3 as Box32, Object3D as Object3D6, Vector3 as Vector311 } from "three";
6539
7126
 
6540
7127
  // src/world/MineRenderWorld.ts
6541
- import { Vector3 as Vector39 } from "three";
7128
+ import { Vector3 as Vector310 } from "three";
6542
7129
  var MineRenderWorld = class {
6543
7130
  constructor(scene) {
6544
7131
  this._size = 4;
@@ -6549,10 +7136,10 @@ var MineRenderWorld = class {
6549
7136
  }
6550
7137
  getBlockAt(posOrX, y, z) {
6551
7138
  if (typeof posOrX == "number") {
6552
- return this.getBlockAt(new Vector39(posOrX, y, z));
7139
+ return this.getBlockAt(new Vector310(posOrX, y, z));
6553
7140
  }
6554
7141
  if (isTripleArray(posOrX)) {
6555
- return this.getBlockAt(new Vector39(posOrX[0], posOrX[1], posOrX[2]));
7142
+ return this.getBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]));
6556
7143
  }
6557
7144
  const chunk = this.getChunkAt(posOrX);
6558
7145
  if (typeof chunk === "undefined") {
@@ -6563,10 +7150,10 @@ var MineRenderWorld = class {
6563
7150
  }
6564
7151
  async setBlockAt(posOrX, yOrBlock, z, block) {
6565
7152
  if (typeof posOrX == "number") {
6566
- return this.setBlockAt(new Vector39(posOrX, yOrBlock, z), block);
7153
+ return this.setBlockAt(new Vector310(posOrX, yOrBlock, z), block);
6567
7154
  }
6568
7155
  if (isTripleArray(posOrX)) {
6569
- return this.setBlockAt(new Vector39(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7156
+ return this.setBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
6570
7157
  }
6571
7158
  this.validatePosBounds(posOrX);
6572
7159
  const chunk = this.getOrCreateChunkAt(posOrX);
@@ -6612,14 +7199,14 @@ var MineRenderWorld = class {
6612
7199
  return this._chunks[index];
6613
7200
  }
6614
7201
  static worldToScenePosition(pos) {
6615
- return new Vector39(
7202
+ return new Vector310(
6616
7203
  pos.x * 16,
6617
7204
  pos.y * 16,
6618
7205
  pos.z * 16
6619
7206
  );
6620
7207
  }
6621
7208
  static sceneToWorldPosition(pos) {
6622
- return new Vector39(
7209
+ return new Vector310(
6623
7210
  pos.x / 16,
6624
7211
  pos.y / 16,
6625
7212
  pos.z / 16
@@ -6648,7 +7235,7 @@ var MineRenderWorld = class {
6648
7235
  };
6649
7236
 
6650
7237
  // src/world/Chunk.ts
6651
- var p12 = prefix("Chunk");
7238
+ var p11 = prefix("Chunk");
6652
7239
  var Chunk = class _Chunk {
6653
7240
  constructor(scene, x, y, z) {
6654
7241
  this._blocks = [];
@@ -6661,7 +7248,7 @@ var Chunk = class _Chunk {
6661
7248
  scene.add(anchor);
6662
7249
  anchor.position.set(this.x * 256 + 128 - 8, this.y * 256 + 128 - 8, this.z * 256 + 128 - 8);
6663
7250
  addBox3WireframeToObject(
6664
- new Box32(new Vector310(0, 0, 0), new Vector310(256, 256, 256)),
7251
+ new Box32(new Vector311(0, 0, 0), new Vector311(256, 256, 256)),
6665
7252
  anchor,
6666
7253
  16776960,
6667
7254
  4
@@ -6669,10 +7256,10 @@ var Chunk = class _Chunk {
6669
7256
  }
6670
7257
  getBlockAt(posOrX, y, z) {
6671
7258
  if (typeof posOrX == "number") {
6672
- return this.getBlockAt(new Vector310(posOrX, y, z));
7259
+ return this.getBlockAt(new Vector311(posOrX, y, z));
6673
7260
  }
6674
7261
  if (isTripleArray(posOrX)) {
6675
- return this.getBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]));
7262
+ return this.getBlockAt(new Vector311(posOrX[0], posOrX[1], posOrX[2]));
6676
7263
  }
6677
7264
  const pos = this.worldPosToChunkPos(posOrX);
6678
7265
  const index = _Chunk.chunkPosToBlockIndex(pos);
@@ -6680,10 +7267,10 @@ var Chunk = class _Chunk {
6680
7267
  }
6681
7268
  async setBlockAt(posOrX, yOrBlock, z, block) {
6682
7269
  if (typeof posOrX == "number") {
6683
- return this.setBlockAt(new Vector310(posOrX, yOrBlock, z), block);
7270
+ return this.setBlockAt(new Vector311(posOrX, yOrBlock, z), block);
6684
7271
  }
6685
7272
  if (isTripleArray(posOrX)) {
6686
- return this.setBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7273
+ return this.setBlockAt(new Vector311(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
6687
7274
  }
6688
7275
  const worldPos = posOrX;
6689
7276
  const pos = this.worldPosToChunkPos(worldPos);
@@ -6700,7 +7287,7 @@ var Chunk = class _Chunk {
6700
7287
  const index = _Chunk.chunkPosToBlockIndex(pos);
6701
7288
  const current = this._blocks[index];
6702
7289
  if (typeof current !== "undefined") {
6703
- console.debug(p12, "deleting existing block at", pos, worldPos, index);
7290
+ console.debug(p11, "deleting existing block at", pos, worldPos, index);
6704
7291
  current.object.removeFromScene();
6705
7292
  delete this._blocks[index];
6706
7293
  }
@@ -6729,7 +7316,7 @@ var Chunk = class _Chunk {
6729
7316
  }
6730
7317
  async clear() {
6731
7318
  let promises = [];
6732
- let pos = new Vector310();
7319
+ let pos = new Vector311();
6733
7320
  for (let x = 0; x < 16; x++) {
6734
7321
  for (let z = 0; z < 16; z++) {
6735
7322
  for (let y = 0; y < 16; y++) {
@@ -6748,14 +7335,14 @@ var Chunk = class _Chunk {
6748
7335
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
6749
7336
  }
6750
7337
  worldPosToChunkPos(pos) {
6751
- return new Vector310(
7338
+ return new Vector311(
6752
7339
  pos.x - this.x * 16,
6753
7340
  pos.y - this.y * 16,
6754
7341
  pos.z - this.z * 16
6755
7342
  );
6756
7343
  }
6757
7344
  chunkPosToWorldPos(pos) {
6758
- return new Vector310(
7345
+ return new Vector311(
6759
7346
  this.x * 16 + pos.x,
6760
7347
  this.y * 16 + pos.y,
6761
7348
  this.z * 16 + pos.z
@@ -6766,6 +7353,7 @@ export {
6766
7353
  AXES,
6767
7354
  ArchiveAssetSource,
6768
7355
  AssetKey,
7356
+ AssetLoadError,
6769
7357
  AssetLoader,
6770
7358
  AssetParser,
6771
7359
  AssetSource,
@@ -6775,9 +7363,9 @@ export {
6775
7363
  BUILTIN_GENERATED,
6776
7364
  BasicAssetKey,
6777
7365
  BatchedExecutor,
6778
- BlockEntityModels,
6779
7366
  BlockObject,
6780
7367
  BlockStates,
7368
+ BlockTints,
6781
7369
  BrowserArchiveProxy,
6782
7370
  BrowserCache,
6783
7371
  BrowserEnv,
@@ -6794,8 +7382,8 @@ export {
6794
7382
  DISPLAY_POSITIONS,
6795
7383
  DefaultBlockStates,
6796
7384
  DisplayPosition,
7385
+ DisplayTransforms,
6797
7386
  Entities,
6798
- EntityModels,
6799
7387
  EntityObject,
6800
7388
  Env,
6801
7389
  Geometries,
@@ -6823,6 +7411,7 @@ export {
6823
7411
  OrbitControls,
6824
7412
  PersistentCache,
6825
7413
  Renderer,
7414
+ RequestError,
6826
7415
  Requests,
6827
7416
  SKIN_PARTS,
6828
7417
  SceneInspector,
@@ -6830,8 +7419,10 @@ export {
6830
7419
  SceneStats,
6831
7420
  SceneStatsDisplay,
6832
7421
  SchematicParser,
7422
+ SkinImage,
6833
7423
  SkinObject,
6834
7424
  SkinPart,
7425
+ SkinTextures,
6835
7426
  Skins,
6836
7427
  StructureParser,
6837
7428
  TextureAtlas,
@@ -6846,10 +7437,10 @@ export {
6846
7437
  addWireframeToObject,
6847
7438
  applyElementRotation,
6848
7439
  applyGenericRotation,
6849
- applyModelPartRotation,
6850
7440
  axisToVec3,
6851
7441
  base64decode,
6852
7442
  base64encode,
7443
+ capeTextureSizes,
6853
7444
  captureException,
6854
7445
  changeEvent,
6855
7446
  clampRotationDegrees,