minerender 2.1.0-alpha.3 → 2.1.0-alpha.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
 
@@ -149,25 +141,29 @@ var Env = class {
149
141
  };
150
142
 
151
143
  // src/cache/PersistentCache.ts
152
- var _PersistentCache = class _PersistentCache {
144
+ var PersistentCache = class _PersistentCache {
153
145
  constructor(backing) {
154
146
  this.backing = backing;
155
147
  }
148
+ static {
149
+ /** Bump to invalidate every persisted entry. */
150
+ this.VERSION = 2;
151
+ }
156
152
  static open(name) {
157
153
  return Env.provider.openCache(name, _PersistentCache.VERSION);
158
154
  }
155
+ /** Loads a missing entry and stores only non-nullish results. */
159
156
  async getOrLoad(key, loader) {
160
157
  let v = await this.get(key);
161
158
  if (typeof v === "undefined" || v === null) {
162
159
  v = await loader(key);
163
- await this.put(key, v);
160
+ if (typeof v !== "undefined" && v !== null) {
161
+ await this.put(key, v);
162
+ }
164
163
  }
165
164
  return v;
166
165
  }
167
166
  };
168
- /** Bump to invalidate every persisted entry. */
169
- _PersistentCache.VERSION = 1;
170
- var PersistentCache = _PersistentCache;
171
167
 
172
168
  // src/env/node/NodeCache.ts
173
169
  var NodeCache = class extends PersistentCache {
@@ -245,7 +241,7 @@ registerNodeEnv();
245
241
  // src/Axis.ts
246
242
  import { Vector3 } from "three";
247
243
  var Axis = /* @__PURE__ */ ((Axis3) => {
248
- Axis3["X"] = "X";
244
+ Axis3["X"] = "x";
249
245
  Axis3["Y"] = "y";
250
246
  Axis3["Z"] = "z";
251
247
  return Axis3;
@@ -253,7 +249,7 @@ var Axis = /* @__PURE__ */ ((Axis3) => {
253
249
  var AXES = Object.values(Axis);
254
250
  function axisToVec3(axis) {
255
251
  switch (axis) {
256
- case "X" /* X */:
252
+ case "x" /* X */:
257
253
  return new Vector3(1, 0, 0);
258
254
  case "y" /* Y */:
259
255
  return new Vector3(0, 1, 0);
@@ -341,8 +337,7 @@ function serializeImageKey(key) {
341
337
  return md5(key.src);
342
338
  }
343
339
  function serializeBoxGeometryKey(key) {
344
- var _a;
345
- return md5([key.width, key.height, key.depth, (_a = key.uv) == null ? void 0 : _a.join(":")].join("_"));
340
+ return md5([key.width, key.height, key.depth, key.uv?.join(":")].join("_"));
346
341
  }
347
342
  function serializeTextureKey(key) {
348
343
  return md5([key.src, key.format, key.rotation].join("$"));
@@ -357,7 +352,67 @@ function serializeMeshKey(key) {
357
352
  // src/cache/Caching.ts
358
353
  import { Caches } from "@inventivetalent/loading-cache";
359
354
  import { Time } from "@inventivetalent/time";
360
- var Caching = class {
355
+ var Caching = class _Caching {
356
+ static {
357
+ this.rawImageCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(5)).buildAsync();
358
+ }
359
+ static {
360
+ this.imageDataCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(10)).buildAsync();
361
+ }
362
+ static {
363
+ this.canvasImageDataCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(10)).buildAsync();
364
+ }
365
+ static {
366
+ this.wrappedImageCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(10)).buildAsync();
367
+ }
368
+ static {
369
+ this.boxGeometryCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(20)).build();
370
+ }
371
+ static {
372
+ this.textureCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).build();
373
+ }
374
+ static {
375
+ this.materialCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).build();
376
+ }
377
+ static {
378
+ this.boxMeshCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).build();
379
+ }
380
+ static {
381
+ this.textureAssetCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
382
+ }
383
+ static {
384
+ this.textureMetaCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
385
+ }
386
+ static {
387
+ this.rawModelCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
388
+ }
389
+ static {
390
+ this.mergedModelCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
391
+ }
392
+ static {
393
+ this.modelTextureAtlasCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
394
+ }
395
+ static {
396
+ this.blockStateCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
397
+ }
398
+ static {
399
+ this.listAssetCache = _Caching.createAssetCache();
400
+ }
401
+ static {
402
+ this.defaultBlockStatesCache = _Caching.createAssetCache();
403
+ }
404
+ static {
405
+ this.blockTintCache = _Caching.createAssetCache();
406
+ }
407
+ static {
408
+ this.entityModelCache = _Caching.createAssetCache();
409
+ }
410
+ static {
411
+ this.entityTextureCache = _Caching.createAssetCache();
412
+ }
413
+ static createAssetCache() {
414
+ return Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
415
+ }
361
416
  /** Every cache above, so clear()/end() can never fall out of sync with the field list again. */
362
417
  static get all() {
363
418
  return [
@@ -374,7 +429,12 @@ var Caching = class {
374
429
  this.rawModelCache,
375
430
  this.mergedModelCache,
376
431
  this.modelTextureAtlasCache,
377
- this.blockStateCache
432
+ this.blockStateCache,
433
+ this.listAssetCache,
434
+ this.defaultBlockStatesCache,
435
+ this.blockTintCache,
436
+ this.entityModelCache,
437
+ this.entityTextureCache
378
438
  ];
379
439
  }
380
440
  static clear() {
@@ -385,8 +445,8 @@ var Caching = class {
385
445
  /**
386
446
  * Clears every cache and stops its expiry timer.
387
447
  *
388
- * loading-cache reschedules that timer indefinitely, so a Node process will not exit until
389
- * this is called - see {@link shutdown}.
448
+ * Expiry timers are unref'd in Node, so idle processes can exit without this cleanup.
449
+ * Use {@link shutdown} to also stop requests and the shared Ticker.
390
450
  */
391
451
  static end() {
392
452
  for (const cache of this.all) {
@@ -400,20 +460,6 @@ var Caching = class {
400
460
  };
401
461
  }
402
462
  };
403
- Caching.rawImageCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(5)).buildAsync();
404
- Caching.imageDataCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(10)).buildAsync();
405
- Caching.canvasImageDataCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(10)).buildAsync();
406
- Caching.wrappedImageCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(10)).buildAsync();
407
- Caching.boxGeometryCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(20)).build();
408
- Caching.textureCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).build();
409
- Caching.materialCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).build();
410
- Caching.boxMeshCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).build();
411
- Caching.textureAssetCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
412
- Caching.textureMetaCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
413
- Caching.rawModelCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
414
- Caching.mergedModelCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
415
- Caching.modelTextureAtlasCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
416
- Caching.blockStateCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
417
463
 
418
464
  // src/Geometries.ts
419
465
  var Geometries = class _Geometries {
@@ -441,6 +487,7 @@ import { CanvasTexture, Texture as Texture2 } from "three";
441
487
 
442
488
  // src/texture/TextureLoader.ts
443
489
  import { RGBAFormat, Texture } from "three";
490
+ import * as THREE from "three";
444
491
 
445
492
  // src/canvas/CanvasCompat.ts
446
493
  function createImage(width, height) {
@@ -451,9 +498,7 @@ function createCanvas2(width, height) {
451
498
  }
452
499
 
453
500
  // src/request/Requests.ts
454
- import axios from "axios";
455
- import { JobQueue } from "jobqu";
456
- import { Time as Time2 } from "@inventivetalent/time";
501
+ import { JobCancelledError, JobQueue } from "jobqu";
457
502
 
458
503
  // src/util/log.ts
459
504
  var Reset = "\x1B[0m";
@@ -463,198 +508,174 @@ function prefix(prefix2) {
463
508
  return `${FgGreen}MR${BgBlack}${prefix2 ?? ""}${Reset}`;
464
509
  }
465
510
 
466
- // src/util/BatchedExecutor.ts
467
- var BatchedExecutor = class {
468
- constructor(interval = 1, batch = 30) {
469
- this.interval = interval;
470
- this.batch = batch;
471
- this.queue = [];
472
- this.task = setInterval(() => this.run());
473
- }
474
- submit(task) {
475
- this.queue.push(task);
476
- }
477
- run() {
478
- for (let i = 0; i < this.batch; i++) {
479
- this.runNext();
480
- }
481
- }
482
- runNext() {
483
- let next = this.queue.shift();
484
- 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";
485
519
  }
486
520
  };
487
-
488
- // src/util/model.ts
489
- import { AxesHelper, BoxGeometry as BoxGeometry2, EdgesGeometry, LineBasicMaterial, LineSegments, Vector3 as Vector32 } from "three";
490
- function applyElementRotation(rotation, geometry) {
491
- const origin = new Vector32(rotation.origin[0], rotation.origin[1], rotation.origin[2]);
492
- geometry.translate(-origin.x, -origin.y, -origin.z);
493
- switch (rotation.axis) {
494
- case "X" /* X */:
495
- geometry.rotateX(toRadians(rotation.angle));
496
- break;
497
- case "y" /* Y */:
498
- geometry.rotateY(toRadians(rotation.angle));
499
- break;
500
- case "z" /* Z */:
501
- geometry.rotateZ(toRadians(rotation.angle));
502
- break;
503
- }
504
- if (rotation.rescale) {
505
- if (Math.abs(rotation.angle) === 22.5) {
506
- const s = 1 / Math.cos(0.39269909262657166);
507
- geometry.scale(s, 1, s);
508
- } else {
509
- const s = 1 / Math.cos(0.7853981852531433);
510
- 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);
511
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);
555
+ }
556
+ throw error;
512
557
  }
513
- geometry.translate(origin.x, origin.y, origin.z);
514
558
  }
515
- function applyModelPartRotation(modelPart, geometry) {
516
- const origin = new Vector32(modelPart.pivotX, modelPart.pivotY, modelPart.pivotZ);
517
- geometry.translate(-origin.x, -origin.y, -origin.z);
518
- geometry.rotateX(toRadians(modelPart.pitch));
519
- geometry.rotateY(toRadians(modelPart.yaw));
520
- geometry.rotateZ(toRadians(modelPart.roll));
521
- geometry.translate(origin.x, origin.y, origin.z);
522
- }
523
- function applyGenericRotation(axis, rotation, obj) {
524
- switch (axis) {
525
- case "X" /* X */:
526
- obj.rotateOnAxis(new Vector32(1, 0, 0), toRadians(rotation));
527
- break;
528
- case "y" /* Y */:
529
- obj.rotateOnAxis(new Vector32(0, 1, 0), toRadians(rotation));
530
- break;
531
- case "z" /* Z */:
532
- obj.rotateOnAxis(new Vector32(0, 0, 1), toRadians(rotation));
533
- 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);
534
571
  }
535
572
  }
536
- function addBox3WireframeToObject(box, obj, color = 16777215, w = 2) {
537
- let size = new Vector32();
538
- box.getSize(size);
539
- let boxGeo = new BoxGeometry2(size.x, size.y, size.z, 1, 1, 1);
540
- let wireGeo = new EdgesGeometry(boxGeo);
541
- let wireMat = new LineBasicMaterial({ color, linewidth: w });
542
- let wireframe = new LineSegments(wireGeo, wireMat);
543
- obj.add(wireframe);
544
- let axes = new AxesHelper(1);
545
- obj.add(axes);
546
- }
547
- function addWireframeToObject(obj, color = 16777215, w = 2) {
548
- let boxGeo = new BoxGeometry2(16, 16, 16, 1, 1, 1);
549
- let wireGeo = new EdgesGeometry(boxGeo);
550
- let wireMat = new LineBasicMaterial({ color, linewidth: w });
551
- let wireframe = new LineSegments(wireGeo, wireMat);
552
- obj.add(wireframe);
553
- let axes = new AxesHelper(1);
554
- obj.add(axes);
555
- }
556
- function addWireframeToMesh(geo, mesh, color = 16777215, w = 2) {
557
- let wireGeo = new EdgesGeometry(geo);
558
- let wireMat = new LineBasicMaterial({ color, linewidth: w });
559
- let wireframe = new LineSegments(wireGeo, wireMat);
560
- mesh.add(wireframe);
561
- let axes = new AxesHelper(1);
562
- mesh.add(axes);
563
- }
564
-
565
- // src/util/performance.ts
566
- function captureException(e, ...extra) {
567
- console.error(e, extra);
568
- }
569
- function timingStart(marker) {
570
- console.time(marker);
571
- }
572
- function timingEnd(marker) {
573
- console.timeEnd(marker);
574
- }
575
-
576
- // src/util/three.ts
577
- function isObject3D(obj) {
578
- return obj.isObject3D;
579
- }
580
- function isMesh(obj) {
581
- return obj.isMesh;
582
- }
583
- function isInstancedMesh(obj) {
584
- return obj.isInstancedMesh;
585
- }
586
- function isPerspectiveCamera(obj) {
587
- return obj.isPerspectiveCamera;
588
- }
589
- function isOrthographicCamera(obj) {
590
- return obj.isOrthographicCamera;
591
- }
592
-
593
- // src/request/Requests.ts
594
- var p = prefix("Requests");
595
- if (typeof window === "undefined") {
596
- axios.defaults.headers["User-Agent"] = "MineRender";
597
- }
598
- var _Requests = class _Requests {
599
- static genericRequest(request) {
600
- return this.genericQueue.add(request).catch(async (e) => {
601
- console.debug(p, "generic catch", e.code, e);
602
- if (this.shouldRetry(e, request)) {
603
- await sleep(100);
604
- return this.genericRequest(request);
605
- }
606
- throw e;
607
- });
608
- }
609
- static mcAssetRequest(request) {
610
- return this.mcAssetRequestQueue.add(request).catch((e) => {
611
- console.debug(p, "mcAssetRequest catch", e.code, e);
612
- throw e;
613
- });
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
+ );
614
581
  }
615
- static shouldRetry(e, request) {
616
- if ("code" in e) {
617
- if ("ECONNABORTED" === e.code) {
618
- if ("minerenderRetry" in request) {
619
- if (request["minerenderRetry"] > 0) {
620
- request["minerenderRetry"]--;
621
- 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;
622
595
  }
623
- } else {
624
- request["minerenderRetry"] = 3;
625
- return true;
596
+ await this.waitForRetry(delay, signal);
626
597
  }
627
598
  }
599
+ } finally {
600
+ if (signal?.aborted) {
601
+ this.queue.remove(request);
602
+ }
603
+ this.pendingRequests--;
628
604
  }
629
- 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 {
648
+ this.genericQueue = new RequestQueue();
649
+ }
650
+ static {
651
+ this.mcAssetRequestQueue = new RequestQueue();
652
+ }
653
+ static genericRequest(request) {
654
+ return this.genericQueue.request(request);
655
+ }
656
+ static mcAssetRequest(request) {
657
+ return this.mcAssetRequestQueue.request(request);
630
658
  }
631
659
  static setMcAssetRoot(root) {
632
- this.mcAssetInstance.defaults.baseURL = root;
660
+ this.mcAssetRequestQueue.baseURL = root;
633
661
  }
662
+ /** Counts unsettled calls, including retry delays and requests still running after shutdown. */
634
663
  static get queueSizes() {
635
664
  return {
636
- generic: this.genericQueue.size,
637
- mcAsset: this.mcAssetRequestQueue.size
665
+ generic: this.genericQueue.pendingSize,
666
+ mcAsset: this.mcAssetRequestQueue.pendingSize
638
667
  };
639
668
  }
640
669
  /**
641
- * Stops both queue timers. jobqu reschedules itself indefinitely, so a Node process will not
642
- * exit until this is called - see {@link shutdown}.
670
+ * Permanently stops both queues and rejects waiting, retrying, and future requests.
671
+ * Requests already running can finish. Idle queues hold no timer, so Node can exit without
672
+ * this cleanup. See {@link shutdown}.
643
673
  */
644
674
  static end() {
645
675
  this.genericQueue.end();
646
676
  this.mcAssetRequestQueue.end();
647
677
  }
648
678
  };
649
- _Requests.axiosInstance = axios.create({
650
- timeout: 5e3
651
- });
652
- _Requests.mcAssetInstance = axios.create({
653
- timeout: 5e3
654
- });
655
- _Requests.genericQueue = new JobQueue((request) => _Requests.axiosInstance.request(request), Time2.millis(10), 1);
656
- _Requests.mcAssetRequestQueue = new JobQueue((request) => _Requests.mcAssetInstance.request(request), Time2.millis(10), 1);
657
- var Requests = _Requests;
658
679
 
659
680
  // src/WrappedImage.ts
660
681
  var WrappedImage = class {
@@ -692,10 +713,10 @@ var WrappedImage = class {
692
713
  return this.animated ? this.width : this.height;
693
714
  }
694
715
  get frameCount() {
695
- return this.height / this.width;
716
+ return this.height / this.frameHeight;
696
717
  }
697
718
  getFrameY(frame) {
698
- return this.height / this.frameCount * Math.max(0, Math.min(this.frameCount, frame));
719
+ return this.frameHeight * Math.max(0, Math.min(this.frameCount - 1, frame));
699
720
  }
700
721
  getSectionData(sx, sy, sw, sh) {
701
722
  return this.dta.data.getImageData(sx, sy, sw, sh);
@@ -705,7 +726,7 @@ var WrappedImage = class {
705
726
  }
706
727
  getFrameSectionData(frame) {
707
728
  const y = this.getFrameY(frame);
708
- return this.getSectionData(0, y, this.width, this.width + y);
729
+ return this.getSectionData(0, y, this.frameWidth, this.frameHeight);
709
730
  }
710
731
  };
711
732
 
@@ -719,29 +740,41 @@ var ImageLoader = class _ImageLoader {
719
740
  static async loadAsync(src) {
720
741
  return new Promise((resolve, reject) => {
721
742
  const image = this._createImage();
722
- image.src = src;
723
743
  image.onload = () => resolve(image);
724
- image.onerror = (err) => reject(err);
744
+ image.onerror = (err) => reject(err instanceof Error ? err : new Error("Failed to decode image"));
745
+ image.src = src;
725
746
  });
726
747
  }
727
748
  static loadElement(src, onload, onerr) {
728
749
  const image = this._createImage();
729
- image.src = src;
730
750
  if (onload)
731
751
  image.onload = onload;
732
752
  if (onerr)
733
753
  image.onerror = onerr;
754
+ image.src = src;
734
755
  return image;
735
756
  }
736
757
  static async loadData(src) {
737
758
  console.debug(p2, "loadData", src);
738
- return await this.infoToData(await this.getInfo(src));
759
+ const image = await this.loadCanvasData(src);
760
+ return image.data.getImageData(0, 0, image.width, image.height);
739
761
  }
740
762
  static async loadCanvasData(src) {
741
- return await this.infoToCanvasData(await this.getInfo(src));
763
+ const keyStr = serializeImageKey({ src });
764
+ const info = this.getInfo(src);
765
+ const cached = Caching.rawImageCache.getIfPresent(keyStr);
766
+ try {
767
+ return await this.infoToCanvasData(await info);
768
+ } catch (err) {
769
+ if (cached && Caching.rawImageCache.getIfPresent(keyStr) === cached) {
770
+ Caching.rawImageCache.invalidate(keyStr);
771
+ }
772
+ throw err;
773
+ }
742
774
  }
743
775
  static async infoToCanvasData(info) {
744
- const image = await _ImageLoader.loadAsync(info.src);
776
+ const type = info.type === "jpg" ? "jpeg" : info.type ?? "png";
777
+ const image = await _ImageLoader.loadAsync(`data:image/${type};base64,${info.data.toString("base64")}`);
745
778
  const canvas = createCanvas2(info.width, info.height);
746
779
  const context = canvas.getContext("2d");
747
780
  context.drawImage(image, 0, 0);
@@ -752,11 +785,8 @@ var ImageLoader = class _ImageLoader {
752
785
  };
753
786
  }
754
787
  static async infoToData(info) {
755
- const image = await _ImageLoader.loadAsync(info.src);
756
- const canvas = createCanvas2(info.width, info.height);
757
- const context = canvas.getContext("2d");
758
- context.drawImage(image, 0, 0);
759
- return context.getImageData(0, 0, canvas.width, canvas.height);
788
+ const image = await this.infoToCanvasData(info);
789
+ return image.data.getImageData(0, 0, image.width, image.height);
760
790
  }
761
791
  static async getData(src) {
762
792
  const keyStr = serializeImageKey({ src });
@@ -771,38 +801,25 @@ var ImageLoader = class _ImageLoader {
771
801
  });
772
802
  }
773
803
  static async processResponse(response) {
774
- const src = response.config.url;
804
+ const src = response.url;
775
805
  const data = Buffer2.from(response.data);
776
806
  const { width, height, type } = Env.provider.imageSize(data);
807
+ if (!width || !height) {
808
+ throw new Error("Invalid or unsupported image dimensions");
809
+ }
777
810
  return {
778
811
  src,
779
- width: width || 0,
780
- height: height || 0,
812
+ width,
813
+ height,
781
814
  type,
782
815
  data
783
816
  };
784
817
  }
785
818
  static async loadInfo(src) {
786
- if (src.startsWith("data:image/png;base64")) {
787
- return this.processResponse({
788
- config: {
789
- url: src
790
- },
791
- data: Buffer2.from(src.substr("data:image/png;base64,".length), "base64")
792
- });
793
- }
794
819
  return Requests.genericRequest({
795
820
  url: src,
796
821
  responseType: "arraybuffer"
797
- }).then(this.processResponse).catch((err) => {
798
- return {
799
- src,
800
- width: 0,
801
- height: 0,
802
- type: void 0,
803
- data: Buffer2.from([0])
804
- };
805
- });
822
+ }).then(this.processResponse);
806
823
  }
807
824
  static async getInfo(src) {
808
825
  const keyStr = serializeImageKey({ src });
@@ -824,15 +841,25 @@ var ImageLoader = class _ImageLoader {
824
841
 
825
842
  // src/texture/TextureLoader.ts
826
843
  var TextureLoader = class {
844
+ static {
845
+ this.failedTextures = /* @__PURE__ */ new WeakSet();
846
+ }
847
+ /** Reports whether image loading failed, so cached textures can be retried. */
848
+ static hasFailed(texture) {
849
+ return this.failedTextures.has(texture);
850
+ }
827
851
  static createTexture() {
828
852
  return new Texture();
829
853
  }
830
854
  static loadInBackground(src, format = RGBAFormat, rotation = 0) {
831
855
  const texture = this.createTexture();
832
- const image = ImageLoader.loadElement(src);
833
- image.onload = function() {
856
+ texture.colorSpace = THREE.SRGBColorSpace;
857
+ const image = ImageLoader.loadElement(src, () => {
834
858
  texture.needsUpdate = true;
835
- };
859
+ }, (error) => {
860
+ this.failedTextures.add(texture);
861
+ console.warn("Failed to load texture", src, error);
862
+ });
836
863
  texture.image = image;
837
864
  texture.format = format;
838
865
  texture.rotation = rotation;
@@ -840,9 +867,13 @@ var TextureLoader = class {
840
867
  }
841
868
  static load(src, format = RGBAFormat, rotation = 0) {
842
869
  const texture = new Texture();
870
+ texture.colorSpace = THREE.SRGBColorSpace;
843
871
  ImageLoader.getData(src).then((image) => {
844
- texture.needsUpdate = true;
845
872
  texture.image = image;
873
+ texture.needsUpdate = true;
874
+ }).catch((error) => {
875
+ this.failedTextures.add(texture);
876
+ console.warn("Failed to load texture", src, error);
846
877
  });
847
878
  texture.format = format;
848
879
  texture.rotation = rotation;
@@ -851,11 +882,11 @@ var TextureLoader = class {
851
882
  };
852
883
 
853
884
  // src/texture/Textures.ts
854
- import * as THREE from "three";
885
+ import * as THREE2 from "three";
855
886
  var Textures = class _Textures {
856
887
  static initTextureProps(texture) {
857
- texture.magFilter = THREE.NearestFilter;
858
- texture.minFilter = THREE.NearestFilter;
888
+ texture.magFilter = THREE2.NearestFilter;
889
+ texture.minFilter = THREE2.NearestFilter;
859
890
  texture.anisotropy = 0;
860
891
  return texture;
861
892
  }
@@ -866,10 +897,16 @@ var Textures = class _Textures {
866
897
  return new Texture2();
867
898
  }
868
899
  static createCanvasTexture(canvas) {
869
- return this.initTextureProps(new CanvasTexture(canvas));
900
+ const texture = new CanvasTexture(canvas);
901
+ texture.colorSpace = THREE2.SRGBColorSpace;
902
+ return this.initTextureProps(texture);
870
903
  }
871
904
  static getImage(key) {
872
905
  const keyStr = serializeTextureKey(key);
906
+ const cached = Caching.textureCache.peek(keyStr);
907
+ if (cached && TextureLoader.hasFailed(cached)) {
908
+ Caching.textureCache.invalidate(keyStr);
909
+ }
873
910
  return Caching.textureCache.get(keyStr, (k) => {
874
911
  return _Textures.createImage(key);
875
912
  });
@@ -877,7 +914,10 @@ var Textures = class _Textures {
877
914
  };
878
915
 
879
916
  // src/Materials.ts
880
- var _Materials = class _Materials {
917
+ var Materials = class _Materials {
918
+ static {
919
+ this.MISSING_TEXTURE_SRC = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABAAQMAAACQp+OdAAAABlBMVEX/AP8AAACfphTyAAAAFUlEQVQoz2MIhQKGVVAwKjIqQrwIAHRz/wFI17TEAAAAAElFTkSuQmCC";
920
+ }
881
921
  // resolved lazily: as a static field this decoded an image the moment the library was
882
922
  // imported, which needs both a DOM and an EnvProvider to already be in place
883
923
  static get MISSING_TEXTURE() {
@@ -913,6 +953,7 @@ var _Materials = class _Materials {
913
953
  uniform bool SHADE;
914
954
 
915
955
  varying vec2 vUv;
956
+ varying vec3 vTint;
916
957
  varying float light;
917
958
  varying float lift;
918
959
 
@@ -925,7 +966,11 @@ var _Materials = class _Materials {
925
966
 
926
967
  if (SHADE) {
927
968
 
928
- vec3 N = vec3( modelMatrix * instanceMatrix * vec4(normal, 0.0) );
969
+ #ifdef USE_INSTANCING
970
+ vec3 N = vec3( modelMatrix * instanceMatrix * vec4(normal, 0.0) );
971
+ #else
972
+ vec3 N = vec3( modelMatrix * vec4(normal, 0.0) );
973
+ #endif
929
974
 
930
975
  float yLight = (1.0+N.y) * 0.5;
931
976
  light = yLight * (1.0-AMBIENT) + N.x*N.x * XFAC + N.z*N.z * ZFAC + AMBIENT;
@@ -943,6 +988,7 @@ var _Materials = class _Materials {
943
988
  }
944
989
 
945
990
  vUv = uv;
991
+ vTint = color;
946
992
 
947
993
  #ifdef USE_INSTANCING
948
994
  gl_Position = projectionMatrix * viewMatrix * modelMatrix * instanceMatrix * vec4(position, 1.0);
@@ -964,12 +1010,14 @@ var _Materials = class _Materials {
964
1010
  uniform float BRIGHTNESS;
965
1011
 
966
1012
  varying vec2 vUv;
1013
+ varying vec3 vTint;
967
1014
  varying float light;
968
1015
  varying float lift;
969
1016
 
970
1017
  void main(void)
971
1018
  {
972
1019
  vec4 color = texture2D(map, vUv);
1020
+ color.rgb *= vTint;
973
1021
 
974
1022
  if (color.a < 0.01) discard;
975
1023
 
@@ -983,6 +1031,9 @@ var _Materials = class _Materials {
983
1031
  gl_FragColor = vec4(lift + color.rgb * light2, 1.0);
984
1032
 
985
1033
  }
1034
+
1035
+ #include <tonemapping_fragment>
1036
+ #include <colorspace_fragment>
986
1037
  }
987
1038
  `;
988
1039
  try {
@@ -1011,6 +1062,10 @@ var _Materials = class _Materials {
1011
1062
  }
1012
1063
  static getImage(key) {
1013
1064
  const keyStr = serializeMaterialKey(key);
1065
+ const map = Caching.materialCache.peek(keyStr)?.map;
1066
+ if (map && TextureLoader.hasFailed(map)) {
1067
+ Caching.materialCache.invalidate(keyStr);
1068
+ }
1014
1069
  return Caching.materialCache.get(keyStr, (k) => {
1015
1070
  return _Materials.createImage(key);
1016
1071
  });
@@ -1031,14 +1086,12 @@ var _Materials = class _Materials {
1031
1086
  }
1032
1087
  }
1033
1088
  };
1034
- _Materials.MISSING_TEXTURE_SRC = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABAAQMAAACQp+OdAAAABlBMVEX/AP8AAACfphTyAAAAFUlEQVQoz2MIhQKGVVAwKjIqQrwIAHRz/wFI17TEAAAAAElFTkSuQmCC";
1035
- var Materials = _Materials;
1036
1089
 
1037
1090
  // src/Meshes.ts
1038
- import { Mesh as Mesh2 } from "three";
1091
+ import { Mesh } from "three";
1039
1092
  var Meshes = class _Meshes {
1040
1093
  static createBox(key) {
1041
- return new Mesh2(Geometries.getBox(key.geometry), Materials.getImage(key.material));
1094
+ return new Mesh(Geometries.getBox(key.geometry), Materials.getImage(key.material));
1042
1095
  }
1043
1096
  static getBox(key) {
1044
1097
  const keyStr = serializeMeshKey(key);
@@ -1049,7 +1102,7 @@ var Meshes = class _Meshes {
1049
1102
  };
1050
1103
 
1051
1104
  // src/UVMapper.ts
1052
- import { Float32BufferAttribute as Float32BufferAttribute2, Vector2 } from "three";
1105
+ import { Float32BufferAttribute as Float32BufferAttribute2, Vector2, Vector3 as Vector34 } from "three";
1053
1106
 
1054
1107
  // src/model/ModelMerger.ts
1055
1108
  var import_ts_deepmerge = __toESM(require_dist());
@@ -1085,9 +1138,10 @@ var AssetKey = class _AssetKey extends BasicAssetKey {
1085
1138
  this.rootType = rootType;
1086
1139
  this.extension = extension;
1087
1140
  this.root = root;
1141
+ this.isAssetKey = true;
1088
1142
  }
1089
1143
  static parse(assetType, str, origin) {
1090
- let namespace = (origin == null ? void 0 : origin.namespace) || DEFAULT_NAMESPACE;
1144
+ let namespace = origin?.namespace || DEFAULT_NAMESPACE;
1091
1145
  if (str.includes(":")) {
1092
1146
  let split2 = str.split(":");
1093
1147
  namespace = split2[0];
@@ -1100,12 +1154,15 @@ var AssetKey = class _AssetKey extends BasicAssetKey {
1100
1154
  split.shift();
1101
1155
  }
1102
1156
  let path = split.join("/");
1103
- let extension = assetType === "textures" ? ".png" : (origin == null ? void 0 : origin.assetType) || ".json";
1104
- return new _AssetKey(namespace, path, assetType, type, "assets", extension, origin == null ? void 0 : origin.root);
1157
+ const extension = assetType === "textures" ? ".png" : origin?.extension ?? ".json";
1158
+ if (extension && path.endsWith(extension)) {
1159
+ path = path.slice(0, -extension.length);
1160
+ }
1161
+ return new _AssetKey(namespace, path, assetType, type, "assets", extension, origin?.root);
1105
1162
  }
1106
1163
  toString() {
1107
1164
  let a = [
1108
- typeof this.root !== "undefined" || AssetLoader.ROOT !== DEFAULT_ROOT ? this.root ?? AssetLoader.ROOT : "__root__",
1165
+ this.root ?? AssetLoader.ROOT,
1109
1166
  this.rootType ?? "__rootType__",
1110
1167
  this.assetType ?? "__assetType__",
1111
1168
  this.type ?? "__type__",
@@ -1115,12 +1172,12 @@ var AssetKey = class _AssetKey extends BasicAssetKey {
1115
1172
  return a.join("/");
1116
1173
  }
1117
1174
  getFullPath() {
1118
- let p13 = [];
1175
+ let p12 = [];
1119
1176
  if (this.type) {
1120
- p13.push(this.type);
1177
+ p12.push(this.type);
1121
1178
  }
1122
- p13.push(this.path);
1123
- return p13.join("/");
1179
+ p12.push(this.path);
1180
+ return p12.join("/");
1124
1181
  }
1125
1182
  toNamespacedString() {
1126
1183
  return this.namespace + ":" + this.getFullPath();
@@ -1130,22 +1187,84 @@ var AssetKey = class _AssetKey extends BasicAssetKey {
1130
1187
  }
1131
1188
  };
1132
1189
  function isBasicAssetKey(obj) {
1133
- return "namespace" in obj && "path" in obj;
1190
+ return obj !== null && typeof obj === "object" && typeof obj.namespace === "string" && typeof obj.path === "string";
1134
1191
  }
1135
1192
  function isAssetKey(obj) {
1136
- return isBasicAssetKey(obj) && "parse" in obj;
1193
+ return isBasicAssetKey(obj) && obj.isAssetKey === true;
1137
1194
  }
1138
1195
 
1196
+ // src/model/DisplayTransforms.ts
1197
+ import { Euler, Matrix4, Quaternion, Vector3 as Vector32 } from "three";
1198
+
1199
+ // src/model/DisplayPosition.ts
1200
+ var DisplayPosition = /* @__PURE__ */ ((DisplayPosition2) => {
1201
+ DisplayPosition2["THIRDPERSON_RIGHTHAND"] = "thirdperson_righthand";
1202
+ DisplayPosition2["THIRDPERSON_LEFTHAND"] = "thirdperson_lefthand";
1203
+ DisplayPosition2["FIRSTPERSON_RIGHTHAND"] = "firstperson_righthand";
1204
+ DisplayPosition2["FIRSTPERSON_LEFTHAND"] = "firstperson_lefthand";
1205
+ DisplayPosition2["GUI"] = "gui";
1206
+ DisplayPosition2["HEAD"] = "head";
1207
+ DisplayPosition2["GROUND"] = "ground";
1208
+ DisplayPosition2["FIXED"] = "fixed";
1209
+ DisplayPosition2["ON_SHELF"] = "on_shelf";
1210
+ return DisplayPosition2;
1211
+ })(DisplayPosition || {});
1212
+ var DISPLAY_POSITIONS = Object.values(DisplayPosition);
1213
+
1214
+ // src/model/DisplayTransforms.ts
1215
+ var DisplayTransforms = class {
1216
+ /** Resolve hand fallbacks within one model, before inheriting parent display entries. */
1217
+ static withHandFallbacks(display) {
1218
+ if (!display) return void 0;
1219
+ const resolved = { ...display };
1220
+ for (const [left, right] of [
1221
+ ["thirdperson_lefthand" /* THIRDPERSON_LEFTHAND */, "thirdperson_righthand" /* THIRDPERSON_RIGHTHAND */],
1222
+ ["firstperson_lefthand" /* FIRSTPERSON_LEFTHAND */, "firstperson_righthand" /* FIRSTPERSON_RIGHTHAND */]
1223
+ ]) {
1224
+ if (resolved[left] === void 0 && resolved[right] !== void 0) resolved[left] = resolved[right];
1225
+ }
1226
+ return resolved;
1227
+ }
1228
+ /** Build a display pose for geometry already centered at the model's [8, 8, 8] pivot. */
1229
+ static getMatrix(display, position) {
1230
+ const transform = this.withHandFallbacks(display)?.[position] ?? {};
1231
+ const left = position === "firstperson_lefthand" /* FIRSTPERSON_LEFTHAND */ || position === "thirdperson_lefthand" /* THIRDPERSON_LEFTHAND */;
1232
+ const sign = left ? -1 : 1;
1233
+ const [x, y, z] = transform.rotation ?? [0, 0, 0];
1234
+ const rotation = new Quaternion().setFromEuler(new Euler(toRadians(x), sign * toRadians(y), sign * toRadians(z), "XYZ"));
1235
+ const translation = new Vector32(...transform.translation ?? [0, 0, 0]).clampScalar(-80, 80);
1236
+ translation.x *= sign;
1237
+ const scale = new Vector32(...transform.scale ?? [1, 1, 1]).clampScalar(-4, 4);
1238
+ return new Matrix4().compose(translation, rotation, scale);
1239
+ }
1240
+ static apply(geometry, matrix) {
1241
+ geometry.applyMatrix4(matrix);
1242
+ if (matrix.determinant() < 0) {
1243
+ if (!geometry.index) geometry.setIndex(Array.from({ length: geometry.getAttribute("position").count }, (_, i) => i));
1244
+ const index = geometry.index;
1245
+ for (let i = 0; i < index.count; i += 3) {
1246
+ const second = index.getX(i + 1);
1247
+ index.setX(i + 1, index.getX(i + 2));
1248
+ index.setX(i + 2, second);
1249
+ }
1250
+ index.needsUpdate = true;
1251
+ }
1252
+ }
1253
+ };
1254
+
1139
1255
  // src/model/ModelMerger.ts
1140
1256
  var ModelMerger = class {
1141
1257
  static async mergeWithParents(model) {
1142
1258
  const models = await this.collectAllParents(model);
1143
1259
  let merged = {};
1144
- for (let parentModel of models) {
1145
- merged = (0, import_ts_deepmerge.default)(merged, parentModel);
1260
+ for (const source of [...models, model]) {
1261
+ const inheritedDisplay = merged.display;
1262
+ merged = import_ts_deepmerge.default.withOptions({ mergeArrays: false }, merged, source);
1263
+ if (source.display) {
1264
+ merged.display = { ...inheritedDisplay, ...DisplayTransforms.withHandFallbacks(source.display) };
1265
+ }
1146
1266
  }
1147
- merged = (0, import_ts_deepmerge.default)(merged, model);
1148
- merged.hierarchy = models.map((m) => m.parent).filter((p13) => `${p13}`);
1267
+ merged.hierarchy = models.map((m) => m.parent).filter((p12) => `${p12}`);
1149
1268
  merged.hierarchy.push(`${merged.parent}`);
1150
1269
  return merged;
1151
1270
  }
@@ -1155,6 +1274,7 @@ var ModelMerger = class {
1155
1274
  }
1156
1275
  const models = [];
1157
1276
  const parentKey = AssetKey.parse("models", model.parent);
1277
+ parentKey.root = model.key?.root;
1158
1278
  const parentModel = await Models.getRaw(parentKey);
1159
1279
  if (parentModel) {
1160
1280
  models.unshift(parentModel);
@@ -1164,9 +1284,6 @@ var ModelMerger = class {
1164
1284
  }
1165
1285
  };
1166
1286
 
1167
- // src/assets/Models.ts
1168
- import { Memoize } from "typescript-memoize";
1169
-
1170
1287
  // src/assets/source/parser/AssetParsers.ts
1171
1288
  var AssetParser = /* @__PURE__ */ ((AssetParser2) => {
1172
1289
  AssetParser2["MODEL"] = "model";
@@ -1180,30 +1297,65 @@ var AssetParser = /* @__PURE__ */ ((AssetParser2) => {
1180
1297
  })(AssetParser || {});
1181
1298
 
1182
1299
  // src/assets/Models.ts
1183
- var _Models = class _Models {
1300
+ var Models = class _Models {
1184
1301
  // opened lazily: touching the store at import time would hit IndexedDB/disk just for loading
1185
1302
  // the library, and would require an EnvProvider before any entry has had a chance to register
1186
1303
  static get PERSISTENT_CACHE() {
1187
1304
  return this._persistentCache ??= PersistentCache.open("minerender-models");
1188
1305
  }
1189
1306
  static async getItemList() {
1190
- return AssetLoader.get(new AssetKey(
1191
- DEFAULT_NAMESPACE,
1192
- "_list",
1193
- "models",
1194
- "item",
1195
- "assets",
1196
- ".json",
1197
- DEFAULT_ROOT
1198
- ), "list" /* LIST */).then((r) => (r == null ? void 0 : r.files) ?? []);
1307
+ const key = new AssetKey(DEFAULT_NAMESPACE, "_list", "items");
1308
+ const legacyKey = new AssetKey(DEFAULT_NAMESPACE, "_list", "models", "item");
1309
+ return Caching.listAssetCache.get(key.serialize(), async () => {
1310
+ return (await AssetLoader.getFirst([key, legacyKey], "list" /* LIST */))?.asset;
1311
+ }).then((r) => r?.files ?? []);
1199
1312
  }
1200
1313
  static async loadAndMerge(key) {
1201
- const model = await this.getRaw(key);
1314
+ const model = key.type === "item" ? await this.getItemModel(key) : await this.getRaw(key);
1202
1315
  if (!model) {
1203
1316
  return void 0;
1204
1317
  }
1205
1318
  return ModelMerger.mergeWithParents(model);
1206
1319
  }
1320
+ static async getItemModel(key) {
1321
+ const itemKey = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
1322
+ const model = await this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(itemKey.serialize()), async () => {
1323
+ const result = await AssetLoader.getFirst([itemKey, key], "json" /* JSON */);
1324
+ if (!result) return void 0;
1325
+ if (result.key.assetType !== "items") return { ...result.asset, key };
1326
+ const reference = this.defaultItemModel(result.asset.model, key);
1327
+ const modelKey = AssetKey.parse("models", reference);
1328
+ modelKey.root = key.root;
1329
+ const model2 = await this.getRaw(modelKey);
1330
+ if (!model2) throw new Error(`Item ${key.toNamespacedString()} references missing model ${reference}`);
1331
+ return model2;
1332
+ });
1333
+ return model ? { ...model, key: Object.assign(new AssetKey("", ""), model.key) } : void 0;
1334
+ }
1335
+ // Item previews use the GUI context, false conditions, and zero numeric properties.
1336
+ static defaultItemModel(node, key) {
1337
+ if (!node || typeof node.type !== "string") {
1338
+ throw new Error(`Unsupported item model definition for ${key.toNamespacedString()}`);
1339
+ }
1340
+ switch (node.type.replace(/^minecraft:/, "")) {
1341
+ case "model":
1342
+ if (typeof node.model === "string" && node.model) return node.model;
1343
+ break;
1344
+ case "condition":
1345
+ return this.defaultItemModel(node.on_false, key);
1346
+ case "select": {
1347
+ 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;
1348
+ return this.defaultItemModel(selected ?? node.fallback, key);
1349
+ }
1350
+ case "range_dispatch": {
1351
+ const selected = node.entries?.reduce((match, entry) => {
1352
+ return entry.threshold <= 0 && (!match || entry.threshold >= match.threshold) ? entry : match;
1353
+ }, void 0);
1354
+ return this.defaultItemModel(selected?.model ?? node.fallback, key);
1355
+ }
1356
+ }
1357
+ throw new Error(`Unsupported item model ${node?.type ?? "definition"} for ${key.toNamespacedString()}`);
1358
+ }
1207
1359
  static async getRaw(key) {
1208
1360
  if (!key.assetType) {
1209
1361
  key.assetType = "models";
@@ -1213,7 +1365,7 @@ var _Models = class _Models {
1213
1365
  }
1214
1366
  const keyStr = key.serialize();
1215
1367
  return Caching.rawModelCache.get(keyStr, (k) => {
1216
- return this.PERSISTENT_CACHE.getOrLoad(keyStr, (k1) => {
1368
+ return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1217
1369
  return AssetLoader.get(key, "model" /* MODEL */);
1218
1370
  });
1219
1371
  }).then((asset) => {
@@ -1242,31 +1394,32 @@ var _Models = class _Models {
1242
1394
  return this.PERSISTENT_CACHE.clear();
1243
1395
  }
1244
1396
  };
1245
- __decorateClass([
1246
- Memoize()
1247
- ], _Models, "getItemList", 1);
1248
- var Models = _Models;
1249
1397
 
1250
1398
  // src/assets/BlockStates.ts
1251
- import { Memoize as Memoize2 } from "typescript-memoize";
1252
1399
  var BlockStates = class {
1253
1400
  static get PERSISTENT_CACHE() {
1254
1401
  return this._persistentCache ??= PersistentCache.open("minerender-blockstates");
1255
1402
  }
1403
+ // BlockState names are hardcoded
1256
1404
  static async getList() {
1257
- return AssetLoader.get(new AssetKey(
1405
+ const key = new AssetKey(
1258
1406
  DEFAULT_NAMESPACE,
1259
1407
  "_list",
1260
1408
  "blockstates",
1261
1409
  void 0,
1262
1410
  "assets",
1263
1411
  ".json",
1264
- DEFAULT_ROOT
1265
- ), "list" /* LIST */).then((r) => (r == null ? void 0 : r.files) ?? []);
1412
+ AssetLoader.ROOT
1413
+ );
1414
+ return Caching.listAssetCache.get(key.serialize(), () => {
1415
+ return AssetLoader.get(key, "list" /* LIST */);
1416
+ }).then((r) => r?.files ?? []);
1266
1417
  }
1267
1418
  static async getDefaultStates() {
1268
1419
  const key = AssetKey.parse("blockstates", "minerender:defaultBlockStates");
1269
- return AssetLoader.get(key, "json" /* JSON */);
1420
+ return Caching.defaultBlockStatesCache.get(key.serialize(), () => {
1421
+ return AssetLoader.get(key, "json" /* JSON */);
1422
+ });
1270
1423
  }
1271
1424
  static async getDefaultState(key) {
1272
1425
  const defaultStates = await this.getDefaultStates();
@@ -1284,7 +1437,7 @@ var BlockStates = class {
1284
1437
  }
1285
1438
  const keyStr = key.serialize();
1286
1439
  return Caching.blockStateCache.get(keyStr, (k) => {
1287
- return this.PERSISTENT_CACHE.getOrLoad(keyStr, (k1) => {
1440
+ return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1288
1441
  return AssetLoader.get(key, "blockstate" /* BLOCKSTATE */);
1289
1442
  });
1290
1443
  }).then((asset) => {
@@ -1305,17 +1458,11 @@ var BlockStates = class {
1305
1458
  await this.PERSISTENT_CACHE.clear();
1306
1459
  }
1307
1460
  };
1308
- __decorateClass([
1309
- Memoize2()
1310
- ], BlockStates, "getList", 1);
1311
- __decorateClass([
1312
- Memoize2()
1313
- ], BlockStates, "getDefaultStates", 1);
1314
1461
  var DefaultBlockStates = class {
1315
1462
  };
1316
1463
 
1317
1464
  // src/assets/Assets.ts
1318
- var DEFAULT_ROOT = "https://assets.mcasset.cloud/1.17.1";
1465
+ var DEFAULT_ROOT = "https://assets.mcasset.cloud/1.21.11";
1319
1466
  var DEFAULT_NAMESPACE = "minecraft";
1320
1467
  var Assets = class {
1321
1468
  /**
@@ -1336,7 +1483,144 @@ var Assets = class {
1336
1483
  var AssetSource = class {
1337
1484
  constructor() {
1338
1485
  }
1486
+ /**
1487
+ * Identifies this source's content across sessions for persistent caching. Sources that
1488
+ * cannot tell whether their content changed (an unnamed archive, for example) return
1489
+ * undefined, which keeps their results out of long-lived caches.
1490
+ */
1491
+ get cacheId() {
1492
+ return void 0;
1493
+ }
1339
1494
  };
1495
+ var AssetLoadError = class extends Error {
1496
+ constructor(source, key, path, cause) {
1497
+ super(`Failed to load ${key.toNamespacedString()} from ${path}${cause instanceof Error ? `: ${cause.message}` : ""}`);
1498
+ this.source = source;
1499
+ this.key = key;
1500
+ this.path = path;
1501
+ this.cause = cause;
1502
+ this.name = "AssetLoadError";
1503
+ }
1504
+ };
1505
+
1506
+ // src/util/BatchedExecutor.ts
1507
+ var BatchedExecutor = class {
1508
+ constructor(interval = 1, batch = 30) {
1509
+ this.interval = interval;
1510
+ this.batch = batch;
1511
+ this.queue = [];
1512
+ this.task = setInterval(() => this.run());
1513
+ }
1514
+ submit(task) {
1515
+ this.queue.push(task);
1516
+ }
1517
+ run() {
1518
+ for (let i = 0; i < this.batch; i++) {
1519
+ this.runNext();
1520
+ }
1521
+ }
1522
+ runNext() {
1523
+ let next = this.queue.shift();
1524
+ if (next) next();
1525
+ }
1526
+ };
1527
+
1528
+ // src/util/model.ts
1529
+ import { AxesHelper, BoxGeometry as BoxGeometry2, EdgesGeometry, LineBasicMaterial, LineSegments, Vector3 as Vector33 } from "three";
1530
+ function applyElementRotation(rotation, geometry) {
1531
+ const origin = new Vector33(rotation.origin[0], rotation.origin[1], rotation.origin[2]);
1532
+ geometry.translate(-origin.x, -origin.y, -origin.z);
1533
+ switch (rotation.axis) {
1534
+ case "x" /* X */:
1535
+ geometry.rotateX(toRadians(rotation.angle));
1536
+ break;
1537
+ case "y" /* Y */:
1538
+ geometry.rotateY(toRadians(rotation.angle));
1539
+ break;
1540
+ case "z" /* Z */:
1541
+ geometry.rotateZ(toRadians(rotation.angle));
1542
+ break;
1543
+ }
1544
+ if (rotation.rescale) {
1545
+ if (Math.abs(rotation.angle) === 22.5) {
1546
+ const s = 1 / Math.cos(0.39269909262657166);
1547
+ geometry.scale(s, 1, s);
1548
+ } else {
1549
+ const s = 1 / Math.cos(0.7853981852531433);
1550
+ geometry.scale(s, 1, s);
1551
+ }
1552
+ }
1553
+ geometry.translate(origin.x, origin.y, origin.z);
1554
+ }
1555
+ function applyGenericRotation(axis, rotation, obj) {
1556
+ switch (axis) {
1557
+ case "x" /* X */:
1558
+ obj.rotateOnAxis(new Vector33(1, 0, 0), toRadians(rotation));
1559
+ break;
1560
+ case "y" /* Y */:
1561
+ obj.rotateOnAxis(new Vector33(0, 1, 0), toRadians(rotation));
1562
+ break;
1563
+ case "z" /* Z */:
1564
+ obj.rotateOnAxis(new Vector33(0, 0, 1), toRadians(rotation));
1565
+ break;
1566
+ }
1567
+ }
1568
+ function addBox3WireframeToObject(box, obj, color = 16777215, w = 2) {
1569
+ let size = new Vector33();
1570
+ box.getSize(size);
1571
+ let boxGeo = new BoxGeometry2(size.x, size.y, size.z, 1, 1, 1);
1572
+ let wireGeo = new EdgesGeometry(boxGeo);
1573
+ let wireMat = new LineBasicMaterial({ color, linewidth: w });
1574
+ let wireframe = new LineSegments(wireGeo, wireMat);
1575
+ obj.add(wireframe);
1576
+ let axes = new AxesHelper(1);
1577
+ obj.add(axes);
1578
+ }
1579
+ function addWireframeToObject(obj, color = 16777215, w = 2) {
1580
+ let boxGeo = new BoxGeometry2(16, 16, 16, 1, 1, 1);
1581
+ let wireGeo = new EdgesGeometry(boxGeo);
1582
+ let wireMat = new LineBasicMaterial({ color, linewidth: w });
1583
+ let wireframe = new LineSegments(wireGeo, wireMat);
1584
+ obj.add(wireframe);
1585
+ let axes = new AxesHelper(1);
1586
+ obj.add(axes);
1587
+ }
1588
+ function addWireframeToMesh(geo, mesh, color = 16777215, w = 2) {
1589
+ let wireGeo = new EdgesGeometry(geo);
1590
+ let wireMat = new LineBasicMaterial({ color, linewidth: w });
1591
+ let wireframe = new LineSegments(wireGeo, wireMat);
1592
+ mesh.add(wireframe);
1593
+ let axes = new AxesHelper(1);
1594
+ mesh.add(axes);
1595
+ }
1596
+
1597
+ // src/util/performance.ts
1598
+ function captureException(e, ...extra) {
1599
+ console.error(e, extra);
1600
+ }
1601
+ function timingStart(marker) {
1602
+ console.time(marker);
1603
+ }
1604
+ function timingEnd(marker) {
1605
+ console.timeEnd(marker);
1606
+ }
1607
+
1608
+ // src/util/three.ts
1609
+ function isObject3D(obj) {
1610
+ return obj.isObject3D;
1611
+ }
1612
+ function isMesh(obj) {
1613
+ return obj.isMesh;
1614
+ }
1615
+ function isInstancedMesh(obj) {
1616
+ return obj.isInstancedMesh;
1617
+ }
1618
+ function isPerspectiveCamera(obj) {
1619
+ return obj.isPerspectiveCamera;
1620
+ }
1621
+ function isOrthographicCamera(obj) {
1622
+ return obj.isOrthographicCamera;
1623
+ }
1340
1624
 
1341
1625
  // src/nbt/NBTHelper.ts
1342
1626
  var NBTHelper = class {
@@ -1353,7 +1637,83 @@ var p3 = prefix("HostedAssetSource");
1353
1637
  var DEFAULT_OPTIONS = {
1354
1638
  retryDefaults: true
1355
1639
  };
1356
- var _HostedAssetSource = class _HostedAssetSource extends AssetSource {
1640
+ var HostedAssetSource = class _HostedAssetSource extends AssetSource {
1641
+ static {
1642
+ this.MODEL = {
1643
+ config(request) {
1644
+ },
1645
+ parse(response) {
1646
+ return response.data;
1647
+ }
1648
+ };
1649
+ }
1650
+ static {
1651
+ this.BLOCKSTATE = {
1652
+ config(request) {
1653
+ },
1654
+ parse(response) {
1655
+ return response.data;
1656
+ }
1657
+ };
1658
+ }
1659
+ static {
1660
+ this.META = {
1661
+ config(request) {
1662
+ },
1663
+ parse(response) {
1664
+ return response.data;
1665
+ }
1666
+ };
1667
+ }
1668
+ static {
1669
+ this.NBT = {
1670
+ config(request) {
1671
+ request.responseType = "arraybuffer";
1672
+ },
1673
+ parse(response) {
1674
+ return NBTHelper.fromBuffer(Buffer.from(response.data));
1675
+ }
1676
+ };
1677
+ }
1678
+ static {
1679
+ this.IMAGE = {
1680
+ config(request) {
1681
+ request.responseType = "arraybuffer";
1682
+ },
1683
+ async parse(response) {
1684
+ return await ImageLoader.processResponse(response);
1685
+ }
1686
+ };
1687
+ }
1688
+ static {
1689
+ this.LIST = {
1690
+ config(request) {
1691
+ },
1692
+ parse(response) {
1693
+ return response.data;
1694
+ }
1695
+ };
1696
+ }
1697
+ static {
1698
+ this.JSON = {
1699
+ config(request) {
1700
+ },
1701
+ parse(response) {
1702
+ return response.data;
1703
+ }
1704
+ };
1705
+ }
1706
+ static {
1707
+ this.PARSER_MAP = /* @__PURE__ */ new Map([
1708
+ ["model" /* MODEL */, _HostedAssetSource.MODEL],
1709
+ ["blockstate" /* BLOCKSTATE */, _HostedAssetSource.BLOCKSTATE],
1710
+ ["meta" /* META */, _HostedAssetSource.META],
1711
+ ["nbt" /* NBT */, _HostedAssetSource.NBT],
1712
+ ["image" /* IMAGE */, _HostedAssetSource.IMAGE],
1713
+ ["list" /* LIST */, _HostedAssetSource.LIST],
1714
+ ["json" /* JSON */, _HostedAssetSource.JSON]
1715
+ ]);
1716
+ }
1357
1717
  async loadOrRetryWithDefaults(key, parser) {
1358
1718
  const direct = await this.load(key, parser);
1359
1719
  if (typeof direct !== "undefined") {
@@ -1366,17 +1726,17 @@ var _HostedAssetSource = class _HostedAssetSource extends AssetSource {
1366
1726
  if (typeof namespaced !== "undefined") {
1367
1727
  return namespaced;
1368
1728
  }
1369
- if (typeof key.root !== "undefined" && key.root !== DEFAULT_ROOT || typeof this.root !== "undefined" && this.root !== DEFAULT_ROOT) {
1729
+ if (typeof key.root !== "undefined" && key.root !== AssetLoader.ROOT || typeof this.root !== "undefined" && this.root !== AssetLoader.ROOT) {
1370
1730
  console.info(p3, "Retrying", key, "with default root+namespace");
1371
- const namespacedRootedKey = new AssetKey(DEFAULT_NAMESPACE, key.path, key.assetType, key.type, key.rootType, key.extension, DEFAULT_ROOT);
1731
+ const namespacedRootedKey = new AssetKey(DEFAULT_NAMESPACE, key.path, key.assetType, key.type, key.rootType, key.extension, AssetLoader.ROOT);
1372
1732
  const namespacedRooted = await this.load(namespacedRootedKey, parser);
1373
1733
  if (typeof namespacedRooted !== "undefined") {
1374
1734
  return namespacedRooted;
1375
1735
  }
1376
1736
  }
1377
- } else if (typeof key.root !== "undefined" && key.root !== DEFAULT_ROOT || typeof this.root !== "undefined" && this.root !== DEFAULT_ROOT) {
1737
+ } else if (typeof key.root !== "undefined" && key.root !== AssetLoader.ROOT || typeof this.root !== "undefined" && this.root !== AssetLoader.ROOT) {
1378
1738
  console.info(p3, "Retrying", key, "with default root");
1379
- const rootKey = new AssetKey(key.namespace, key.path, key.assetType, key.type, key.rootType, key.extension, DEFAULT_ROOT);
1739
+ const rootKey = new AssetKey(key.namespace, key.path, key.assetType, key.type, key.rootType, key.extension, AssetLoader.ROOT);
1380
1740
  const rooted = await this.load(rootKey, parser);
1381
1741
  if (typeof rooted !== "undefined") {
1382
1742
  return rooted;
@@ -1388,31 +1748,25 @@ var _HostedAssetSource = class _HostedAssetSource extends AssetSource {
1388
1748
  console.info(p3, "Loading", key);
1389
1749
  const url = `${this.assetBasePath(key)}${key.type !== void 0 ? key.type + "/" : ""}${key.path}${key.extension}`;
1390
1750
  console.debug(p3, url);
1391
- let req = {
1392
- url
1393
- };
1394
- parser.config(req);
1395
- return await Requests.mcAssetRequest(req).then((response) => {
1396
- if (response && response.data) {
1397
- try {
1398
- return parser.parse(response);
1399
- } catch (e) {
1400
- console.warn("Parser failed to process response", response, e);
1401
- throw e;
1402
- }
1403
- }
1404
- return void 0;
1405
- }).catch((err) => {
1406
- if (err.response) {
1407
- let response = err.response;
1408
- if (response.status === 404) {
1409
- console.debug(p3, key, "not found");
1751
+ const request = { url };
1752
+ try {
1753
+ parser.config(request);
1754
+ let response;
1755
+ try {
1756
+ response = await Requests.mcAssetRequest(request);
1757
+ } catch (cause) {
1758
+ if (cause instanceof RequestError && cause.response?.status === 404) {
1410
1759
  return void 0;
1411
1760
  }
1761
+ throw cause;
1412
1762
  }
1413
- console.debug(p3, "Failed to load", key, err == null ? void 0 : err.message);
1414
- return void 0;
1415
- });
1763
+ if (response.data === void 0) {
1764
+ throw new Error("Asset response has no data");
1765
+ }
1766
+ return await parser.parse(response);
1767
+ } catch (cause) {
1768
+ throw new AssetLoadError(this, key, request.url, cause);
1769
+ }
1416
1770
  }
1417
1771
  assetBasePath(key) {
1418
1772
  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 + "/" : ""}`;
@@ -1425,6 +1779,9 @@ var _HostedAssetSource = class _HostedAssetSource extends AssetSource {
1425
1779
  get root() {
1426
1780
  return this._root;
1427
1781
  }
1782
+ get cacheId() {
1783
+ return `hosted:${this._root}`;
1784
+ }
1428
1785
  get options() {
1429
1786
  return this._options;
1430
1787
  }
@@ -1436,74 +1793,14 @@ var _HostedAssetSource = class _HostedAssetSource extends AssetSource {
1436
1793
  return this.load(key, responseParser);
1437
1794
  }
1438
1795
  };
1439
- _HostedAssetSource.MODEL = {
1440
- config(request) {
1441
- },
1442
- parse(response) {
1443
- return response.data;
1444
- }
1445
- };
1446
- _HostedAssetSource.BLOCKSTATE = {
1447
- config(request) {
1448
- },
1449
- parse(response) {
1450
- return response.data;
1451
- }
1452
- };
1453
- _HostedAssetSource.META = {
1454
- config(request) {
1455
- },
1456
- parse(response) {
1457
- return response.data;
1458
- }
1459
- };
1460
- _HostedAssetSource.NBT = {
1461
- config(request) {
1462
- request.responseType = "arraybuffer";
1463
- },
1464
- parse(response) {
1465
- return NBTHelper.fromBuffer(Buffer.from(response.data));
1466
- }
1467
- };
1468
- _HostedAssetSource.IMAGE = {
1469
- config(request) {
1470
- request.responseType = "arraybuffer";
1471
- },
1472
- async parse(response) {
1473
- return await ImageLoader.processResponse(response);
1474
- }
1475
- };
1476
- _HostedAssetSource.LIST = {
1477
- config(request) {
1478
- },
1479
- parse(response) {
1480
- return response.data;
1481
- }
1482
- };
1483
- _HostedAssetSource.JSON = {
1484
- config(request) {
1485
- },
1486
- parse(response) {
1487
- return response.data;
1488
- }
1489
- };
1490
- _HostedAssetSource.PARSER_MAP = /* @__PURE__ */ new Map([
1491
- ["model" /* MODEL */, _HostedAssetSource.MODEL],
1492
- ["blockstate" /* BLOCKSTATE */, _HostedAssetSource.BLOCKSTATE],
1493
- ["meta" /* META */, _HostedAssetSource.META],
1494
- ["nbt" /* NBT */, _HostedAssetSource.NBT],
1495
- ["image" /* IMAGE */, _HostedAssetSource.IMAGE],
1496
- ["list" /* LIST */, _HostedAssetSource.LIST],
1497
- ["json" /* JSON */, _HostedAssetSource.JSON]
1498
- ]);
1499
- var HostedAssetSource = _HostedAssetSource;
1500
1796
 
1501
1797
  // src/assets/source/archive/BrowserArchiveProxy.ts
1502
1798
  import { BlobReader, BlobWriter, ZipReader } from "@zip.js/zip.js";
1503
1799
  var BrowserArchiveProxy = class {
1504
- constructor(blob) {
1800
+ constructor(blob, id) {
1505
1801
  this._blob = blob;
1506
1802
  this._reader = new ZipReader(new BlobReader(this._blob));
1803
+ this.id = id ?? (typeof File !== "undefined" && blob instanceof File ? `${blob.name}:${blob.size}:${blob.lastModified}` : void 0);
1507
1804
  }
1508
1805
  async getEntries() {
1509
1806
  return (await this._reader.getEntries()).map((e) => {
@@ -1519,7 +1816,6 @@ var BrowserArchiveProxy = class {
1519
1816
  };
1520
1817
 
1521
1818
  // src/assets/source/archive/ArchiveAssetSource.ts
1522
- var p4 = prefix("ArchiveAssetSource");
1523
1819
  var ArchiveAssetSource = class _ArchiveAssetSource extends AssetSource {
1524
1820
  constructor(archiveProxy) {
1525
1821
  super();
@@ -1529,6 +1825,10 @@ var ArchiveAssetSource = class _ArchiveAssetSource extends AssetSource {
1529
1825
  static blob(blob) {
1530
1826
  return new _ArchiveAssetSource(new BrowserArchiveProxy(blob));
1531
1827
  }
1828
+ get cacheId() {
1829
+ const id = this._archiveProxy.id;
1830
+ return id === void 0 ? void 0 : `archive:${id}`;
1831
+ }
1532
1832
  async getEntries() {
1533
1833
  return this._archiveProxy.getEntries();
1534
1834
  }
@@ -1538,74 +1838,31 @@ var ArchiveAssetSource = class _ArchiveAssetSource extends AssetSource {
1538
1838
  }
1539
1839
  async get(key, parser) {
1540
1840
  const responseParser = HostedAssetSource.PARSER_MAP.get(parser);
1541
- console.log("ArchiveAssetSource.get", key, parser);
1542
- let entries = await this.getEntries();
1543
- const path = `${this.assetBasePath(key)}${key.type !== void 0 ? key.type + "/" : ""}${key.path}${key.extension}`;
1544
- console.log(path);
1545
- const entry = await this.getEntry(path);
1546
- console.log(entry);
1547
- if (!entry) {
1548
- return void 0;
1549
- }
1550
- let blob = await entry.getData();
1551
- const url = URL.createObjectURL(blob);
1552
- console.log(url);
1553
- let req = {
1554
- url
1555
- };
1556
- responseParser.config(req);
1557
- return Requests.genericRequest(req).then((response) => {
1558
- if (response && response.data) {
1559
- try {
1560
- return responseParser.parse(response);
1561
- } catch (e) {
1562
- console.warn("Parser failed to process response", response, e);
1563
- throw e;
1564
- }
1565
- }
1566
- return void 0;
1567
- }).catch((err) => {
1568
- if (err.response) {
1569
- let response = err.response;
1570
- if (response.status === 404) {
1571
- console.debug(p4, key, "not found");
1572
- return void 0;
1573
- }
1574
- }
1575
- console.debug(p4, "Failed to load", key, err == null ? void 0 : err.message);
1576
- return void 0;
1577
- });
1841
+ return this.load(key, responseParser);
1578
1842
  }
1579
- //TODO: abstract this
1580
1843
  async load(key, parser) {
1581
- console.info(p4, "Loading", key);
1582
- const url = `${this.assetBasePath(key)}${key.type !== void 0 ? key.type + "/" : ""}${key.path}${key.extension}`;
1583
- console.debug(p4, url);
1584
- let req = {
1585
- url
1586
- };
1587
- parser.config(req);
1588
- return await Requests.genericRequest(req).then((response) => {
1589
- if (response && response.data) {
1590
- try {
1591
- return parser.parse(response);
1592
- } catch (e) {
1593
- console.warn("Parser failed to process response", response, e);
1594
- throw e;
1595
- }
1844
+ const path = `${this.assetBasePath(key)}${key.type !== void 0 ? key.type + "/" : ""}${key.path}${key.extension}`;
1845
+ let url;
1846
+ try {
1847
+ const entry = await this.getEntry(path);
1848
+ if (!entry) {
1849
+ return void 0;
1596
1850
  }
1597
- return void 0;
1598
- }).catch((err) => {
1599
- if (err.response) {
1600
- let response = err.response;
1601
- if (response.status === 404) {
1602
- console.debug(p4, key, "not found");
1603
- return void 0;
1604
- }
1851
+ url = URL.createObjectURL(await entry.getData());
1852
+ const request = { url };
1853
+ parser.config(request);
1854
+ const response = await Requests.genericRequest(request);
1855
+ if (response.data === void 0) {
1856
+ throw new Error("Asset response has no data");
1605
1857
  }
1606
- console.debug(p4, "Failed to load", key, err == null ? void 0 : err.message);
1607
- return void 0;
1608
- });
1858
+ return await parser.parse(response);
1859
+ } catch (cause) {
1860
+ throw new AssetLoadError(this, key, path, cause);
1861
+ } finally {
1862
+ if (url !== void 0) {
1863
+ URL.revokeObjectURL(url);
1864
+ }
1865
+ }
1609
1866
  }
1610
1867
  assetBasePath(key) {
1611
1868
  return `${key.rootType !== void 0 ? key.rootType + "/" : ""}${key.namespace !== void 0 ? key.namespace + "/" : ""}${key.assetType !== void 0 ? key.assetType + "/" : ""}`;
@@ -1613,18 +1870,64 @@ var ArchiveAssetSource = class _ArchiveAssetSource extends AssetSource {
1613
1870
  };
1614
1871
 
1615
1872
  // src/assets/AssetLoader.ts
1616
- var import_ts_deepmerge3 = __toESM(require_dist());
1617
- var p5 = prefix("AssetLoader");
1618
- var _AssetLoader = class _AssetLoader {
1873
+ var p4 = prefix("AssetLoader");
1874
+ var FALLBACK_ROOT = "https://raw.githubusercontent.com/InventivetalentDev/minerender-fallback-assets/master";
1875
+ var AssetLoader = class {
1876
+ static {
1877
+ this.ROOT = DEFAULT_ROOT;
1878
+ }
1879
+ static {
1880
+ this._SOURCES = [];
1881
+ }
1882
+ static {
1883
+ /** Used when a registered source cannot identify its content, so nothing persists past this session. */
1884
+ this.SESSION_SCOPE = `session:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
1885
+ }
1886
+ static get version() {
1887
+ return this.ROOT.substring(this.ROOT.lastIndexOf("/") + 1);
1888
+ }
1889
+ static setVersion(version) {
1890
+ this.ROOT = `https://assets.mcasset.cloud/${version}`;
1891
+ const source = new HostedAssetSource(this.ROOT, { retryDefaults: false });
1892
+ const index = this._SOURCES.findIndex((s) => s.key === "mcassets");
1893
+ if (index !== -1) {
1894
+ this._SOURCES[index] = { key: "mcassets", source };
1895
+ } else {
1896
+ this.addSource("mcassets", source);
1897
+ }
1898
+ Caching.clear();
1899
+ }
1900
+ /**
1901
+ * Scope for persistent cache keys. Empty with only the default vanilla sources, so their
1902
+ * entries stay valid across sessions; otherwise it names every added source (resource packs,
1903
+ * mirrors) so their results never masquerade as vanilla assets after a reload.
1904
+ */
1905
+ static get persistentScope() {
1906
+ const parts = [];
1907
+ for (const { key, source } of this._SOURCES) {
1908
+ const id = source.cacheId;
1909
+ if (key === "mcassets" && id === `hosted:${this.ROOT}`) continue;
1910
+ if (key === "mcassets-fallback" && id === `hosted:${FALLBACK_ROOT}`) continue;
1911
+ if (id === void 0) return this.SESSION_SCOPE;
1912
+ parts.push(`${key}=${id}`);
1913
+ }
1914
+ return parts.join("|");
1915
+ }
1916
+ /** Prefixes a persistent cache key with the current source scope. */
1917
+ static persistentKey(key) {
1918
+ const scope = this.persistentScope;
1919
+ return scope ? `${scope}
1920
+ ${key}` : key;
1921
+ }
1619
1922
  static addSource(key, source, override = true) {
1620
1923
  if (override) {
1621
1924
  const existing = this.removeSource(key);
1622
1925
  if (existing) {
1623
- console.log(p5, "Removed existing AssetSource", key);
1926
+ console.log(p4, "Removed existing AssetSource", key);
1624
1927
  }
1625
1928
  }
1626
1929
  this._SOURCES.unshift({ key, source });
1627
- console.log(p5, "Added AssetSource", key);
1930
+ console.log(p4, "Added AssetSource", key);
1628
1931
  }
1629
1932
  static removeSource(key) {
1630
1933
  const index = this._SOURCES.findIndex((s) => s.key === key);
@@ -1636,8 +1939,11 @@ var _AssetLoader = class _AssetLoader {
1636
1939
  }
1637
1940
  return void 0;
1638
1941
  }
1942
+ static {
1943
+ this.addSource("mcassets-fallback", new HostedAssetSource(FALLBACK_ROOT, { retryDefaults: false }));
1944
+ this.addSource("mcassets", new HostedAssetSource(this.ROOT, { retryDefaults: false }));
1945
+ }
1639
1946
  static async getAll(key, parser) {
1640
- console.log(this._SOURCES);
1641
1947
  let promises = [];
1642
1948
  for (const source of this._SOURCES) {
1643
1949
  promises.push(source.source.get(key, parser));
@@ -1646,31 +1952,36 @@ var _AssetLoader = class _AssetLoader {
1646
1952
  return results.filter((r) => r != void 0);
1647
1953
  });
1648
1954
  }
1955
+ /** Returns the first defined result in source-priority order, without merging assets. */
1649
1956
  static async get(key, parser) {
1650
- console.log(this._SOURCES);
1651
- let promises = [];
1652
- for (const source of this._SOURCES) {
1653
- promises.push(source.source.get(key, parser));
1654
- }
1655
- return Promise.all(promises).then((results) => {
1656
- const fallback = results[results.length - 1];
1657
- for (const result of results) {
1658
- if (result) {
1659
- if (fallback) {
1660
- return (0, import_ts_deepmerge3.default)({}, fallback, result);
1661
- }
1662
- return result;
1957
+ const keys = [key];
1958
+ if (key.rootType === "data" && (key.assetType === "structure" || key.assetType === "structures")) {
1959
+ keys.push(new AssetKey(
1960
+ key.namespace,
1961
+ key.path,
1962
+ key.assetType === "structure" ? "structures" : "structure",
1963
+ key.type,
1964
+ key.rootType,
1965
+ key.extension,
1966
+ key.root
1967
+ ));
1968
+ }
1969
+ return (await this.getFirst(keys, parser))?.asset;
1970
+ }
1971
+ /** Tries each path within a source before considering lower-priority sources. */
1972
+ static async getFirst(keys, parser) {
1973
+ const sources = [...this._SOURCES];
1974
+ for (const source of sources) {
1975
+ for (const key of keys) {
1976
+ const result = await source.source.get(key, parser);
1977
+ if (result !== void 0) {
1978
+ return { key, asset: result };
1663
1979
  }
1664
1980
  }
1665
- return void 0;
1666
- });
1981
+ }
1982
+ return void 0;
1667
1983
  }
1668
1984
  };
1669
- _AssetLoader.ROOT = DEFAULT_ROOT;
1670
- _AssetLoader._SOURCES = [];
1671
- _AssetLoader.addSource("mcassets-fallback", new HostedAssetSource("https://raw.githubusercontent.com/InventivetalentDev/minerender-fallback-assets/master", { retryDefaults: false }));
1672
- _AssetLoader.addSource("mcassets", new HostedAssetSource(DEFAULT_ROOT, { retryDefaults: false }));
1673
- var AssetLoader = _AssetLoader;
1674
1985
 
1675
1986
  // src/assets/ModelTextures.ts
1676
1987
  var ModelTextures = class {
@@ -1678,11 +1989,21 @@ var ModelTextures = class {
1678
1989
  return this._persistentMetaCache ??= PersistentCache.open("minerender-texturemeta");
1679
1990
  }
1680
1991
  static async get(key) {
1681
- const asset = await this.preload(key);
1682
- if (asset) {
1683
- return ImageLoader.infoToCanvasData(asset);
1992
+ const keyStr = key.serialize();
1993
+ const pending = this.preload(key);
1994
+ const cached = Caching.textureAssetCache.getIfPresent(keyStr);
1995
+ try {
1996
+ const asset = await pending;
1997
+ if (asset) {
1998
+ return await ImageLoader.infoToCanvasData(asset);
1999
+ }
2000
+ return void 0;
2001
+ } catch (err) {
2002
+ if (cached && Caching.textureAssetCache.getIfPresent(keyStr) === cached) {
2003
+ Caching.textureAssetCache.invalidate(keyStr);
2004
+ }
2005
+ throw err;
1684
2006
  }
1685
- return void 0;
1686
2007
  }
1687
2008
  static async preload(key) {
1688
2009
  const keyStr = key.serialize();
@@ -1701,7 +2022,7 @@ var ModelTextures = class {
1701
2022
  }
1702
2023
  const keyStr = key.serialize();
1703
2024
  return Caching.textureMetaCache.get(keyStr, (k) => {
1704
- return this.PERSISTENT_META_CACHE.getOrLoad(keyStr, (k1) => {
2025
+ return this.PERSISTENT_META_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
1705
2026
  return AssetLoader.get(key, "meta" /* META */).then((asset) => {
1706
2027
  if (asset)
1707
2028
  asset.key = key;
@@ -1756,7 +2077,28 @@ var CanvasImage = class {
1756
2077
  };
1757
2078
 
1758
2079
  // src/Ticker.ts
1759
- var _Ticker = class _Ticker {
2080
+ var Ticker = class _Ticker {
2081
+ static {
2082
+ this.TPS = 20;
2083
+ }
2084
+ static {
2085
+ this.counter = 0;
2086
+ }
2087
+ static {
2088
+ this._tickers = /* @__PURE__ */ new Map();
2089
+ }
2090
+ static {
2091
+ this.oneSecondTps = 0;
2092
+ }
2093
+ static {
2094
+ this.oneSecondTicks = 0;
2095
+ }
2096
+ static {
2097
+ this.fiveSecondTps = 0;
2098
+ }
2099
+ static {
2100
+ this.fiveSecondTicks = 0;
2101
+ }
1760
2102
  static schedule(fn, ms) {
1761
2103
  const handle = setInterval(fn, ms);
1762
2104
  const unref = handle.unref;
@@ -1818,14 +2160,6 @@ var _Ticker = class _Ticker {
1818
2160
  this._tickers.clear();
1819
2161
  }
1820
2162
  };
1821
- _Ticker.TPS = 20;
1822
- _Ticker.counter = 0;
1823
- _Ticker._tickers = /* @__PURE__ */ new Map();
1824
- _Ticker.oneSecondTps = 0;
1825
- _Ticker.oneSecondTicks = 0;
1826
- _Ticker.fiveSecondTps = 0;
1827
- _Ticker.fiveSecondTicks = 0;
1828
- var Ticker = _Ticker;
1829
2163
 
1830
2164
  // src/texture/TextureAtlas.ts
1831
2165
  var TextureAtlas = class {
@@ -1851,18 +2185,24 @@ var TextureAtlas = class {
1851
2185
 
1852
2186
  // src/model/ModelGenerator.ts
1853
2187
  var ModelGenerator = class {
2188
+ static {
2189
+ this.ITEM_LAYERS = ["layer0", "layer1", "layer2", "layer3", "layer4"];
2190
+ }
1854
2191
  static generateItemModel(imageData, layer) {
2192
+ const tintindex = this.ITEM_LAYERS.indexOf(layer);
1855
2193
  const mainElement = {
1856
2194
  from: [0, 0, 7.5],
1857
2195
  to: [16, 16, 8.5],
1858
2196
  faces: {
1859
2197
  "south": {
1860
2198
  uv: [0, 0, 16, 16],
1861
- texture: `#${layer}`
2199
+ texture: `#${layer}`,
2200
+ tintindex
1862
2201
  },
1863
2202
  "north": {
1864
2203
  uv: [16, 0, 0, 16],
1865
- texture: `#${layer}`
2204
+ texture: `#${layer}`,
2205
+ tintindex
1866
2206
  }
1867
2207
  },
1868
2208
  shade: true
@@ -1870,14 +2210,21 @@ var ModelGenerator = class {
1870
2210
  return [mainElement];
1871
2211
  }
1872
2212
  };
1873
- ModelGenerator.ITEM_LAYERS = ["layer0", "layer1", "layer2", "layer3", "layer4"];
1874
2213
 
1875
2214
  // src/UVMapper.ts
1876
- var p6 = prefix("UVMapper");
2215
+ var p5 = prefix("UVMapper");
1877
2216
  var DEFAULT_UV = [0, 0, 16, 16];
1878
2217
  var X = 0;
1879
2218
  var Y = 1;
1880
2219
  var Z = 2;
2220
+ var FACE_UV_AXES = {
2221
+ east: [new Vector34(0, 0, -1), new Vector34(0, -1, 0)],
2222
+ west: [new Vector34(0, 0, 1), new Vector34(0, -1, 0)],
2223
+ up: [new Vector34(1, 0, 0), new Vector34(0, 0, 1)],
2224
+ down: [new Vector34(1, 0, 0), new Vector34(0, 0, -1)],
2225
+ south: [new Vector34(1, 0, 0), new Vector34(0, -1, 0)],
2226
+ north: [new Vector34(-1, 0, 0), new Vector34(0, -1, 0)]
2227
+ };
1881
2228
  var UVMapper = class {
1882
2229
  static makeUv(uvCoord, originalTextureSize, actualTextureSize) {
1883
2230
  if (uvCoord === 0) return 0;
@@ -1975,6 +2322,42 @@ var UVMapper = class {
1975
2322
  static facesToUv(faces, originalTextureSize, actualTextureSize) {
1976
2323
  return new Float32BufferAttribute2(this.facesToUvArray(faces, originalTextureSize, actualTextureSize), 2);
1977
2324
  }
2325
+ static lockUvs(geometry, faces, atlas, rotation) {
2326
+ const uv = geometry.getAttribute("uv");
2327
+ for (let faceIndex = 0; faceIndex < CUBE_FACES.length; faceIndex++) {
2328
+ const faceName = CUBE_FACES[faceIndex];
2329
+ const texture = faces[faceName]?.texture?.substring(1);
2330
+ const position = texture && atlas.positions[texture];
2331
+ const size = texture && atlas.sizes[texture];
2332
+ if (!position || !size) continue;
2333
+ const [sourceU, sourceV] = FACE_UV_AXES[faceName];
2334
+ const uAxis = sourceU.clone().applyEuler(rotation);
2335
+ const vAxis = sourceV.clone().applyEuler(rotation);
2336
+ const normal = vAxis.clone().cross(uAxis);
2337
+ const targetFace = CUBE_FACES.find((name) => {
2338
+ const [u, v] = FACE_UV_AXES[name];
2339
+ return v.clone().cross(u).dot(normal) > 0.5;
2340
+ });
2341
+ const [targetU, targetV] = FACE_UV_AXES[targetFace];
2342
+ const uu = Math.round(targetU.dot(uAxis));
2343
+ const uvCross = Math.round(targetU.dot(vAxis));
2344
+ const vu = Math.round(targetV.dot(uAxis));
2345
+ const vv = Math.round(targetV.dot(vAxis));
2346
+ for (let corner = 0; corner < 4; corner++) {
2347
+ const index = faceIndex * 4 + corner;
2348
+ const u = (uv.getX(index) * atlas.image.width - position[0]) / size[0] - 0.5;
2349
+ const v = ((1 - uv.getY(index)) * atlas.image.height - position[1]) / size[1] - 0.5;
2350
+ const lockedU = uu * u + uvCross * v + 0.5;
2351
+ const lockedV = vu * u + vv * v + 0.5;
2352
+ uv.setXY(
2353
+ index,
2354
+ (position[0] + lockedU * size[0]) / atlas.image.width,
2355
+ 1 - (position[1] + lockedV * size[1]) / atlas.image.height
2356
+ );
2357
+ }
2358
+ }
2359
+ uv.needsUpdate = true;
2360
+ }
1978
2361
  static async getAtlas(model) {
1979
2362
  const keyStr = model.key.serialize();
1980
2363
  return Caching.modelTextureAtlasCache.get(keyStr, (k) => {
@@ -2046,12 +2429,11 @@ var UVMapper = class {
2046
2429
  return [tl, tr, bl, br];
2047
2430
  }
2048
2431
  static async createAtlas(originalModel) {
2049
- var _a;
2050
2432
  const textureMap = {};
2051
2433
  const metaMap = {};
2052
2434
  const model = { ...originalModel };
2053
2435
  const isItemModel = !("elements" in model);
2054
- console.debug(p6, "Creating Atlas for", model.key);
2436
+ console.debug(p5, "Creating Atlas for", model.key);
2055
2437
  if (model.textures) {
2056
2438
  const promises = [];
2057
2439
  const uniqueTextureNames = [];
@@ -2065,7 +2447,7 @@ var UVMapper = class {
2065
2447
  const assetKey = AssetKey.parse("textures", textureValue, model.key);
2066
2448
  promises.push(ModelTextures.get(assetKey).then((asset) => {
2067
2449
  if (!asset) {
2068
- console.warn(p6, "Missing texture", assetKey);
2450
+ console.warn(p5, "Missing texture", assetKey);
2069
2451
  return;
2070
2452
  }
2071
2453
  textureMap[textureKey] = new WrappedImage(asset);
@@ -2077,7 +2459,7 @@ var UVMapper = class {
2077
2459
  }
2078
2460
  await Promise.all(promises);
2079
2461
  const textureCount = uniqueTextureNames.length;
2080
- console.debug(p6, "Creating Atlas for", textureCount, "textures");
2462
+ console.debug(p5, "Creating Atlas for", textureCount, "textures");
2081
2463
  this.fillMissingTextureKeys(model.textures, textureMap);
2082
2464
  this.fillMissingTextureKeys(model.textures, metaMap);
2083
2465
  const sizes = {};
@@ -2097,7 +2479,7 @@ var UVMapper = class {
2097
2479
  this.fillMissingTextureKeys(model.textures, sizes);
2098
2480
  const s = Math.ceil(Math.sqrt(textureCount + 1));
2099
2481
  const size = Math.ceil(s * maxWidth);
2100
- console.debug(p6, "Atlas size:", size);
2482
+ console.debug(p5, "Atlas size:", size);
2101
2483
  const squaredSize = size * size;
2102
2484
  const missing = await ImageLoader.getData("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABAAQMAAACQp+OdAAAABlBMVEX/AP8AAACfphTyAAAAFUlEQVQoz2MIhQKGVVAwKjIqQrwIAHRz/wFI17TEAAAAAElFTkSuQmCC");
2103
2485
  const empty = await ImageLoader.getData("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABAAQMAAACQp+OdAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAA5JREFUKM9jGAWjgAIAAAJAAAFSSwuTAAAAAElFTkSuQmCC");
@@ -2126,16 +2508,15 @@ var UVMapper = class {
2126
2508
  if (texture.animated) {
2127
2509
  hasAnimation = true;
2128
2510
  const meta = metaMap[textureKey];
2129
- const frameTime = ((_a = meta == null ? void 0 : meta.animation) == null ? void 0 : _a.frametime) ?? 1;
2511
+ const frameTime = meta?.animation?.frametime ?? 1;
2130
2512
  console.log(meta);
2131
2513
  console.log(frameTime);
2132
2514
  let t = 0;
2133
2515
  let f = 0;
2134
2516
  animatorFunctions[textureKey] = () => {
2135
- var _a2;
2136
2517
  if (t++ >= frameTime) {
2137
2518
  t = 0;
2138
- const frames = (_a2 = meta == null ? void 0 : meta.animation) == null ? void 0 : _a2.frames;
2519
+ const frames = meta?.animation?.frames;
2139
2520
  const frame = frames ? frames[f] : f;
2140
2521
  image.putData(texture.getFrameSectionData(frame), x, y, 0, 0, maxWidth, maxWidth);
2141
2522
  f++;
@@ -2153,10 +2534,10 @@ var UVMapper = class {
2153
2534
  }
2154
2535
  }
2155
2536
  const atlasImageData = image.toDataURL();
2156
- console.log(p6, "Atlas Image for", model.key, atlasImageData);
2537
+ console.log(p5, "Atlas Image for", model.key, atlasImageData);
2157
2538
  this.fillMissingTextureKeys(model.textures, positions);
2158
2539
  if (isItemModel) {
2159
- console.debug(p6, "Generating item model for", model);
2540
+ console.debug(p5, "Generating item model for", model);
2160
2541
  model.elements = [];
2161
2542
  for (let layerName of ModelGenerator.ITEM_LAYERS) {
2162
2543
  const textureImage = textureMap[layerName];
@@ -2164,7 +2545,7 @@ var UVMapper = class {
2164
2545
  model.elements.push(...ModelGenerator.generateItemModel(textureImage.data, layerName));
2165
2546
  }
2166
2547
  }
2167
- console.debug(p6, "Item model elements", model.elements);
2548
+ console.debug(p5, "Item model elements", model.elements);
2168
2549
  }
2169
2550
  if (!model.elements) {
2170
2551
  }
@@ -2182,17 +2563,13 @@ var UVMapper = class {
2182
2563
  this.setCubeFaceUvInArray(uv, faceIndex * 4, [0, 1, 8 / size, (size - 8) / size]);
2183
2564
  continue;
2184
2565
  }
2566
+ if (!face.texture) continue;
2185
2567
  if (!face.uv) {
2186
2568
  face.uv = this.getFallbackUv(element, faceName);
2187
2569
  }
2188
- let faceTexture = face.texture;
2189
- let texPosition;
2190
- let texSize;
2191
- if (faceTexture) {
2192
- faceTexture = faceTexture.substr(1);
2193
- texPosition = positions[faceTexture] || [0, 0];
2194
- texSize = sizes[faceTexture] || [16, 16];
2195
- }
2570
+ const faceTexture = face.texture.substr(1);
2571
+ const texPosition = positions[faceTexture] || [0, 0];
2572
+ const texSize = sizes[faceTexture] || [16, 16];
2196
2573
  let texPosV = new Vector2(texPosition[0], texPosition[1]);
2197
2574
  const [tl, tr, bl, br] = this.expandAndRotateMcUvs(face.uv, face.rotation, texSize[0], texSize[1]);
2198
2575
  tl.add(texPosV);
@@ -2224,7 +2601,7 @@ var UVMapper = class {
2224
2601
  hasTransparency
2225
2602
  );
2226
2603
  } else {
2227
- console.warn(p6, "Model does not have any textures", model);
2604
+ console.warn(p5, "Model does not have any textures", model);
2228
2605
  }
2229
2606
  return void 0;
2230
2607
  }
@@ -2266,18 +2643,18 @@ var MinecraftCubeTexture = class {
2266
2643
  }
2267
2644
  getUvEast(w, h, l) {
2268
2645
  return [
2269
- [this.mapW(this.u), this.mapH(this.v + l + h)],
2270
- [this.mapW(this.u + l), this.mapH(this.v + l + h)],
2271
- [this.mapW(this.u), this.mapH(this.v + l)],
2272
- [this.mapW(this.u + l), this.mapH(this.v + l)]
2646
+ [this.mapW(this.u + l + w + l), this.mapH(this.v + l + h)],
2647
+ [this.mapW(this.u + l + w), this.mapH(this.v + l + h)],
2648
+ [this.mapW(this.u + l + w + l), this.mapH(this.v + l)],
2649
+ [this.mapW(this.u + l + w), this.mapH(this.v + l)]
2273
2650
  ];
2274
2651
  }
2275
2652
  getUvWest(w, h, l) {
2276
2653
  return [
2277
- [this.mapW(this.u + l + w), this.mapH(this.v + l + h)],
2278
- [this.mapW(this.u + l + w + l), this.mapH(this.v + l + h)],
2279
- [this.mapW(this.u + l + w), this.mapH(this.v + l)],
2280
- [this.mapW(this.u + l + w + l), this.mapH(this.v + l)]
2654
+ [this.mapW(this.u + l), this.mapH(this.v + l + h)],
2655
+ [this.mapW(this.u), this.mapH(this.v + l + h)],
2656
+ [this.mapW(this.u + l), this.mapH(this.v + l)],
2657
+ [this.mapW(this.u), this.mapH(this.v + l)]
2281
2658
  ];
2282
2659
  }
2283
2660
  getUvUp(w, h, l) {
@@ -2290,26 +2667,26 @@ var MinecraftCubeTexture = class {
2290
2667
  }
2291
2668
  getUvDown(w, h, l) {
2292
2669
  return [
2293
- [this.mapW(this.u + l), this.mapH(this.v + l)],
2294
- [this.mapW(this.u + l + w), this.mapH(this.v + l)],
2295
2670
  [this.mapW(this.u + l), this.mapH(this.v)],
2296
- [this.mapW(this.u + l + w), this.mapH(this.v)]
2671
+ [this.mapW(this.u + l + w), this.mapH(this.v)],
2672
+ [this.mapW(this.u + l), this.mapH(this.v + l)],
2673
+ [this.mapW(this.u + l + w), this.mapH(this.v + l)]
2297
2674
  ];
2298
2675
  }
2299
2676
  getUvSouth(w, h, l) {
2300
2677
  return [
2301
- [this.mapW(this.u + l + w + l), this.mapH(this.v + l + h)],
2302
2678
  [this.mapW(this.u + l + w + l + w), this.mapH(this.v + l + h)],
2303
- [this.mapW(this.u + l + w + l), this.mapH(this.v + l)],
2304
- [this.mapW(this.u + l + w + l + w), this.mapH(this.v + l)]
2679
+ [this.mapW(this.u + l + w + l), this.mapH(this.v + l + h)],
2680
+ [this.mapW(this.u + l + w + l + w), this.mapH(this.v + l)],
2681
+ [this.mapW(this.u + l + w + l), this.mapH(this.v + l)]
2305
2682
  ];
2306
2683
  }
2307
2684
  getUvNorth(w, h, l) {
2308
2685
  return [
2309
- [this.mapW(this.u + l), this.mapH(this.v + l + h)],
2310
2686
  [this.mapW(this.u + l + w), this.mapH(this.v + l + h)],
2311
- [this.mapW(this.u + l), this.mapH(this.v + l)],
2312
- [this.mapW(this.u + l + w), this.mapH(this.v + l)]
2687
+ [this.mapW(this.u + l), this.mapH(this.v + l + h)],
2688
+ [this.mapW(this.u + l + w), this.mapH(this.v + l)],
2689
+ [this.mapW(this.u + l), this.mapH(this.v + l)]
2313
2690
  ];
2314
2691
  }
2315
2692
  getFaceUv(face, w, h, l) {
@@ -2328,10 +2705,12 @@ var MinecraftCubeTexture = class {
2328
2705
  return this.getUvNorth(w, h, l);
2329
2706
  }
2330
2707
  }
2331
- toUvArray(w, h, l) {
2708
+ toUvArray(w, h, l, mirror = false) {
2332
2709
  const uv = [];
2333
2710
  for (let i = 0; i < CUBE_FACES.length; i++) {
2334
- UVMapper.setFaceUvInArray(uv, i * 4, this.getFaceUv(CUBE_FACES[i], w, h, l));
2711
+ const face = mirror && i < 2 ? CUBE_FACES[1 - i] : CUBE_FACES[i];
2712
+ const corners = this.getFaceUv(face, w, h, l);
2713
+ UVMapper.setFaceUvInArray(uv, i * 4, mirror ? [corners[1], corners[0], corners[3], corners[2]] : corners);
2335
2714
  }
2336
2715
  return uv;
2337
2716
  }
@@ -2361,76 +2740,85 @@ function shutdown() {
2361
2740
  Caching.end();
2362
2741
  }
2363
2742
 
2743
+ // src/error/MineRenderError.ts
2744
+ var MineRenderError = class _MineRenderError extends Error {
2745
+ constructor(msg) {
2746
+ super(msg);
2747
+ Object.setPrototypeOf(this, _MineRenderError.prototype);
2748
+ }
2749
+ get name() {
2750
+ return "MineRenderError";
2751
+ }
2752
+ };
2753
+
2364
2754
  // src/assets/Entities.ts
2365
- import { Memoize as Memoize3 } from "typescript-memoize";
2366
2755
  var Entities = class {
2367
- static async getBlockEntityModels() {
2368
- const key = AssetKey.parse("models", "minerender:blockEntityModels");
2369
- console.log(key);
2370
- return AssetLoader.get(key, "json" /* JSON */);
2371
- }
2372
- static async getEntityModels() {
2373
- const key = AssetKey.parse("models", "minerender:entityModels");
2374
- console.log(key);
2375
- return AssetLoader.get(key, "json" /* JSON */);
2376
- }
2377
- // BlockEntity names are hardcoded
2378
- static async getBlockList() {
2379
- const models = await this.getBlockEntityModels();
2380
- if (!models) {
2381
- return [];
2382
- }
2383
- return Object.keys(models);
2384
- }
2385
2756
  static async getEntityList() {
2386
- const models = await this.getEntityModels();
2387
- if (!models) {
2388
- return [];
2389
- }
2390
- return Object.keys(models);
2391
- }
2392
- static async getBlock(modelKey, textureKey) {
2393
- const models = await this.getBlockEntityModels();
2394
- if (!models) {
2395
- return void 0;
2396
- }
2397
- if (!textureKey) {
2398
- textureKey = modelKey;
2399
- }
2400
- const baseKey = modelKey.path.includes("/") ? new BasicAssetKey(modelKey.namespace, modelKey.path.split("/")[0]) : modelKey;
2401
- return {
2402
- key: textureKey,
2403
- parts: models[baseKey.toNamespacedString()]
2757
+ const collect = async (path) => {
2758
+ const prefix2 = path ? `${path}/` : "";
2759
+ const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json");
2760
+ const list = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
2761
+ if (!list) return [];
2762
+ const files = list.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
2763
+ const children = await Promise.all(list.directories.map((directory) => collect(`${prefix2}${directory}`)));
2764
+ return [...files, ...children.flat()];
2404
2765
  };
2766
+ return collect("");
2767
+ }
2768
+ static async getEntity(modelKey, textureKey, options) {
2769
+ const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
2770
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json");
2771
+ const model = await Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
2772
+ if (!model) return void 0;
2773
+ const layerName = options?.layer ?? "main";
2774
+ const layer = model.layers[layerName];
2775
+ if (!layer) {
2776
+ throw new MineRenderError(`Entity ${model.id} has no layer "${layerName}". Available layers: ${Object.keys(model.layers).join(", ")}`);
2777
+ }
2778
+ let texture;
2779
+ if (textureKey) {
2780
+ const path2 = isAssetKey(textureKey) ? textureKey.getFullPath() : textureKey.path;
2781
+ 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);
2782
+ } else if (layer.textureLocation !== void 0) {
2783
+ texture = AssetKey.parse("textures", layer.textureLocation.replace(/^([^:]+:)?textures\//, "$1"));
2784
+ } else {
2785
+ texture = await this.resolveTexture(modelKey);
2786
+ }
2787
+ return { key: textureKey ?? modelKey, texture, layer, id: model.id };
2788
+ }
2789
+ static async resolveTexture(modelKey) {
2790
+ const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
2791
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json");
2792
+ return Caching.entityTextureCache.get(key.serialize(), async () => {
2793
+ const name = path.split("/").pop();
2794
+ for (const candidate of [path, `${path}/${name}`]) {
2795
+ const texture = new AssetKey(modelKey.namespace, candidate, "textures", "entity", "assets", ".png");
2796
+ if (await ModelTextures.get(texture)) return texture;
2797
+ }
2798
+ const listKey = new AssetKey(modelKey.namespace, "_list", `${name}_variant`, void 0, "data", ".json");
2799
+ const list = await AssetLoader.get(listKey, "list" /* LIST */);
2800
+ const files = list?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
2801
+ const file = files?.find((file2) => file2 === "temperate.json") ?? files?.[0];
2802
+ if (!file) return void 0;
2803
+ const variantKey = new AssetKey(modelKey.namespace, file.slice(0, -5), `${name}_variant`, void 0, "data", ".json");
2804
+ const variant = await AssetLoader.get(variantKey, "json" /* JSON */);
2805
+ const assets = variant?.assets;
2806
+ const textureId = variant?.asset_id ?? (typeof assets?.wild === "string" ? assets.wild : Object.values(assets ?? {}).find((value) => typeof value === "string"));
2807
+ return typeof textureId === "string" ? AssetKey.parse("textures", textureId) : void 0;
2808
+ });
2405
2809
  }
2406
- static async getEntity(modelKey, textureKey) {
2407
- const models = await this.getEntityModels();
2408
- if (!models) {
2409
- return void 0;
2410
- }
2411
- if (!textureKey) {
2412
- textureKey = modelKey;
2413
- }
2414
- const baseKey = modelKey.path.includes("/") ? new BasicAssetKey(modelKey.namespace, modelKey.path.split("/")[0]) : modelKey;
2415
- return {
2416
- key: textureKey,
2417
- parts: models[baseKey.toNamespacedString()]
2418
- };
2810
+ /** @deprecated Use getEntityList(); block entities share the entity dataset. */
2811
+ static getBlockList() {
2812
+ return this.getEntityList();
2813
+ }
2814
+ /** @deprecated Use getEntity(); block entities share the entity dataset. */
2815
+ static getBlock(modelKey, textureKey, options) {
2816
+ return this.getEntity(modelKey, textureKey, options);
2419
2817
  }
2420
- };
2421
- __decorateClass([
2422
- Memoize3()
2423
- ], Entities, "getBlockEntityModels", 1);
2424
- __decorateClass([
2425
- Memoize3()
2426
- ], Entities, "getEntityModels", 1);
2427
- var EntityModels = class {
2428
- };
2429
- var BlockEntityModels = class {
2430
2818
  };
2431
2819
 
2432
2820
  // src/renderer/SceneObject.ts
2433
- import { Euler, InstancedMesh, MathUtils, Matrix4 as Matrix42, Mesh as Mesh3, Object3D as Object3D2, Quaternion as Quaternion2, Vector3 as Vector33 } from "three";
2821
+ import { Euler as Euler3, InstancedMesh, MathUtils, Matrix4 as Matrix43, Mesh as Mesh3, Object3D as Object3D2, Quaternion as Quaternion3, Vector3 as Vector35 } from "three";
2434
2822
 
2435
2823
  // src/instance/InstanceReference.ts
2436
2824
  var InstanceReference = class {
@@ -2475,30 +2863,27 @@ function isInstanceReference(obj) {
2475
2863
  return obj.isInstanceReference;
2476
2864
  }
2477
2865
 
2478
- // src/error/MineRenderError.ts
2479
- var MineRenderError = class _MineRenderError extends Error {
2480
- constructor(msg) {
2481
- super(msg);
2482
- Object.setPrototypeOf(this, _MineRenderError.prototype);
2483
- }
2484
- get name() {
2485
- return "MineRenderError";
2486
- }
2487
- };
2488
-
2489
2866
  // src/renderer/SceneObject.ts
2490
- var import_ts_deepmerge4 = __toESM(require_dist());
2491
- var p7 = prefix("SceneObject");
2492
- var _SceneObject = class _SceneObject extends Object3D2 {
2867
+ var import_ts_deepmerge3 = __toESM(require_dist());
2868
+ var p6 = prefix("SceneObject");
2869
+ var SceneObject = class _SceneObject extends Object3D2 {
2493
2870
  constructor(options) {
2494
2871
  super();
2495
2872
  this.isSceneObject = true;
2496
2873
  this.materialCallbacks = {};
2497
2874
  this._isInstanced = false;
2498
2875
  this._instanceCounter = 0;
2499
- this.options = (0, import_ts_deepmerge4.default)({}, _SceneObject.DEFAULT_OPTIONS, options ?? {});
2876
+ this.options = (0, import_ts_deepmerge3.default)({}, _SceneObject.DEFAULT_OPTIONS, options ?? {});
2500
2877
  console.log("SceneObject options", this.options);
2501
2878
  }
2879
+ static {
2880
+ this.DEFAULT_OPTIONS = (0, import_ts_deepmerge3.default)({}, {
2881
+ instanceMeshes: true,
2882
+ maxInstanceCount: 50,
2883
+ mergeMeshes: true,
2884
+ wireframe: false
2885
+ });
2886
+ }
2502
2887
  set scene(scene) {
2503
2888
  if (!!this._scene) throw new MineRenderError("Scene already set");
2504
2889
  this._scene = scene;
@@ -2590,6 +2975,7 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2590
2975
  }
2591
2976
  createInstancedMesh(name, geometry, material, count) {
2592
2977
  const mesh = new InstancedMesh(geometry, material, count);
2978
+ mesh.count = 0;
2593
2979
  if (name) {
2594
2980
  mesh.name = `mesh:${name}`;
2595
2981
  }
@@ -2610,15 +2996,8 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2610
2996
  return this.toggleObjectVisibility(this.getMeshByName(name), visible);
2611
2997
  }
2612
2998
  iterateAllMeshes(cb) {
2613
- this.children.forEach((obj) => {
2614
- if (obj.isMesh) {
2615
- cb(obj);
2616
- }
2617
- obj.children.forEach((obj1) => {
2618
- if (obj1.isMesh) {
2619
- cb(obj1);
2620
- }
2621
- });
2999
+ this.traverse((obj) => {
3000
+ if (isMesh(obj)) cb(obj);
2622
3001
  });
2623
3002
  }
2624
3003
  //</editor-fold>
@@ -2666,10 +3045,14 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2666
3045
  nextInstance() {
2667
3046
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2668
3047
  const i = this._instanceCounter++;
2669
- this.setMatrixAt(i, new Matrix42());
2670
- console.debug(p7, "nextInstance " + i);
3048
+ this.setMatrixAt(i, new Matrix43());
3049
+ const mesh = this.children[0];
3050
+ if (mesh && isInstancedMesh(mesh)) {
3051
+ mesh.count = Math.min(this._instanceCounter, mesh.instanceMatrix.count);
3052
+ }
3053
+ console.debug(p6, "nextInstance " + i);
2671
3054
  if (i === this.options.maxInstanceCount) {
2672
- console.warn(p7, "Max instance count reached for " + this);
3055
+ console.warn(p6, "Max instance count reached for " + this);
2673
3056
  }
2674
3057
  if (this.scene) {
2675
3058
  this.scene.stats.instanceCount++;
@@ -2679,7 +3062,7 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2679
3062
  }
2680
3063
  //</editor-fold>
2681
3064
  //<editor-fold desc="TRANSFORMATION">
2682
- getMatrixAt(index, matrix = new Matrix42()) {
3065
+ getMatrixAt(index, matrix = new Matrix43()) {
2683
3066
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2684
3067
  const child = this.children[0];
2685
3068
  if (child && isInstancedMesh(child)) {
@@ -2698,16 +3081,16 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2698
3081
  }
2699
3082
  setPositionRotationScaleAt(index, position, rotation, scale) {
2700
3083
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2701
- const oldPosition = new Vector33();
2702
- const oldRotation = new Quaternion2();
2703
- const oldScale = new Vector33();
2704
- const matrix = new Matrix42();
3084
+ const oldPosition = new Vector35();
3085
+ const oldRotation = new Quaternion3();
3086
+ const oldScale = new Vector35();
3087
+ const matrix = new Matrix43();
2705
3088
  if (!scale || !rotation || !position) {
2706
3089
  this.getMatrixAt(index, matrix).decompose(oldPosition, oldRotation, oldScale);
2707
3090
  }
2708
3091
  matrix.compose(
2709
3092
  position ? position : oldPosition,
2710
- rotation ? new Quaternion2().setFromEuler(rotation) : oldRotation,
3093
+ rotation ? new Quaternion3().setFromEuler(rotation) : oldRotation,
2711
3094
  scale ? scale : oldScale
2712
3095
  );
2713
3096
  this.setMatrixAt(index, matrix);
@@ -2716,7 +3099,7 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2716
3099
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2717
3100
  this.setPositionRotationScaleAt(index, position);
2718
3101
  }
2719
- getPositionAt(index, vector = new Vector33()) {
3102
+ getPositionAt(index, vector = new Vector35()) {
2720
3103
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2721
3104
  const matrix = this.getMatrixAt(index);
2722
3105
  vector.setFromMatrixPosition(matrix);
@@ -2726,7 +3109,7 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2726
3109
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2727
3110
  this.setPositionRotationScaleAt(index, void 0, rotation, void 0);
2728
3111
  }
2729
- getRotationAt(index, euler = new Euler()) {
3112
+ getRotationAt(index, euler = new Euler3()) {
2730
3113
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2731
3114
  const matrix = this.getMatrixAt(index);
2732
3115
  euler.setFromRotationMatrix(matrix);
@@ -2736,7 +3119,7 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2736
3119
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2737
3120
  this.setPositionRotationScaleAt(index, void 0, void 0, scale);
2738
3121
  }
2739
- getScaleAt(index, vector = new Vector33()) {
3122
+ getScaleAt(index, vector = new Vector35()) {
2740
3123
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
2741
3124
  const matrix = this.getMatrixAt(index);
2742
3125
  vector.setFromMatrixScale(matrix);
@@ -2840,34 +3223,28 @@ var _SceneObject = class _SceneObject extends Object3D2 {
2840
3223
  this.notifyDirty();
2841
3224
  }
2842
3225
  removeFromScene() {
2843
- var _a;
2844
- (_a = this._scene) == null ? void 0 : _a.remove(this);
3226
+ this._scene?.remove(this);
2845
3227
  this.notifyDirty();
2846
3228
  }
2847
3229
  //</editor-fold>
2848
3230
  };
2849
- _SceneObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge4.default)({}, {
2850
- instanceMeshes: true,
2851
- maxInstanceCount: 50,
2852
- mergeMeshes: true,
2853
- wireframe: false
2854
- });
2855
- var SceneObject = _SceneObject;
2856
3231
  function isSceneObject(obj) {
2857
3232
  return obj.isSceneObject;
2858
3233
  }
2859
3234
 
2860
3235
  // src/entity/scene/EntityObject.ts
2861
- var import_ts_deepmerge5 = __toESM(require_dist());
3236
+ var import_ts_deepmerge4 = __toESM(require_dist());
2862
3237
  import { Object3D as Object3D3 } from "three";
2863
- import * as THREE2 from "three";
2864
- var EntityObject = class extends SceneObject {
3238
+ var EntityObject = class _EntityObject extends SceneObject {
2865
3239
  constructor(entity, options) {
2866
3240
  super();
2867
3241
  this.entity = entity;
2868
3242
  this.isEntityObject = true;
2869
3243
  this.meshesCreated = false;
2870
- this.options = (0, import_ts_deepmerge5.default)({}, SceneObject.DEFAULT_OPTIONS, options ?? {});
3244
+ this.options = (0, import_ts_deepmerge4.default)({}, _EntityObject.DEFAULT_OPTIONS, options ?? {});
3245
+ }
3246
+ static {
3247
+ this.DEFAULT_OPTIONS = (0, import_ts_deepmerge4.default)({}, SceneObject.DEFAULT_OPTIONS, { flip: true });
2871
3248
  }
2872
3249
  async init() {
2873
3250
  this.createMeshes();
@@ -2876,67 +3253,76 @@ var EntityObject = class extends SceneObject {
2876
3253
  dispose() {
2877
3254
  super.dispose();
2878
3255
  }
2879
- async loadTextures() {
2880
- var _a;
2881
- this.imageData = await ModelTextures.get(new AssetKey(
2882
- ((_a = this.entity.key) == null ? void 0 : _a.namespace) ?? DEFAULT_NAMESPACE,
2883
- this.entity.key.path,
2884
- //TODO: texture may differ from entity name; most of them are in subdirectories for multiple variants etc.
3256
+ get textureKey() {
3257
+ if (this.entity.texture) return this.entity.texture;
3258
+ const key = this.entity.key;
3259
+ return new AssetKey(
3260
+ key.namespace,
3261
+ isAssetKey(key) ? key.getFullPath() : key.path,
2885
3262
  "textures",
2886
3263
  "entity",
2887
3264
  "assets",
2888
3265
  ".png"
2889
- ));
3266
+ );
3267
+ }
3268
+ async loadTextures() {
3269
+ this.imageData = await ModelTextures.get(this.textureKey);
2890
3270
  }
2891
- //TODO: abstract this, since extracted model data can be used for entities, blocks, players
2892
3271
  createMeshes(force = false) {
2893
3272
  if (this.meshesCreated && !force) return;
2894
- const mat = Materials.MISSING_TEXTURE;
2895
- for (let partName in this.entity.parts) {
2896
- const part = this.entity.parts[partName];
2897
- const texture = new MinecraftCubeTexture(part.textureOffsetU, part.textureOffsetV, part.textureWidth, part.textureHeight);
2898
- const partAnchor = new Object3D3();
2899
- partAnchor.position.x = part.pivotX;
2900
- partAnchor.position.y = part.pivotY;
2901
- partAnchor.position.z = part.pivotZ;
2902
- for (let cube of part.cubes) {
2903
- const w = cube.maxX - cube.minX;
2904
- const h = cube.maxY - cube.minY;
2905
- const l = cube.maxZ - cube.minZ;
2906
- const uv = texture.toUvArray(w, h, l);
2907
- const cubeGeo = this._getBoxGeometryForDimensionsAndUv(w, h, l, uv).clone();
2908
- cubeGeo.applyMatrix4(new THREE2.Matrix4().makeTranslation(w / 2, h / 2, l / 2));
2909
- cubeGeo.applyMatrix4(new THREE2.Matrix4().makeTranslation(cube.minX, cube.minY, cube.minZ));
2910
- applyModelPartRotation(part, cubeGeo);
2911
- const mesh = this.createMesh(partName, cubeGeo, mat);
2912
- partAnchor.add(mesh);
2913
- if (this.options.wireframe) {
2914
- addWireframeToMesh(cubeGeo, mesh);
2915
- }
2916
- }
2917
- this.add(partAnchor);
2918
- }
3273
+ const modelRoot = new Object3D3();
3274
+ if (this.options.flip) modelRoot.scale.set(-1, -1, 1);
3275
+ this.add(modelRoot);
3276
+ this.createPart("root", this.entity.layer.root, modelRoot, this.entity.layer.texture, Materials.MISSING_TEXTURE);
2919
3277
  this.meshesCreated = true;
2920
3278
  }
2921
- async applyTextures() {
2922
- var _a;
2923
- const textureAsset = await ModelTextures.preload(new AssetKey(
2924
- ((_a = this.entity.key) == null ? void 0 : _a.namespace) ?? DEFAULT_NAMESPACE,
2925
- this.entity.key.path,
2926
- "textures",
2927
- "entity",
2928
- "assets",
2929
- ".png"
2930
- ));
2931
- if (textureAsset) {
2932
- let mat = Materials.getImage({ texture: { src: textureAsset.src } });
2933
- this.iterateAllMeshes((mesh) => {
2934
- mesh.material = mat;
2935
- });
3279
+ createPart(name, part, parent, textureSize, material) {
3280
+ const anchor = this.createGroup(name);
3281
+ anchor.position.fromArray(part.pose.offset);
3282
+ anchor.rotation.set(...part.pose.rotation, "ZYX");
3283
+ anchor.scale.fromArray(part.pose.scale ?? [1, 1, 1]);
3284
+ parent.add(anchor);
3285
+ const size = part.texture ?? textureSize;
3286
+ for (const cube of part.cubes) {
3287
+ const [width, height, depth] = cube.size;
3288
+ const [growX, growY, growZ] = cube.grow ?? [0, 0, 0];
3289
+ const texture = new MinecraftCubeTexture(...cube.uv, ...size);
3290
+ const uv = size[0] === 0 || size[1] === 0 ? new Array(48).fill(0) : texture.toUvArray(width, height, depth, cube.mirror);
3291
+ const geometry = this._getBoxGeometryForDimensionsAndUv(
3292
+ width + growX * 2,
3293
+ height + growY * 2,
3294
+ depth + growZ * 2,
3295
+ uv
3296
+ ).clone();
3297
+ geometry.translate(cube.origin[0] + width / 2, cube.origin[1] + height / 2, cube.origin[2] + depth / 2);
3298
+ const mesh = this.createMesh(name, geometry, material);
3299
+ anchor.add(mesh);
3300
+ if (this.options.wireframe) addWireframeToMesh(geometry, mesh);
3301
+ }
3302
+ for (const [childName, child] of Object.entries(part.children)) {
3303
+ this.createPart(childName, child, anchor, size, material);
2936
3304
  }
2937
3305
  }
3306
+ async applyTextures() {
3307
+ const assetKeyStr = this.textureKey.serialize();
3308
+ const keyStr = `entity:${assetKeyStr}`;
3309
+ let mat = Caching.materialCache.getIfPresent(keyStr);
3310
+ if (!mat) {
3311
+ const pending = this.loadTextures();
3312
+ const cachedAsset = Caching.textureAssetCache.getIfPresent(assetKeyStr);
3313
+ await pending;
3314
+ if (!this.imageData) return;
3315
+ const canvas = this.imageData.data.canvas;
3316
+ const createMaterial = () => Materials.createBasicCanvasMaterial(canvas);
3317
+ mat = cachedAsset && Caching.textureAssetCache.getIfPresent(assetKeyStr) === cachedAsset ? Caching.materialCache.get(keyStr, createMaterial) : createMaterial();
3318
+ this.imageData = void 0;
3319
+ }
3320
+ this.iterateAllMeshes((mesh) => {
3321
+ mesh.material = mat;
3322
+ });
3323
+ this.notifyDirty();
3324
+ }
2938
3325
  };
2939
- EntityObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge5.default)({}, SceneObject.DEFAULT_OPTIONS, {});
2940
3326
  function isEntityObject(obj) {
2941
3327
  return obj.isEntityObject;
2942
3328
  }
@@ -2948,7 +3334,7 @@ var GuiObject = class extends SceneObject {
2948
3334
 
2949
3335
  // src/inspector/SceneInspector.ts
2950
3336
  import { Raycaster, Vector2 as Vector22 } from "three";
2951
- var p8 = prefix("SceneInspector");
3337
+ var p7 = prefix("SceneInspector");
2952
3338
  var help = "<span>Ctrl+Click to Select<br/></span><br/>";
2953
3339
  var SceneInspector = class {
2954
3340
  constructor(renderer) {
@@ -2977,8 +3363,7 @@ var SceneInspector = class {
2977
3363
  return out;
2978
3364
  }
2979
3365
  onClick(event) {
2980
- var _a;
2981
- if (((_a = event.target) == null ? void 0 : _a.nodeName) !== "CANVAS") return;
3366
+ if (event.target?.nodeName !== "CANVAS") return;
2982
3367
  if (!event.ctrlKey) return;
2983
3368
  setTimeout(() => {
2984
3369
  this.mouse.x = event.clientX / window.innerWidth * 2 - 1;
@@ -3018,7 +3403,7 @@ var SceneInspector = class {
3018
3403
  }
3019
3404
  selectObject(targetObject, targetIntersection) {
3020
3405
  this.selectedObject = targetObject;
3021
- console.log(p8, "selected", targetObject);
3406
+ console.log(p7, "selected", targetObject);
3022
3407
  this.redraw(targetObject, targetIntersection);
3023
3408
  }
3024
3409
  redraw(targetObject, targetIntersection) {
@@ -3316,7 +3701,7 @@ var SceneStatsDisplay = class {
3316
3701
  };
3317
3702
 
3318
3703
  // src/instance/InstanceManager.ts
3319
- var p9 = prefix("InstanceManager");
3704
+ var p8 = prefix("InstanceManager");
3320
3705
  var InstanceManager = class {
3321
3706
  constructor() {
3322
3707
  this.isInstanceManager = true;
@@ -3324,7 +3709,7 @@ var InstanceManager = class {
3324
3709
  }
3325
3710
  async get(key) {
3326
3711
  if (key in this.instanceCache) {
3327
- console.debug(p9, "key in cache", key);
3712
+ console.debug(p8, "key in cache", key);
3328
3713
  return (await this.instanceCache[key]).nextInstance();
3329
3714
  }
3330
3715
  return void 0;
@@ -3333,13 +3718,17 @@ var InstanceManager = class {
3333
3718
  if (key in this.instanceCache) {
3334
3719
  return await this.get(key);
3335
3720
  }
3336
- console.debug(p9, "key not in cache", key);
3337
- this.instanceCache[key] = new Promise(async (resolve) => {
3338
- const obj = await supplier();
3339
- const instance = obj.nextInstance();
3340
- resolve(instance);
3341
- });
3342
- return await this.instanceCache[key];
3721
+ console.debug(p8, "key not in cache", key);
3722
+ const pending = Promise.resolve().then(supplier).then((obj) => obj.nextInstance());
3723
+ this.instanceCache[key] = pending;
3724
+ try {
3725
+ return await pending;
3726
+ } catch (error) {
3727
+ if (this.instanceCache[key] === pending) {
3728
+ delete this.instanceCache[key];
3729
+ }
3730
+ throw error;
3731
+ }
3343
3732
  }
3344
3733
  reset() {
3345
3734
  for (let instanceCacheKey in this.instanceCache) {
@@ -3356,20 +3745,6 @@ function isInstanceable(obj) {
3356
3745
  return "nextInstance" in obj;
3357
3746
  }
3358
3747
 
3359
- // src/model/DisplayPosition.ts
3360
- var DisplayPosition = /* @__PURE__ */ ((DisplayPosition2) => {
3361
- DisplayPosition2["THIRDPERSON_RIGHTHAND"] = "thirdperson_righthand";
3362
- DisplayPosition2["THIRDPERSON_LEFTHAND"] = "thirdperson_lefthand";
3363
- DisplayPosition2["FIRSTPERSON_RIGHTHAND"] = "firstperson_righthand";
3364
- DisplayPosition2["FIRSTPERSON_LEFTHAND"] = "firstperson_lefthand";
3365
- DisplayPosition2["GUI"] = "gui";
3366
- DisplayPosition2["HEAD"] = "head";
3367
- DisplayPosition2["GROUND"] = "ground";
3368
- DisplayPosition2["FIXED"] = "fixed";
3369
- return DisplayPosition2;
3370
- })(DisplayPosition || {});
3371
- var DISPLAY_POSITIONS = Object.values(DisplayPosition);
3372
-
3373
3748
  // src/model/GuiLight.ts
3374
3749
  var GuiLight = /* @__PURE__ */ ((GuiLight2) => {
3375
3750
  GuiLight2["FRONT"] = "front";
@@ -3392,8 +3767,93 @@ function isQuadArray(obj) {
3392
3767
  return Array.isArray(obj) && obj.length === 4;
3393
3768
  }
3394
3769
 
3770
+ // src/model/block/blockTints.json
3771
+ var blockTints_default = {
3772
+ "minecraft:large_fern": { source: "grass" },
3773
+ "minecraft:tall_grass": { source: "grass" },
3774
+ "minecraft:grass_block": { source: "grass" },
3775
+ "minecraft:fern": { source: "grass" },
3776
+ "minecraft:grass": { source: "grass" },
3777
+ "minecraft:short_grass": { source: "grass" },
3778
+ "minecraft:potted_fern": { source: "grass" },
3779
+ "minecraft:bush": { source: "grass" },
3780
+ "minecraft:pink_petals": { source: "grass", untinted: [0] },
3781
+ "minecraft:wildflowers": { source: "grass", untinted: [0] },
3782
+ "minecraft:spruce_leaves": { source: "constant", color: 6396257 },
3783
+ "minecraft:birch_leaves": { source: "constant", color: 8431445 },
3784
+ "minecraft:oak_leaves": { source: "constant", color: 4764952 },
3785
+ "minecraft:jungle_leaves": { source: "constant", color: 4764952 },
3786
+ "minecraft:acacia_leaves": { source: "constant", color: 4764952 },
3787
+ "minecraft:dark_oak_leaves": { source: "constant", color: 4764952 },
3788
+ "minecraft:vine": { source: "constant", color: 4764952 },
3789
+ "minecraft:mangrove_leaves": { source: "constant", color: 4764952 },
3790
+ "minecraft:leaf_litter": { source: "constant", color: 6044722 },
3791
+ "minecraft:water_cauldron": { source: "constant", color: 4159204 },
3792
+ "minecraft:redstone_wire": { source: "redstone" },
3793
+ "minecraft:attached_melon_stem": { source: "constant", color: 14731036 },
3794
+ "minecraft:attached_pumpkin_stem": { source: "constant", color: 14731036 },
3795
+ "minecraft:melon_stem": { source: "stem" },
3796
+ "minecraft:pumpkin_stem": { source: "stem" },
3797
+ "minecraft:lily_pad": { source: "constant", color: 7455580 }
3798
+ };
3799
+
3800
+ // src/model/block/BlockTints.ts
3801
+ var rules = blockTints_default;
3802
+ var BlockTints = class {
3803
+ /** Resolves block preview colors without biome context; explicit tints take precedence. */
3804
+ static async get(key, state, model, tints) {
3805
+ const rule = key && rules[key.toNamespacedString()];
3806
+ if (!key || !rule) return tints;
3807
+ const indices = /* @__PURE__ */ new Set();
3808
+ for (const element of model.elements ?? []) {
3809
+ for (const face of Object.values(element.faces)) {
3810
+ const index = face?.tintindex;
3811
+ if (index !== void 0 && index >= 0 && tints?.[index] === void 0 && !rule.untinted?.includes(index)) {
3812
+ indices.add(index);
3813
+ }
3814
+ }
3815
+ }
3816
+ if (!indices.size) return tints;
3817
+ let color;
3818
+ switch (rule.source) {
3819
+ case "constant":
3820
+ color = rule.color;
3821
+ break;
3822
+ case "grass":
3823
+ color = await this.grassColor(key);
3824
+ break;
3825
+ case "redstone": {
3826
+ const power = Number(state.power ?? 0) / 15;
3827
+ const red = power * 0.6 + (power > 0 ? 0.4 : 0.3);
3828
+ const green = Math.max(0, power * power * 0.7 - 0.5);
3829
+ const blue = Math.max(0, power * power * 0.6 - 0.7);
3830
+ color = Math.floor(red * 255) << 16 | Math.floor(green * 255) << 8 | Math.floor(blue * 255);
3831
+ break;
3832
+ }
3833
+ case "stem": {
3834
+ const age = Number(state.age ?? 0);
3835
+ color = age * 32 << 16 | 255 - age * 8 << 8 | age * 4;
3836
+ break;
3837
+ }
3838
+ }
3839
+ return { ...Object.fromEntries([...indices].map((index) => [index, color])), ...tints };
3840
+ }
3841
+ static async grassColor(origin) {
3842
+ const key = AssetKey.parse("textures", "minecraft:colormap/grass", origin);
3843
+ return await Caching.blockTintCache.get(key.serialize(), async () => {
3844
+ const image = await ModelTextures.get(key);
3845
+ if (!image) throw new MineRenderError(`Missing grass colormap: ${key.toNamespacedString()}`);
3846
+ if (image.width !== 256 || image.height !== 256) {
3847
+ throw new MineRenderError("Grass colormaps must be 256 \xD7 256 pixels");
3848
+ }
3849
+ const [red, green, blue] = image.data.getImageData(127, 127, 1, 1).data;
3850
+ return red << 16 | green << 8 | blue;
3851
+ });
3852
+ }
3853
+ };
3854
+
3395
3855
  // src/model/scene/ModelObject.ts
3396
- import { BoxGeometry as BoxGeometry5, Matrix4 as Matrix45 } from "three";
3856
+ import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute4, Color as Color3, Euler as Euler4, Matrix4 as Matrix44 } from "three";
3397
3857
 
3398
3858
  // src/three/BufferGeometryUtils.js
3399
3859
  import {
@@ -3405,7 +3865,7 @@ import {
3405
3865
  TriangleFanDrawMode,
3406
3866
  TriangleStripDrawMode,
3407
3867
  TrianglesDrawMode,
3408
- Vector3 as Vector35
3868
+ Vector3 as Vector36
3409
3869
  } from "three";
3410
3870
  function computeTangents(geometry) {
3411
3871
  geometry.computeTangents();
@@ -3724,15 +4184,15 @@ function computeMorphedAttributes(object) {
3724
4184
  console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
3725
4185
  return null;
3726
4186
  }
3727
- const _vA = new Vector35();
3728
- const _vB = new Vector35();
3729
- const _vC = new Vector35();
3730
- const _tempA = new Vector35();
3731
- const _tempB = new Vector35();
3732
- const _tempC = new Vector35();
3733
- const _morphA = new Vector35();
3734
- const _morphB = new Vector35();
3735
- const _morphC = new Vector35();
4187
+ const _vA = new Vector36();
4188
+ const _vB = new Vector36();
4189
+ const _vC = new Vector36();
4190
+ const _tempA = new Vector36();
4191
+ const _tempB = new Vector36();
4192
+ const _tempC = new Vector36();
4193
+ const _morphA = new Vector36();
4194
+ const _morphB = new Vector36();
4195
+ const _morphC = new Vector36();
3736
4196
  function _calculateMorphedAttributeData(object2, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
3737
4197
  _vA.fromBufferAttribute(attribute, a2);
3738
4198
  _vB.fromBufferAttribute(attribute, b2);
@@ -3938,26 +4398,29 @@ function computeMorphedAttributes(object) {
3938
4398
  }
3939
4399
 
3940
4400
  // src/model/scene/ModelObject.ts
3941
- var import_ts_deepmerge6 = __toESM(require_dist());
3942
- var p10 = prefix("ModelObject");
3943
- var _ModelObject = class _ModelObject extends SceneObject {
4401
+ var import_ts_deepmerge5 = __toESM(require_dist());
4402
+ var p9 = prefix("ModelObject");
4403
+ var ModelObject = class _ModelObject extends SceneObject {
3944
4404
  constructor(originalModel, options) {
3945
4405
  super(options);
3946
4406
  this.originalModel = originalModel;
3947
4407
  this.isModelObject = true;
3948
4408
  this.meshesCreated = false;
3949
- this.options = (0, import_ts_deepmerge6.default)({}, _ModelObject.DEFAULT_OPTIONS, options ?? {});
4409
+ this.options = (0, import_ts_deepmerge5.default)({}, _ModelObject.DEFAULT_OPTIONS, options ?? {});
4410
+ if (this.options.tints) this.options.tints = { ...this.options.tints };
3950
4411
  console.log("ModelObject options", this.options);
3951
4412
  }
4413
+ static {
4414
+ this.DEFAULT_OPTIONS = (0, import_ts_deepmerge5.default)({}, SceneObject.DEFAULT_OPTIONS, {});
4415
+ }
3952
4416
  async init() {
3953
4417
  await this.loadTextures();
3954
4418
  this.createMeshes();
3955
4419
  this.applyTextures();
3956
4420
  }
3957
4421
  dispose() {
3958
- var _a;
3959
4422
  super.dispose();
3960
- (_a = this.atlas) == null ? void 0 : _a.dispose();
4423
+ this.atlas?.dispose();
3961
4424
  }
3962
4425
  get textureAtlas() {
3963
4426
  return this.atlas;
@@ -3967,20 +4430,38 @@ var _ModelObject = class _ModelObject extends SceneObject {
3967
4430
  this.atlas = await UVMapper.getAtlas(this.originalModel);
3968
4431
  }
3969
4432
  createMeshes(force = false) {
3970
- var _a;
3971
4433
  if (this.meshesCreated && !force) return;
3972
4434
  const mat = Materials.MISSING_TEXTURE;
4435
+ const displayTransform = this.options.displayPosition ? DisplayTransforms.getMatrix(this.originalModel.display, this.options.displayPosition) : void 0;
3973
4436
  let allGeos = [];
3974
4437
  if (this.atlas) {
3975
4438
  if (this.atlas.model.elements) {
3976
- (_a = this.atlas.model.elements) == null ? void 0 : _a.forEach((el) => {
4439
+ this.atlas.model.elements?.forEach((el) => {
3977
4440
  const elGeo = this._getBoxGeometryFromElement(el).clone();
3978
- 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));
3979
- elGeo.applyMatrix4(new Matrix45().makeTranslation(el.from[0], el.from[1], el.from[2]));
4441
+ if (this.options.uvLockRotation) {
4442
+ UVMapper.lockUvs(elGeo, el.faces, this.atlas, new Euler4(...this.options.uvLockRotation));
4443
+ }
4444
+ if (this.options.tints) {
4445
+ const colors = new Float32Array(elGeo.getAttribute("position").count * 3).fill(1);
4446
+ for (const [faceIndex, faceName] of CUBE_FACES.entries()) {
4447
+ const tintIndex = el.faces[faceName]?.tintindex;
4448
+ if (tintIndex === void 0 || tintIndex < 0) continue;
4449
+ const tint = this.options.tints[tintIndex];
4450
+ if (tint === void 0) continue;
4451
+ const color = new Color3(tint);
4452
+ for (let vertex = 0; vertex < 4; vertex++) {
4453
+ color.toArray(colors, (faceIndex * 4 + vertex) * 3);
4454
+ }
4455
+ }
4456
+ elGeo.setAttribute("color", new BufferAttribute4(colors, 3));
4457
+ }
4458
+ 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));
4459
+ elGeo.applyMatrix4(new Matrix44().makeTranslation(el.from[0], el.from[1], el.from[2]));
3980
4460
  if (el.rotation) {
3981
4461
  applyElementRotation(el.rotation, elGeo);
3982
4462
  }
3983
- elGeo.applyMatrix4(new Matrix45().makeTranslation(-8, -8, -8));
4463
+ elGeo.applyMatrix4(new Matrix44().makeTranslation(-8, -8, -8));
4464
+ if (displayTransform) DisplayTransforms.apply(elGeo, displayTransform);
3984
4465
  if (this.options.mergeMeshes) {
3985
4466
  allGeos.push(elGeo);
3986
4467
  } else {
@@ -3991,10 +4472,10 @@ var _ModelObject = class _ModelObject extends SceneObject {
3991
4472
  }
3992
4473
  });
3993
4474
  } else {
3994
- console.debug(p10, this.atlas.model, "has no elements");
4475
+ console.debug(p9, this.atlas.model, "has no elements");
3995
4476
  }
3996
4477
  } else {
3997
- console.debug(p10, "Missing atlas for", this);
4478
+ console.debug(p9, "Missing atlas for", this);
3998
4479
  }
3999
4480
  if (this.options.mergeMeshes) {
4000
4481
  let combinedGeo;
@@ -4002,6 +4483,7 @@ var _ModelObject = class _ModelObject extends SceneObject {
4002
4483
  combinedGeo = mergeBufferGeometries(allGeos);
4003
4484
  } else {
4004
4485
  combinedGeo = new BoxGeometry5(16, 16, 16);
4486
+ if (displayTransform) DisplayTransforms.apply(combinedGeo, displayTransform);
4005
4487
  }
4006
4488
  combinedGeo.computeBoundingBox();
4007
4489
  let mesh;
@@ -4031,6 +4513,7 @@ var _ModelObject = class _ModelObject extends SceneObject {
4031
4513
  /*TODO: get this from render options*/
4032
4514
  );
4033
4515
  this.iterateAllMeshes((mesh) => {
4516
+ if (mesh.geometry.hasAttribute("color")) mat.vertexColors = true;
4034
4517
  mesh.material = mat;
4035
4518
  });
4036
4519
  if (this.atlas.hasAnimation) {
@@ -4046,17 +4529,25 @@ var _ModelObject = class _ModelObject extends SceneObject {
4046
4529
  }
4047
4530
  }
4048
4531
  };
4049
- _ModelObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge6.default)({}, SceneObject.DEFAULT_OPTIONS, {});
4050
- var ModelObject = _ModelObject;
4051
4532
  function isModelObject(obj) {
4052
4533
  return obj.isModelObject;
4053
4534
  }
4054
4535
 
4055
4536
  // src/model/block/scene/BlockObject.ts
4056
- var import_ts_deepmerge7 = __toESM(require_dist());
4057
- import { Euler as Euler3, Matrix4 as Matrix46, Vector3 as Vector36 } from "three";
4058
- var p11 = prefix("BlockObject");
4059
- var _BlockObject = class _BlockObject extends SceneObject {
4537
+ var import_ts_deepmerge6 = __toESM(require_dist());
4538
+ import { Euler as Euler5, Matrix4 as Matrix45, Vector3 as Vector37 } from "three";
4539
+ var p10 = prefix("BlockObject");
4540
+ function matchesCondition(condition, state) {
4541
+ return Object.entries(condition).every(([key, value]) => {
4542
+ if (Array.isArray(value)) {
4543
+ if (key === "OR") return value.some((child) => matchesCondition(child, state));
4544
+ if (key === "AND") return value.every((child) => matchesCondition(child, state));
4545
+ return false;
4546
+ }
4547
+ return typeof value === "string" && state[key] !== void 0 && value.split("|").includes(`${state[key]}`);
4548
+ });
4549
+ }
4550
+ var BlockObject = class _BlockObject extends SceneObject {
4060
4551
  constructor(blockState, options) {
4061
4552
  super(options);
4062
4553
  this.blockState = blockState;
@@ -4069,11 +4560,16 @@ var _BlockObject = class _BlockObject extends SceneObject {
4069
4560
  this._instanceModel = [];
4070
4561
  this._variants = [];
4071
4562
  this._models = [];
4072
- this.options = (0, import_ts_deepmerge7.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
4563
+ this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
4564
+ }
4565
+ static {
4566
+ this.DEFAULT_OPTIONS = (0, import_ts_deepmerge6.default)({}, ModelObject.DEFAULT_OPTIONS, {
4567
+ applyDefaultState: true
4568
+ });
4073
4569
  }
4074
4570
  async init() {
4075
4571
  if (this.options.applyDefaultState) {
4076
- const defaultState = this.blockState.key ? BlockStates.getDefaultState(this.blockState.key) : void 0;
4572
+ const defaultState = this.blockState.key ? await BlockStates.getDefaultState(this.blockState.key) : void 0;
4077
4573
  if (defaultState && Object.keys(defaultState).length > 0) {
4078
4574
  const state = {};
4079
4575
  for (let k in defaultState) {
@@ -4084,15 +4580,9 @@ var _BlockObject = class _BlockObject extends SceneObject {
4084
4580
  if (this.blockState.variants) {
4085
4581
  await this.setState(Object.keys(this.blockState.variants)[0]);
4086
4582
  } else if (this.blockState.multipart) {
4087
- for (let part of this.blockState.multipart) {
4088
- if (part.when && !("OR" in part.when)) {
4089
- const k = Object.keys(part.when)[0];
4090
- if (k) {
4091
- await this.setState(k, part.when[k].split("|")[0]);
4092
- break;
4093
- }
4094
- }
4095
- }
4583
+ const condition = this.blockState.multipart.map((part) => part.when).find((when) => when !== void 0 && Object.values(when).every((value) => typeof value === "string"));
4584
+ const state = Object.fromEntries(Object.entries(condition ?? {}).map(([key, value]) => [key, value.split("|")[0]]));
4585
+ await this.setState(state);
4096
4586
  }
4097
4587
  }
4098
4588
  } else {
@@ -4107,7 +4597,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4107
4597
  if (isModelObject(model)) {
4108
4598
  model.removeFromScene();
4109
4599
  } else if (isInstanceReference(model)) {
4110
- model.setScale(new Vector36(0, 0, 0));
4600
+ model.setScale(new Vector37(0, 0, 0));
4111
4601
  }
4112
4602
  }
4113
4603
  super.removeFromScene();
@@ -4149,55 +4639,11 @@ var _BlockObject = class _BlockObject extends SceneObject {
4149
4639
  } else if (this.blockState.multipart) {
4150
4640
  for (let part of this.blockState.multipart) {
4151
4641
  if (!part.apply) {
4152
- console.debug(p11, "Missing apply for blockState part", part);
4642
+ console.debug(p10, "Missing apply for blockState part", part);
4153
4643
  continue;
4154
4644
  }
4155
- if (!part.when) {
4645
+ if (!part.when || matchesCondition(part.when, state)) {
4156
4646
  out.push(this.getSingleVariant(part.apply));
4157
- } else {
4158
- let matches = true;
4159
- if ("OR" in part.when) {
4160
- const or = part.when.OR;
4161
- let anyMatch = false;
4162
- for (let o of or) {
4163
- for (let k in o) {
4164
- const split = o[k].split("|");
4165
- let m = false;
4166
- for (let s of split) {
4167
- if (`${state[k]}` === `${s}`) {
4168
- m = true;
4169
- break;
4170
- }
4171
- }
4172
- if (m) {
4173
- anyMatch = true;
4174
- break;
4175
- }
4176
- }
4177
- }
4178
- if (!anyMatch) {
4179
- matches = false;
4180
- }
4181
- } else {
4182
- for (let k in part.when) {
4183
- const split = part.when[k].split("|");
4184
- let m = false;
4185
- for (let s of split) {
4186
- if (`${state[k]}` === `${s}`) {
4187
- m = true;
4188
- break;
4189
- }
4190
- }
4191
- if (!m) {
4192
- matches = false;
4193
- break;
4194
- }
4195
- }
4196
- }
4197
- if (matches) {
4198
- const variants = part.apply;
4199
- out.push(this.getSingleVariant(variants));
4200
- }
4201
4647
  }
4202
4648
  }
4203
4649
  }
@@ -4212,7 +4658,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4212
4658
  }
4213
4659
  for (let model of this._models) {
4214
4660
  if (isInstanceReference(model)) {
4215
- model.setScale(new Vector36(0, 0, 0));
4661
+ model.setScale(new Vector37(0, 0, 0));
4216
4662
  } else {
4217
4663
  model.dispose();
4218
4664
  }
@@ -4227,11 +4673,21 @@ var _BlockObject = class _BlockObject extends SceneObject {
4227
4673
  }
4228
4674
  }
4229
4675
  getSingleVariant(variants) {
4230
- if (Array.isArray(variants)) {
4231
- return variants[0];
4232
- } else {
4233
- return variants;
4234
- }
4676
+ if (!Array.isArray(variants)) return variants;
4677
+ if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
4678
+ const total = variants.reduce((sum, variant) => {
4679
+ const weight = variant.weight ?? 1;
4680
+ if (!Number.isInteger(weight) || weight < 1) {
4681
+ throw new MineRenderError(`Invalid blockstate variant weight: ${weight}`);
4682
+ }
4683
+ return sum + weight;
4684
+ }, 0);
4685
+ let choice = Math.random() * total;
4686
+ for (const variant of variants) {
4687
+ choice -= variant.weight ?? 1;
4688
+ if (choice < 0) return variant;
4689
+ }
4690
+ return variants[variants.length - 1];
4235
4691
  }
4236
4692
  // @deprecated
4237
4693
  async createAVariant(variants, instanceInfo) {
@@ -4239,28 +4695,27 @@ var _BlockObject = class _BlockObject extends SceneObject {
4239
4695
  await this.createVariant(variant);
4240
4696
  }
4241
4697
  async createVariant(variant) {
4242
- const model = await Models.getMerged(AssetKey.parse("models", variant.model));
4243
- const obj = await this.scene.addModel(model, this.options);
4244
- if (isInstanceReference(obj) || obj.isInstanced) {
4245
- this._isInstanced = true;
4246
- this._instanceCounter = 1;
4247
- }
4248
- let rotation = new Euler3(0, 0, 0);
4698
+ const rotation = new Euler5();
4249
4699
  if (typeof variant.x !== "undefined") {
4250
4700
  rotation.x = toRadians(clampRotationDegrees(variant.x));
4251
4701
  }
4252
4702
  if (typeof variant.y !== "undefined") {
4253
- if (typeof variant.x !== "undefined") {
4254
- rotation.y = toRadians(clampRotationDegrees(variant.y));
4255
- } else {
4256
- rotation.y = toRadians(clampRotationDegrees(360 - variant.y));
4257
- }
4703
+ rotation.y = toRadians(clampRotationDegrees(typeof variant.x !== "undefined" ? variant.y : 360 - variant.y));
4704
+ }
4705
+ const modelKey = AssetKey.parse("models", variant.model);
4706
+ modelKey.root = this.blockState.key?.root;
4707
+ const model = await Models.getMerged(modelKey);
4708
+ const options = {
4709
+ ...this.options,
4710
+ tints: model ? await BlockTints.get(this.blockState.key, this.state, model, this.options.tints) : this.options.tints,
4711
+ uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
4712
+ };
4713
+ const obj = await this.scene.addModel(model, options);
4714
+ if (isInstanceReference(obj) || obj.isInstanced) {
4715
+ this._isInstanced = true;
4716
+ this._instanceCounter = 1;
4258
4717
  }
4259
4718
  obj.setRotation(rotation);
4260
- setTimeout(() => {
4261
- obj.setRotation(rotation);
4262
- }, 150);
4263
- console.log(obj);
4264
4719
  return obj;
4265
4720
  }
4266
4721
  async resetState() {
@@ -4329,7 +4784,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4329
4784
  setPositionRotationScaleAt(index, position, rotation, scale) {
4330
4785
  super.setPositionRotationScaleAt(index, position, rotation, scale);
4331
4786
  }
4332
- getMatrixAt(index, matrix = new Matrix46()) {
4787
+ getMatrixAt(index, matrix = new Matrix45()) {
4333
4788
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
4334
4789
  const child = this._models[0];
4335
4790
  if (child && isModelObject(child)) {
@@ -4350,18 +4805,15 @@ var _BlockObject = class _BlockObject extends SceneObject {
4350
4805
  }
4351
4806
  }
4352
4807
  setPosition(position) {
4353
- super.setPosition(position);
4808
+ this.position.copy(position);
4354
4809
  for (let model of this._models) {
4355
4810
  model.setPosition(position);
4356
4811
  }
4812
+ this.notifyDirty();
4357
4813
  }
4358
4814
  //TODO: should override rotation + scale methods
4359
4815
  //TODO
4360
4816
  };
4361
- _BlockObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge7.default)({}, ModelObject.DEFAULT_OPTIONS, {
4362
- applyDefaultState: true
4363
- });
4364
- var BlockObject = _BlockObject;
4365
4817
  function isBlockObject(obj) {
4366
4818
  return obj.isBlockObject;
4367
4819
  }
@@ -4386,11 +4838,10 @@ var StructureParser = class {
4386
4838
  throw new MineRenderError("Structure does not have palette(s)");
4387
4839
  }
4388
4840
  const blocks = nbt.blocks.value.value.map((block) => {
4389
- var _a;
4390
4841
  const state = palette.value.value[block.state.value];
4391
4842
  if (state.Name.value === "minecraft:air") return void 0;
4392
4843
  const props = {};
4393
- for (let k in (_a = state.Properties) == null ? void 0 : _a.value) {
4844
+ for (let k in state.Properties?.value) {
4394
4845
  props[k] = state.Properties.value[k].value;
4395
4846
  }
4396
4847
  return {
@@ -4409,7 +4860,7 @@ var StructureParser = class {
4409
4860
 
4410
4861
  // src/renderer/MineRenderScene.ts
4411
4862
  import { Scene as Scene2 } from "three";
4412
- var import_ts_deepmerge9 = __toESM(require_dist());
4863
+ var import_ts_deepmerge8 = __toESM(require_dist());
4413
4864
 
4414
4865
  // src/skin/SkinPart.ts
4415
4866
  var SkinPart = /* @__PURE__ */ ((SkinPart2) => {
@@ -5045,62 +5496,52 @@ var baseSkinCoordinates = {
5045
5496
  }
5046
5497
  },
5047
5498
  cape: {
5048
- //TODO
5049
- east: {
5050
- uv: [
5051
- 0,
5052
- 36,
5053
- 4,
5054
- 48
5055
- ]
5056
- },
5057
- west: {
5058
- uv: [
5059
- 8,
5060
- 36,
5061
- 12,
5062
- 48
5063
- ]
5064
- },
5065
- north: {
5066
- uv: [
5067
- 4,
5068
- 36,
5069
- 8,
5070
- 48
5071
- ]
5072
- },
5073
- south: {
5074
- uv: [
5075
- 12,
5076
- 36,
5077
- 16,
5078
- 48
5079
- ]
5080
- },
5081
- up: {
5082
- uv: [
5083
- 4,
5084
- 32,
5085
- 8,
5086
- 36
5087
- ]
5088
- },
5089
- down: {
5090
- uv: [
5091
- 8,
5092
- 32,
5093
- 12,
5094
- 36
5095
- ],
5096
- rotation: 180
5097
- }
5499
+ east: { uv: [0, 1, 1, 17] },
5500
+ west: { uv: [11, 1, 12, 17] },
5501
+ north: { uv: [1, 1, 11, 17] },
5502
+ south: { uv: [12, 1, 22, 17] },
5503
+ up: { uv: [11, 1, 1, 0] },
5504
+ down: { uv: [21, 0, 11, 1] }
5098
5505
  }
5099
5506
  };
5100
5507
  var classicSkinTextureCoordinates = {
5101
5508
  ...baseSkinCoordinates
5102
5509
  };
5103
- var slimSkinTextureCoordinates = { ...baseSkinCoordinates };
5510
+ var slimSkinTextureCoordinates = {
5511
+ ...baseSkinCoordinates,
5512
+ leftArm: {
5513
+ ...baseSkinCoordinates.leftArm,
5514
+ west: { uv: [39, 52, 43, 64] },
5515
+ north: { uv: [36, 52, 39, 64] },
5516
+ south: { uv: [43, 52, 46, 64] },
5517
+ up: { uv: [39, 52, 36, 48] },
5518
+ down: { uv: [42, 48, 39, 52] }
5519
+ },
5520
+ rightArm: {
5521
+ ...baseSkinCoordinates.rightArm,
5522
+ west: { uv: [47, 20, 51, 32] },
5523
+ north: { uv: [44, 20, 47, 32] },
5524
+ south: { uv: [51, 20, 54, 32] },
5525
+ up: { uv: [47, 20, 44, 16] },
5526
+ down: { uv: [50, 16, 47, 20] }
5527
+ },
5528
+ leftSleeve: {
5529
+ ...baseSkinCoordinates.leftSleeve,
5530
+ west: { uv: [55, 52, 59, 64] },
5531
+ north: { uv: [52, 52, 55, 64] },
5532
+ south: { uv: [59, 52, 62, 64] },
5533
+ up: { uv: [55, 52, 52, 48] },
5534
+ down: { uv: [58, 48, 55, 52], rotation: 180 }
5535
+ },
5536
+ rightSleeve: {
5537
+ ...baseSkinCoordinates.rightSleeve,
5538
+ west: { uv: [47, 36, 51, 48] },
5539
+ north: { uv: [44, 36, 47, 48] },
5540
+ south: { uv: [51, 36, 54, 48] },
5541
+ up: { uv: [47, 36, 44, 32] },
5542
+ down: { uv: [50, 32, 47, 36], rotation: 180 }
5543
+ }
5544
+ };
5104
5545
 
5105
5546
  // src/skin/SkinGeometries.ts
5106
5547
  var box8inner = [8, 8, 8];
@@ -5140,15 +5581,118 @@ var slimSkinGeometries = {
5140
5581
  };
5141
5582
 
5142
5583
  // src/skin/scene/SkinObject.ts
5143
- var import_ts_deepmerge8 = __toESM(require_dist());
5584
+ var import_ts_deepmerge7 = __toESM(require_dist());
5585
+
5586
+ // src/skin/SkinTextures.ts
5587
+ import { DataTexture, DoubleSide as DoubleSide2, FrontSide as FrontSide2, MeshBasicMaterial as MeshBasicMaterial3, SRGBColorSpace as SRGBColorSpace3 } from "three";
5588
+ var SkinTextures = class {
5589
+ static async get(src, legacy) {
5590
+ const image = await ImageLoader.getData(src);
5591
+ const scale = image.width / 64;
5592
+ if (!Number.isInteger(scale) || scale < 1 || image.height !== image.width && image.height !== image.width / 2) {
5593
+ throw new MineRenderError(`Invalid skin dimensions ${image.width}x${image.height}; expected 64x64 or 64x32, or an integer-scaled equivalent`);
5594
+ }
5595
+ const isLegacy = legacy ?? image.height === image.width / 2;
5596
+ if (!isLegacy && image.height !== image.width) {
5597
+ throw new MineRenderError(`Invalid skin dimensions ${image.width}x${image.height}; non-legacy skins must be square`);
5598
+ }
5599
+ const slim = !isLegacy && this.isSlim(image, scale);
5600
+ const key = `skin:${serializeImageKey({ src })}:${isLegacy}`;
5601
+ const material = Caching.materialCache.get(key, () => {
5602
+ const pixels = isLegacy ? this.normalizeLegacy(image, scale) : new Uint8Array(image.data);
5603
+ return this.createMaterial(pixels, image.width, image.width);
5604
+ });
5605
+ return { material, slim, legacy: isLegacy };
5606
+ }
5607
+ static async getCape(src, layout = "minecraft") {
5608
+ const image = await ImageLoader.getData(src);
5609
+ const scale = image.width / 64;
5610
+ if (layout === "minecraft" && (!Number.isInteger(scale) || scale < 1 || image.height !== image.width / 2)) {
5611
+ throw new MineRenderError(`Invalid cape dimensions ${image.width}x${image.height}; expected 64x32 or an integer-scaled equivalent`);
5612
+ }
5613
+ return Caching.materialCache.get(`cape:${serializeImageKey({ src })}`, () => {
5614
+ return this.createMaterial(new Uint8Array(image.data), image.width, image.height, false);
5615
+ });
5616
+ }
5617
+ static createMaterial(pixels, width, height, transparent = true) {
5618
+ const texture = Textures.initTextureProps(new DataTexture(pixels, width, height));
5619
+ texture.colorSpace = SRGBColorSpace3;
5620
+ texture.flipY = true;
5621
+ texture.needsUpdate = true;
5622
+ return new MeshBasicMaterial3({ map: texture, transparent, side: transparent ? DoubleSide2 : FrontSide2, alphaTest: transparent ? 0.5 : 0 });
5623
+ }
5624
+ static isSlim(image, scale) {
5625
+ for (const [x, y] of [[46, 52], [54, 20]]) {
5626
+ for (let row = y * scale; row < (y + 12) * scale; row++) {
5627
+ for (let column = x * scale; column < (x + 1) * scale; column++) {
5628
+ if (image.data[(row * image.width + column) * 4 + 3] !== 0) return false;
5629
+ }
5630
+ }
5631
+ }
5632
+ return true;
5633
+ }
5634
+ static normalizeLegacy(image, scale) {
5635
+ const pixels = new Uint8Array(image.width * image.width * 4);
5636
+ pixels.set(image.data.subarray(0, image.width * image.width / 2 * 4));
5637
+ for (const [sx, sy, dx, dy, width, height] of [
5638
+ [4, 16, 20, 48, 4, 4],
5639
+ [8, 16, 24, 48, 4, 4],
5640
+ [0, 20, 24, 52, 4, 12],
5641
+ [4, 20, 20, 52, 4, 12],
5642
+ [8, 20, 16, 52, 4, 12],
5643
+ [12, 20, 28, 52, 4, 12],
5644
+ [44, 16, 36, 48, 4, 4],
5645
+ [48, 16, 40, 48, 4, 4],
5646
+ [40, 20, 40, 52, 4, 12],
5647
+ [44, 20, 36, 52, 4, 12],
5648
+ [48, 20, 32, 52, 4, 12],
5649
+ [52, 20, 44, 52, 4, 12]
5650
+ ]) {
5651
+ for (let y = 0; y < height * scale; y++) {
5652
+ for (let x = 0; x < width * scale; x++) {
5653
+ const source = ((sy * scale + y) * image.width + sx * scale + x) * 4;
5654
+ const target = ((dy * scale + y) * image.width + (dx + width) * scale - 1 - x) * 4;
5655
+ pixels.set(image.data.subarray(source, source + 4), target);
5656
+ }
5657
+ }
5658
+ }
5659
+ let clearHat = true;
5660
+ for (let y = 0; y < 32 * scale; y++) {
5661
+ for (let x = 32 * scale; x < image.width; x++) {
5662
+ if (pixels[(y * image.width + x) * 4 + 3] < 128) clearHat = false;
5663
+ }
5664
+ }
5665
+ for (let y = 0; y < image.width; y++) {
5666
+ for (let x = 0; x < image.width; x++) {
5667
+ const alpha = (y * image.width + x) * 4 + 3;
5668
+ if (x < 32 * scale && y < 16 * scale || y >= 16 * scale && y < 32 * scale || x >= 16 * scale && x < 48 * scale && y >= 48 * scale) {
5669
+ pixels[alpha] = 255;
5670
+ } else if (clearHat && x >= 32 * scale && y < 32 * scale) {
5671
+ pixels[alpha] = 0;
5672
+ }
5673
+ }
5674
+ }
5675
+ return pixels;
5676
+ }
5677
+ };
5678
+
5679
+ // src/skin/CapeLayout.ts
5680
+ var capeTextureSizes = {
5681
+ minecraft: [64, 32],
5682
+ optifine: [46, 22],
5683
+ labymod: [22, 17]
5684
+ };
5685
+
5686
+ // src/skin/scene/SkinObject.ts
5144
5687
  var SkinObject = class extends SceneObject {
5145
5688
  constructor(options) {
5146
5689
  super();
5147
5690
  this.slim = false;
5148
- this.legacy = false;
5149
- this.skinTextureWidth = 64;
5150
- this.skinTextureHeight = 64;
5151
- this.options = (0, import_ts_deepmerge8.default)({}, SceneObject.DEFAULT_OPTIONS, options ?? {});
5691
+ this.detectedSlim = false;
5692
+ this.skinLoad = 0;
5693
+ this.capeLoad = 0;
5694
+ this.options = (0, import_ts_deepmerge7.default)({}, SceneObject.DEFAULT_OPTIONS, options ?? {});
5695
+ this.slim = this.options.slim ?? false;
5152
5696
  }
5153
5697
  async init() {
5154
5698
  this.createMeshes();
@@ -5157,15 +5701,13 @@ var SkinObject = class extends SceneObject {
5157
5701
  }
5158
5702
  createMeshes() {
5159
5703
  console.log("#createMeshes");
5160
- const mat = Materials.MISSING_TEXTURE;
5704
+ const mat = this.skinMaterial ?? Materials.MISSING_TEXTURE;
5161
5705
  {
5162
5706
  const headGroup = this.createAndAddGroup("head", 0, 28, 0, "y" /* Y */, -4);
5163
5707
  const headGeo = this.getBoxGeometry("head" /* HEAD */);
5164
5708
  const head = this.createAndAddMesh("head", headGroup, headGeo, mat, "y" /* Y */, 4);
5165
- if (!this.legacy) {
5166
- const hatGeo = this.getBoxGeometry("hat" /* HAT */);
5167
- const hat = this.createAndAddMesh("hat", headGroup, hatGeo, mat, "y" /* Y */, 4);
5168
- }
5709
+ const hatGeo = this.getBoxGeometry("hat" /* HAT */);
5710
+ const hat = this.createAndAddMesh("hat", headGroup, hatGeo, mat, "y" /* Y */, 4);
5169
5711
  }
5170
5712
  {
5171
5713
  const bodyGroup = this.createAndAddGroup("body", 0, 18, 0);
@@ -5208,51 +5750,95 @@ var SkinObject = class extends SceneObject {
5208
5750
  }
5209
5751
  console.log("#createMeshes done");
5210
5752
  }
5211
- setSkinTexture(src) {
5753
+ async setSkinTexture(src) {
5212
5754
  if (typeof src === "undefined") return;
5213
5755
  this.skinTextureSrc = src;
5214
- const mat = Materials.getImage({
5215
- texture: { src },
5216
- transparent: true
5217
- });
5218
- for (let part of SKIN_PARTS) {
5219
- let mesh = this.getMeshByName(part);
5756
+ const load = ++this.skinLoad;
5757
+ const texture = await SkinTextures.get(src, this.options.legacy);
5758
+ if (load !== this.skinLoad) return;
5759
+ this.skinMaterial = texture.material;
5760
+ this.detectedSlim = texture.slim;
5761
+ this.updateSlim(this.options.slim ?? this.detectedSlim);
5762
+ for (const part of SKIN_PARTS) {
5763
+ if (part === "cape" /* CAPE */) continue;
5764
+ const mesh = this.getMeshByName(part);
5220
5765
  if (mesh) {
5221
- mesh.material = mat;
5766
+ mesh.material = texture.material;
5222
5767
  }
5223
5768
  }
5769
+ this.notifyDirty();
5770
+ }
5771
+ /** Load a cape texture in the selected layout, or pass undefined to remove the cape. */
5772
+ async setCapeTexture(src, layout = "minecraft") {
5773
+ const load = ++this.capeLoad;
5774
+ if (src === void 0) {
5775
+ const group = this.getGroupByName("cape" /* CAPE */);
5776
+ if (group) this.remove(group);
5777
+ this.notifyDirty();
5778
+ return;
5779
+ }
5780
+ const material = await SkinTextures.getCape(src, layout);
5781
+ if (load !== this.capeLoad) return;
5782
+ const size = capeTextureSizes[layout];
5783
+ const geometry = this._getBoxGeometryFromDimensions(
5784
+ classicSkinGeometries.cape,
5785
+ classicSkinTextureCoordinates.cape,
5786
+ size,
5787
+ size
5788
+ );
5789
+ const mesh = this.getMeshByName("cape" /* CAPE */);
5790
+ if (mesh) {
5791
+ mesh.material = material;
5792
+ mesh.geometry = geometry;
5793
+ } else {
5794
+ const group = this.createAndAddGroup("cape" /* CAPE */, 0, 24, 2);
5795
+ group.rotation.set(toRadians(-6), Math.PI, 0);
5796
+ const cape = this.createAndAddMesh("cape" /* CAPE */, group, geometry, material);
5797
+ cape.position.set(0, -8, -0.5);
5798
+ }
5799
+ this.notifyDirty();
5224
5800
  }
5225
- //TODO: cape
5801
+ /** Select the arm model, or pass undefined to use texture detection. */
5226
5802
  setSlim(slim) {
5227
- console.log("#setSlim");
5228
- const changed = slim !== this.slim;
5803
+ this.options.slim = slim;
5804
+ this.updateSlim(slim ?? this.detectedSlim);
5805
+ }
5806
+ updateSlim(slim) {
5807
+ if (slim === this.slim) return;
5229
5808
  this.slim = slim;
5230
- if (changed) {
5231
- this.createMeshes();
5232
- }
5233
- if (changed) {
5234
- if (this.skinTextureSrc) {
5235
- this.setSkinTexture(this.skinTextureSrc);
5236
- }
5237
- }
5238
- console.log("#setSlim done");
5809
+ for (const part of ["leftArm" /* LEFT_ARM */, "leftSleeve" /* LEFT_SLEEVE */, "rightArm" /* RIGHT_ARM */, "rightSleeve" /* RIGHT_SLEEVE */]) {
5810
+ const mesh = this.getMeshByName(part);
5811
+ if (mesh) mesh.geometry = this.getBoxGeometry(part);
5812
+ }
5813
+ const offset = slim ? 0.5 : -0.5;
5814
+ const leftArm = this.getGroupByName("leftArm" /* LEFT_ARM */);
5815
+ const rightArm = this.getGroupByName("rightArm" /* RIGHT_ARM */);
5816
+ if (leftArm) leftArm.position.x += offset;
5817
+ if (rightArm) rightArm.position.x -= offset;
5818
+ this.notifyDirty();
5239
5819
  }
5240
- setLegacy(legacy) {
5241
- if (legacy !== this.legacy) {
5242
- }
5243
- this.legacy = legacy;
5820
+ /** Select the texture layout, or pass undefined to detect it from image dimensions. */
5821
+ async setLegacy(legacy) {
5822
+ if (legacy === this.options.legacy) return;
5823
+ this.options.legacy = legacy;
5824
+ if (this.skinTextureSrc) await this.setSkinTexture(this.skinTextureSrc);
5825
+ }
5826
+ dispose() {
5827
+ this.skinLoad++;
5828
+ this.capeLoad++;
5829
+ super.dispose();
5244
5830
  }
5245
5831
  //TODO: layer toggles
5246
5832
  getBoxGeometry(part) {
5247
5833
  console.log("slim", this.slim);
5248
5834
  const coordinates = this.slim ? slimSkinTextureCoordinates : classicSkinTextureCoordinates;
5249
5835
  const geometries = this.slim ? slimSkinGeometries : classicSkinGeometries;
5250
- return this._getBoxGeometryFromDimensions(geometries[part], coordinates[part], [64, 64], [this.skinTextureWidth, this.skinTextureHeight]);
5836
+ return this._getBoxGeometryFromDimensions(geometries[part], coordinates[part], [64, 64], [64, 64]);
5251
5837
  }
5252
5838
  };
5253
5839
 
5254
5840
  // src/renderer/MineRenderScene.ts
5255
- var _MineRenderScene = class _MineRenderScene extends Scene2 {
5841
+ var MineRenderScene = class _MineRenderScene extends Scene2 {
5256
5842
  constructor(options) {
5257
5843
  super();
5258
5844
  this.isMineRenderScene = true;
@@ -5261,12 +5847,14 @@ var _MineRenderScene = class _MineRenderScene extends Scene2 {
5261
5847
  this.dirty = true;
5262
5848
  this.observedObjects = /* @__PURE__ */ new Set();
5263
5849
  this.onObjectChange = (event) => {
5264
- var _a;
5265
- if (((_a = event.target) == null ? void 0 : _a.parent) === this) {
5850
+ if (event.target?.parent === this) {
5266
5851
  this.dirty = true;
5267
5852
  }
5268
5853
  };
5269
- this.options = (0, import_ts_deepmerge9.default)({}, _MineRenderScene.DEFAULT_OPTIONS, options ?? {});
5854
+ this.options = (0, import_ts_deepmerge8.default)({}, _MineRenderScene.DEFAULT_OPTIONS, options ?? {});
5855
+ }
5856
+ static {
5857
+ this.DEFAULT_OPTIONS = (0, import_ts_deepmerge8.default)({}, {});
5270
5858
  }
5271
5859
  add(...objects) {
5272
5860
  for (const object of objects) {
@@ -5274,7 +5862,7 @@ var _MineRenderScene = class _MineRenderScene extends Scene2 {
5274
5862
  try {
5275
5863
  super.add(object);
5276
5864
  } finally {
5277
- if ((object == null ? void 0 : object.isObject3D) && object !== this) {
5865
+ if (object?.isObject3D && object !== this) {
5278
5866
  this.updateObjectRegistration(object, object.parent === this);
5279
5867
  }
5280
5868
  }
@@ -5317,15 +5905,21 @@ var _MineRenderScene = class _MineRenderScene extends Scene2 {
5317
5905
  return this.add(...object);
5318
5906
  }
5319
5907
  async addSceneObject(asset, objectSupplier, _options, parent = this) {
5320
- var _a, _b;
5321
5908
  console.log("addSceneObject", asset);
5322
5909
  console.log("parent", parent);
5323
5910
  console.log(_options);
5324
5911
  this.dirty = true;
5325
5912
  const obj = await objectSupplier();
5326
- if (((_a = obj == null ? void 0 : obj.options) == null ? void 0 : _a.instanceMeshes) && asset.key && ((_b = asset.key) == null ? void 0 : _b.assetType) === "models") {
5913
+ if (obj?.options?.instanceMeshes && asset.key && asset.key?.assetType === "models") {
5327
5914
  console.log("instanceMeshes + key");
5328
- const key = asset.key.serialize();
5915
+ let key = asset.key.serialize();
5916
+ if (isModelObject(obj)) {
5917
+ const { displayPosition, uvLockRotation, tints } = obj.options;
5918
+ if (displayPosition) key += `|display:${displayPosition}`;
5919
+ if (uvLockRotation) key += `|uvlock:${uvLockRotation.join(",")}`;
5920
+ const palette = Object.entries(tints ?? {}).sort(([a], [b]) => Number(a) - Number(b));
5921
+ if (palette.length) key += `|tints:${JSON.stringify(palette)}`;
5922
+ }
5329
5923
  return this.instanceManager.getOrCreate(key, async () => {
5330
5924
  obj.scene = this;
5331
5925
  await obj.init();
@@ -5352,10 +5946,10 @@ var _MineRenderScene = class _MineRenderScene extends Scene2 {
5352
5946
  this.dirty = true;
5353
5947
  const obj = new SkinObject(options);
5354
5948
  obj.scene = this;
5355
- await obj.init();
5356
5949
  if (skin) {
5357
- obj.setSkinTexture(skin);
5950
+ await obj.setSkinTexture(skin);
5358
5951
  }
5952
+ await obj.init();
5359
5953
  parent.add(obj);
5360
5954
  return obj;
5361
5955
  }
@@ -5363,15 +5957,13 @@ var _MineRenderScene = class _MineRenderScene extends Scene2 {
5363
5957
  return this.addSceneObject(entity, () => new EntityObject(entity, options), options, parent);
5364
5958
  }
5365
5959
  };
5366
- _MineRenderScene.DEFAULT_OPTIONS = (0, import_ts_deepmerge9.default)({}, {});
5367
- var MineRenderScene = _MineRenderScene;
5368
5960
  function isMineRenderScene(obj) {
5369
5961
  return obj.isMineRenderScene;
5370
5962
  }
5371
5963
 
5372
5964
  // src/renderer/Renderer.ts
5373
- import { AxesHelper as AxesHelper3, GridHelper, LinearEncoding, OrthographicCamera, PCFSoftShadowMap, PerspectiveCamera, Vector3 as Vector38, WebGLRenderer } from "three";
5374
- var import_ts_deepmerge10 = __toESM(require_dist());
5965
+ import { AxesHelper as AxesHelper2, GridHelper, OrthographicCamera, PCFSoftShadowMap, PerspectiveCamera, SRGBColorSpace as SRGBColorSpace4, Vector3 as Vector39, WebGLRenderer } from "three";
5966
+ var import_ts_deepmerge9 = __toESM(require_dist());
5375
5967
  import Stats from "stats.js";
5376
5968
  import { EffectComposer, RenderPass } from "postprocessing";
5377
5969
 
@@ -5379,11 +5971,11 @@ import { EffectComposer, RenderPass } from "postprocessing";
5379
5971
  import {
5380
5972
  EventDispatcher,
5381
5973
  MOUSE,
5382
- Quaternion as Quaternion3,
5974
+ Quaternion as Quaternion4,
5383
5975
  Spherical,
5384
5976
  TOUCH,
5385
5977
  Vector2 as Vector23,
5386
- Vector3 as Vector37
5978
+ Vector3 as Vector38
5387
5979
  } from "three";
5388
5980
  var _changeEvent = { type: "change" };
5389
5981
  var _startEvent = { type: "start" };
@@ -5397,7 +5989,7 @@ var OrbitControls = class extends EventDispatcher {
5397
5989
  this.domElement = domElement;
5398
5990
  this.domElement.style.touchAction = "none";
5399
5991
  this.enabled = true;
5400
- this.target = new Vector37();
5992
+ this.target = new Vector38();
5401
5993
  this.minDistance = 0;
5402
5994
  this.maxDistance = Infinity;
5403
5995
  this.minZoom = 0;
@@ -5449,11 +6041,11 @@ var OrbitControls = class extends EventDispatcher {
5449
6041
  state = STATE.NONE;
5450
6042
  };
5451
6043
  this.update = function() {
5452
- const offset = new Vector37();
5453
- const quat = new Quaternion3().setFromUnitVectors(object.up, new Vector37(0, 1, 0));
6044
+ const offset = new Vector38();
6045
+ const quat = new Quaternion4().setFromUnitVectors(object.up, new Vector38(0, 1, 0));
5454
6046
  const quatInverse = quat.clone().invert();
5455
- const lastPosition = new Vector37();
5456
- const lastQuaternion = new Quaternion3();
6047
+ const lastPosition = new Vector38();
6048
+ const lastQuaternion = new Quaternion4();
5457
6049
  const twoPI = 2 * Math.PI;
5458
6050
  return function update() {
5459
6051
  const position = scope.object.position;
@@ -5542,7 +6134,7 @@ var OrbitControls = class extends EventDispatcher {
5542
6134
  const spherical = new Spherical();
5543
6135
  const sphericalDelta = new Spherical();
5544
6136
  let scale = 1;
5545
- const panOffset = new Vector37();
6137
+ const panOffset = new Vector38();
5546
6138
  let zoomChanged = false;
5547
6139
  const rotateStart = new Vector23();
5548
6140
  const rotateEnd = new Vector23();
@@ -5568,7 +6160,7 @@ var OrbitControls = class extends EventDispatcher {
5568
6160
  sphericalDelta.phi -= angle;
5569
6161
  }
5570
6162
  const panLeft = function() {
5571
- const v = new Vector37();
6163
+ const v = new Vector38();
5572
6164
  return function panLeft2(distance, objectMatrix) {
5573
6165
  v.setFromMatrixColumn(objectMatrix, 0);
5574
6166
  v.multiplyScalar(-distance);
@@ -5576,7 +6168,7 @@ var OrbitControls = class extends EventDispatcher {
5576
6168
  };
5577
6169
  }();
5578
6170
  const panUp = function() {
5579
- const v = new Vector37();
6171
+ const v = new Vector38();
5580
6172
  return function panUp2(distance, objectMatrix) {
5581
6173
  if (scope.screenSpacePanning === true) {
5582
6174
  v.setFromMatrixColumn(objectMatrix, 1);
@@ -5589,7 +6181,7 @@ var OrbitControls = class extends EventDispatcher {
5589
6181
  };
5590
6182
  }();
5591
6183
  const pan = function() {
5592
- const offset = new Vector37();
6184
+ const offset = new Vector38();
5593
6185
  return function pan2(deltaX, deltaY) {
5594
6186
  const element = scope.domElement;
5595
6187
  if (scope.object.isPerspectiveCamera) {
@@ -6026,11 +6618,9 @@ var MapControls = class extends OrbitControls {
6026
6618
  };
6027
6619
 
6028
6620
  // src/renderer/Renderer.ts
6029
- var _Renderer = class _Renderer {
6621
+ var Renderer = class _Renderer {
6030
6622
  constructor(options) {
6031
6623
  this._dirty = true;
6032
- this._animationTimer = void 0;
6033
- this._animationFrame = void 0;
6034
6624
  this._resizeListener = void 0;
6035
6625
  this._disposed = false;
6036
6626
  this._debugHelpers = [];
@@ -6038,9 +6628,9 @@ var _Renderer = class _Renderer {
6038
6628
  this._changeListener = () => {
6039
6629
  this._dirty = true;
6040
6630
  };
6041
- this.options = (0, import_ts_deepmerge10.default)({}, _Renderer.DEFAULT_OPTIONS, options ?? {});
6631
+ this.options = (0, import_ts_deepmerge9.default)({}, _Renderer.DEFAULT_OPTIONS, options ?? {});
6042
6632
  this._animationLoop = this.animate.bind(this);
6043
- this._fpsTimer = this.options.render.fpsLimit > 0 ? 1e3 / this.options.render.fpsLimit : void 0;
6633
+ this._frameInterval = this.options.render.fpsLimit > 0 ? 1e3 / this.options.render.fpsLimit : void 0;
6044
6634
  this._scene = this.createScene();
6045
6635
  this._camera = this.createCamera();
6046
6636
  this._renderer = this.createRenderer();
@@ -6052,6 +6642,45 @@ var _Renderer = class _Renderer {
6052
6642
  this.init();
6053
6643
  this._controls = this.createControls();
6054
6644
  }
6645
+ static {
6646
+ this.DEFAULT_OPTIONS = (0, import_ts_deepmerge9.default)({}, {
6647
+ camera: {
6648
+ type: "perspective",
6649
+ near: 1,
6650
+ far: 5e3,
6651
+ perspective: {
6652
+ aspect: void 0,
6653
+ fov: 50
6654
+ },
6655
+ orthographic: {
6656
+ left: void 0,
6657
+ right: void 0,
6658
+ top: void 0,
6659
+ bottom: void 0
6660
+ },
6661
+ position: new Vector39(50, 50, 50),
6662
+ lookingAt: new Vector39(0, 0, 0)
6663
+ },
6664
+ render: {
6665
+ fpsLimit: 60,
6666
+ stats: false,
6667
+ antialias: true,
6668
+ shade: true,
6669
+ autoResize: true,
6670
+ renderAlways: false
6671
+ },
6672
+ composer: {
6673
+ enabled: true
6674
+ },
6675
+ controls: {
6676
+ enabled: false
6677
+ },
6678
+ debug: {
6679
+ grid: false,
6680
+ axes: false
6681
+ }
6682
+ });
6683
+ }
6055
6684
  //<editor-fold desc="INIT">
6056
6685
  createScene() {
6057
6686
  return new MineRenderScene();
@@ -6087,20 +6716,19 @@ var _Renderer = class _Renderer {
6087
6716
  renderer.setClearColor(0, 0);
6088
6717
  renderer.shadowMap.enabled = true;
6089
6718
  renderer.shadowMap.type = PCFSoftShadowMap;
6090
- renderer.outputEncoding = LinearEncoding;
6719
+ renderer.outputColorSpace = SRGBColorSpace4;
6091
6720
  renderer.setSize(this.viewWidth, this.viewHeight);
6092
6721
  return renderer;
6093
6722
  }
6094
6723
  createComposer() {
6724
+ if (!this.options.composer.enabled) return void 0;
6095
6725
  const composer = new EffectComposer(this.renderer);
6096
- if (!this.options.composer.enabled) return composer;
6097
6726
  composer.setSize(this.viewWidth, this.viewHeight);
6098
6727
  composer.addPass(new RenderPass(this.scene, this.camera));
6099
6728
  return composer;
6100
6729
  }
6101
6730
  createControls() {
6102
- var _a;
6103
- if (!((_a = this.options.controls) == null ? void 0 : _a.enabled)) return void 0;
6731
+ if (!this.options.controls?.enabled) return void 0;
6104
6732
  const position = this.camera.position.clone();
6105
6733
  const controls = new OrbitControls(this.camera, this.renderer.domElement);
6106
6734
  this.camera.position.copy(position);
@@ -6135,7 +6763,7 @@ var _Renderer = class _Renderer {
6135
6763
  this.scene.add(gridHelper3);
6136
6764
  }
6137
6765
  if (this.options.debug.axes) {
6138
- const axesHelper = new AxesHelper3(64);
6766
+ const axesHelper = new AxesHelper2(64);
6139
6767
  this._debugHelpers.push(axesHelper);
6140
6768
  this.scene.add(axesHelper);
6141
6769
  }
@@ -6203,7 +6831,7 @@ var _Renderer = class _Renderer {
6203
6831
  this.camera.updateProjectionMatrix();
6204
6832
  }
6205
6833
  this.renderer.setSize(width, height);
6206
- this.composer.setSize(width, height);
6834
+ this.composer?.setSize(width, height);
6207
6835
  this._dirty = true;
6208
6836
  }
6209
6837
  //<editor-fold desc="RENDER">
@@ -6222,12 +6850,7 @@ var _Renderer = class _Renderer {
6222
6850
  stop() {
6223
6851
  if (this._disposed) return;
6224
6852
  this.renderer.setAnimationLoop(null);
6225
- if (this._animationTimer)
6226
- clearTimeout(this._animationTimer);
6227
- this._animationTimer = void 0;
6228
- if (this._animationFrame)
6229
- cancelAnimationFrame(this._animationFrame);
6230
- this._animationFrame = void 0;
6853
+ this._nextFrameTime = void 0;
6231
6854
  }
6232
6855
  /**
6233
6856
  * Stops rendering and releases owned resources. Repeated calls have no effect, and a disposed
@@ -6235,7 +6858,6 @@ var _Renderer = class _Renderer {
6235
6858
  * controls must be managed by their owners.
6236
6859
  */
6237
6860
  dispose() {
6238
- var _a;
6239
6861
  if (this._disposed) return;
6240
6862
  this.stop();
6241
6863
  this._disposed = true;
@@ -6249,7 +6871,7 @@ var _Renderer = class _Renderer {
6249
6871
  }
6250
6872
  }
6251
6873
  this._eventDispatchers.clear();
6252
- (_a = this._stats) == null ? void 0 : _a.dom.remove();
6874
+ this._stats?.dom.remove();
6253
6875
  this._stats = void 0;
6254
6876
  this.renderer.domElement.remove();
6255
6877
  this._element = void 0;
@@ -6268,26 +6890,30 @@ var _Renderer = class _Renderer {
6268
6890
  }
6269
6891
  }
6270
6892
  this._debugHelpers.length = 0;
6271
- this.composer.dispose();
6893
+ this.composer?.dispose();
6272
6894
  this.renderer.dispose();
6273
6895
  this.renderer.forceContextLoss();
6274
6896
  }
6275
- animate(t) {
6276
- var _a;
6897
+ animate(t = performance.now()) {
6277
6898
  if (this._disposed) return;
6278
- if (this._stats) {
6279
- this._stats.begin();
6280
- }
6281
- if ((_a = this._controls) == null ? void 0 : _a.enabled) {
6899
+ if (this._controls?.enabled) {
6282
6900
  this._controls.update();
6283
6901
  }
6284
6902
  if (this._disposed) return;
6285
- if (this.dirty || this.options.render.renderAlways) {
6286
- if (this.options.composer.enabled) {
6287
- this.composer.render();
6288
- } else {
6289
- this.renderer.render(this.scene, this.camera);
6290
- }
6903
+ if (!this.dirty && !this.options.render.renderAlways) return;
6904
+ const interval = this._frameInterval;
6905
+ if (interval) {
6906
+ const next = this._nextFrameTime;
6907
+ if (next !== void 0 && t + 0.1 < next) return;
6908
+ this._nextFrameTime = next !== void 0 && t - next < interval ? next + interval : t + interval;
6909
+ }
6910
+ if (this._stats) {
6911
+ this._stats.begin();
6912
+ }
6913
+ if (this.options.composer.enabled && this.composer) {
6914
+ this.composer.render();
6915
+ } else {
6916
+ this.renderer.render(this.scene, this.camera);
6291
6917
  }
6292
6918
  this.dirty = false;
6293
6919
  if (this._stats) {
@@ -6303,12 +6929,10 @@ var _Renderer = class _Renderer {
6303
6929
  return this.element === document.body;
6304
6930
  }
6305
6931
  get viewWidth() {
6306
- var _a;
6307
- return this.attachedToBody ? window.innerWidth : ((_a = this.element) == null ? void 0 : _a.offsetWidth) || 0;
6932
+ return this.attachedToBody ? window.innerWidth : this.element?.offsetWidth || 0;
6308
6933
  }
6309
6934
  get viewHeight() {
6310
- var _a;
6311
- return this.attachedToBody ? window.innerHeight : ((_a = this.element) == null ? void 0 : _a.offsetHeight) || 0;
6935
+ return this.attachedToBody ? window.innerHeight : this.element?.offsetHeight || 0;
6312
6936
  }
6313
6937
  get scene() {
6314
6938
  return this._scene;
@@ -6328,44 +6952,6 @@ var _Renderer = class _Renderer {
6328
6952
  }
6329
6953
  ///
6330
6954
  };
6331
- _Renderer.DEFAULT_OPTIONS = (0, import_ts_deepmerge10.default)({}, {
6332
- camera: {
6333
- type: "perspective",
6334
- near: 1,
6335
- far: 5e3,
6336
- perspective: {
6337
- aspect: void 0,
6338
- fov: 50
6339
- },
6340
- orthographic: {
6341
- left: void 0,
6342
- right: void 0,
6343
- top: void 0,
6344
- bottom: void 0
6345
- },
6346
- position: new Vector38(50, 50, 50),
6347
- lookingAt: new Vector38(0, 0, 0)
6348
- },
6349
- render: {
6350
- fpsLimit: 60,
6351
- stats: false,
6352
- antialias: true,
6353
- shade: true,
6354
- autoResize: true,
6355
- renderAlways: false
6356
- },
6357
- composer: {
6358
- enabled: true
6359
- },
6360
- controls: {
6361
- enabled: false
6362
- },
6363
- debug: {
6364
- grid: false,
6365
- axes: false
6366
- }
6367
- });
6368
- var Renderer = _Renderer;
6369
6955
 
6370
6956
  // src/skin/Skins.ts
6371
6957
  var Skins = class {
@@ -6377,7 +6963,7 @@ var Skins = class {
6377
6963
  return this.fromUsername(uuidOrUsername);
6378
6964
  }
6379
6965
  static async fromUuid(uuid) {
6380
- return this.getSkinProxySkinUrl(uuid);
6966
+ return this.getMcProxySkinUrl(uuid);
6381
6967
  }
6382
6968
  static async fromUsername(username) {
6383
6969
  return this.usernameToUuid(username).then((uuid) => {
@@ -6404,7 +6990,7 @@ var Skins = class {
6404
6990
  return this.capeFromUsername(uuidOrUsername);
6405
6991
  }
6406
6992
  static async capeFromUuid(uuid) {
6407
- return this.getSkinProxyCapeUrl(uuid);
6993
+ return this.getMcProxyCapeUrl(uuid);
6408
6994
  }
6409
6995
  static async capeFromUsername(username) {
6410
6996
  return this.usernameToUuid(username).then((uuid) => {
@@ -6414,19 +7000,31 @@ var Skins = class {
6414
7000
  return void 0;
6415
7001
  });
6416
7002
  }
6417
- static async capeFromCapesDev(uuid, capeType = "minecraft") {
6418
- return this.getCapesDev(uuid, capeType).then((cape) => {
6419
- if (cape) {
6420
- return cape == null ? void 0 : cape.stillImageUrl;
6421
- }
6422
- return void 0;
6423
- });
7003
+ /** Returns a full static cape texture for a username or UUID. */
7004
+ static async capeFromCapesDev(player, capeType = "minecraft") {
7005
+ const cape = await this.getCapesDev(player, capeType);
7006
+ return cape?.exists ? cape.imageUrls?.still?.full : void 0;
6424
7007
  }
6425
7008
  //</editor-fold>
6426
7009
  //<editor-fold desc="Helpers">
6427
7010
  static async usernameToUuid(username) {
6428
- return this.usernameToUuidSkinProxy(username);
7011
+ return this.usernameToUuidMcProxy(username);
7012
+ }
7013
+ static async usernameToUuidMcProxy(username) {
7014
+ return Requests.genericRequest({
7015
+ baseURL: "https://mcproxy.dev",
7016
+ url: "/uuid/" + username
7017
+ }).then((res) => {
7018
+ if (res.status === 200) {
7019
+ return res.data["data"]["id"];
7020
+ }
7021
+ return void 0;
7022
+ }).catch((err) => {
7023
+ console.warn(err);
7024
+ return void 0;
7025
+ });
6429
7026
  }
7027
+ /** @deprecated **/
6430
7028
  static async usernameToUuidSkinProxy(username) {
6431
7029
  return Requests.genericRequest({
6432
7030
  baseURL: "https://minecraft-skin-proxy.inventive.workers.dev",
@@ -6479,18 +7077,15 @@ var Skins = class {
6479
7077
  return void 0;
6480
7078
  });
6481
7079
  }
6482
- static async getCapesDev(uuid, type = "all") {
7080
+ static async getCapesDev(player, type = "all") {
6483
7081
  return Requests.genericRequest({
6484
7082
  baseURL: "https://api.capes.dev",
6485
- url: "/load/" + uuid + "/" + type
7083
+ url: "/load/" + encodeURIComponent(player) + "/" + encodeURIComponent(type)
6486
7084
  }).then((res) => {
6487
7085
  if (res.status === 200) {
6488
7086
  return res.data;
6489
7087
  }
6490
7088
  return void 0;
6491
- }).catch((err) => {
6492
- console.warn(err);
6493
- return void 0;
6494
7089
  });
6495
7090
  }
6496
7091
  static getCrafatarSkinUrl(uuid) {
@@ -6505,20 +7100,28 @@ var Skins = class {
6505
7100
  static getMineSkinPlayerSkinUrl(uuid) {
6506
7101
  return "https://mineskin.org/skin/player/" + uuid;
6507
7102
  }
7103
+ /** @deprecated **/
6508
7104
  static getSkinProxySkinUrl(uuid) {
6509
7105
  return "https://minecraft-skin-proxy.inventive.workers.dev/skin/" + uuid;
6510
7106
  }
7107
+ /** @deprecated **/
6511
7108
  static getSkinProxyCapeUrl(uuid) {
6512
7109
  return "https://minecraft-skin-proxy.inventive.workers.dev/cape/" + uuid;
6513
7110
  }
7111
+ static getMcProxySkinUrl(uuid) {
7112
+ return "https://mcproxy.dev/skin/" + uuid;
7113
+ }
7114
+ static getMcProxyCapeUrl(uuid) {
7115
+ return "https://mcproxy.dev/cape/" + uuid;
7116
+ }
6514
7117
  //</editor-fold>
6515
7118
  };
6516
7119
 
6517
7120
  // src/world/Chunk.ts
6518
- import { Box3 as Box32, Object3D as Object3D6, Vector3 as Vector310 } from "three";
7121
+ import { Box3 as Box32, Object3D as Object3D6, Vector3 as Vector311 } from "three";
6519
7122
 
6520
7123
  // src/world/MineRenderWorld.ts
6521
- import { Vector3 as Vector39 } from "three";
7124
+ import { Vector3 as Vector310 } from "three";
6522
7125
  var MineRenderWorld = class {
6523
7126
  constructor(scene) {
6524
7127
  this._size = 4;
@@ -6529,10 +7132,10 @@ var MineRenderWorld = class {
6529
7132
  }
6530
7133
  getBlockAt(posOrX, y, z) {
6531
7134
  if (typeof posOrX == "number") {
6532
- return this.getBlockAt(new Vector39(posOrX, y, z));
7135
+ return this.getBlockAt(new Vector310(posOrX, y, z));
6533
7136
  }
6534
7137
  if (isTripleArray(posOrX)) {
6535
- return this.getBlockAt(new Vector39(posOrX[0], posOrX[1], posOrX[2]));
7138
+ return this.getBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]));
6536
7139
  }
6537
7140
  const chunk = this.getChunkAt(posOrX);
6538
7141
  if (typeof chunk === "undefined") {
@@ -6543,10 +7146,10 @@ var MineRenderWorld = class {
6543
7146
  }
6544
7147
  async setBlockAt(posOrX, yOrBlock, z, block) {
6545
7148
  if (typeof posOrX == "number") {
6546
- return this.setBlockAt(new Vector39(posOrX, yOrBlock, z), block);
7149
+ return this.setBlockAt(new Vector310(posOrX, yOrBlock, z), block);
6547
7150
  }
6548
7151
  if (isTripleArray(posOrX)) {
6549
- return this.setBlockAt(new Vector39(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7152
+ return this.setBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
6550
7153
  }
6551
7154
  this.validatePosBounds(posOrX);
6552
7155
  const chunk = this.getOrCreateChunkAt(posOrX);
@@ -6574,7 +7177,7 @@ var MineRenderWorld = class {
6574
7177
  console.log("CHUNKS", this._chunks);
6575
7178
  for (let chunk of this._chunks.values()) {
6576
7179
  console.log("dispose chunk", chunk);
6577
- await (chunk == null ? void 0 : chunk.dispose());
7180
+ await chunk?.dispose();
6578
7181
  }
6579
7182
  this._chunks.clear();
6580
7183
  }
@@ -6592,14 +7195,14 @@ var MineRenderWorld = class {
6592
7195
  return this._chunks[index];
6593
7196
  }
6594
7197
  static worldToScenePosition(pos) {
6595
- return new Vector39(
7198
+ return new Vector310(
6596
7199
  pos.x * 16,
6597
7200
  pos.y * 16,
6598
7201
  pos.z * 16
6599
7202
  );
6600
7203
  }
6601
7204
  static sceneToWorldPosition(pos) {
6602
- return new Vector39(
7205
+ return new Vector310(
6603
7206
  pos.x / 16,
6604
7207
  pos.y / 16,
6605
7208
  pos.z / 16
@@ -6628,7 +7231,7 @@ var MineRenderWorld = class {
6628
7231
  };
6629
7232
 
6630
7233
  // src/world/Chunk.ts
6631
- var p12 = prefix("Chunk");
7234
+ var p11 = prefix("Chunk");
6632
7235
  var Chunk = class _Chunk {
6633
7236
  constructor(scene, x, y, z) {
6634
7237
  this._blocks = [];
@@ -6641,7 +7244,7 @@ var Chunk = class _Chunk {
6641
7244
  scene.add(anchor);
6642
7245
  anchor.position.set(this.x * 256 + 128 - 8, this.y * 256 + 128 - 8, this.z * 256 + 128 - 8);
6643
7246
  addBox3WireframeToObject(
6644
- new Box32(new Vector310(0, 0, 0), new Vector310(256, 256, 256)),
7247
+ new Box32(new Vector311(0, 0, 0), new Vector311(256, 256, 256)),
6645
7248
  anchor,
6646
7249
  16776960,
6647
7250
  4
@@ -6649,10 +7252,10 @@ var Chunk = class _Chunk {
6649
7252
  }
6650
7253
  getBlockAt(posOrX, y, z) {
6651
7254
  if (typeof posOrX == "number") {
6652
- return this.getBlockAt(new Vector310(posOrX, y, z));
7255
+ return this.getBlockAt(new Vector311(posOrX, y, z));
6653
7256
  }
6654
7257
  if (isTripleArray(posOrX)) {
6655
- return this.getBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]));
7258
+ return this.getBlockAt(new Vector311(posOrX[0], posOrX[1], posOrX[2]));
6656
7259
  }
6657
7260
  const pos = this.worldPosToChunkPos(posOrX);
6658
7261
  const index = _Chunk.chunkPosToBlockIndex(pos);
@@ -6660,10 +7263,10 @@ var Chunk = class _Chunk {
6660
7263
  }
6661
7264
  async setBlockAt(posOrX, yOrBlock, z, block) {
6662
7265
  if (typeof posOrX == "number") {
6663
- return this.setBlockAt(new Vector310(posOrX, yOrBlock, z), block);
7266
+ return this.setBlockAt(new Vector311(posOrX, yOrBlock, z), block);
6664
7267
  }
6665
7268
  if (isTripleArray(posOrX)) {
6666
- return this.setBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7269
+ return this.setBlockAt(new Vector311(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
6667
7270
  }
6668
7271
  const worldPos = posOrX;
6669
7272
  const pos = this.worldPosToChunkPos(worldPos);
@@ -6680,7 +7283,7 @@ var Chunk = class _Chunk {
6680
7283
  const index = _Chunk.chunkPosToBlockIndex(pos);
6681
7284
  const current = this._blocks[index];
6682
7285
  if (typeof current !== "undefined") {
6683
- console.debug(p12, "deleting existing block at", pos, worldPos, index);
7286
+ console.debug(p11, "deleting existing block at", pos, worldPos, index);
6684
7287
  current.object.removeFromScene();
6685
7288
  delete this._blocks[index];
6686
7289
  }
@@ -6709,7 +7312,7 @@ var Chunk = class _Chunk {
6709
7312
  }
6710
7313
  async clear() {
6711
7314
  let promises = [];
6712
- let pos = new Vector310();
7315
+ let pos = new Vector311();
6713
7316
  for (let x = 0; x < 16; x++) {
6714
7317
  for (let z = 0; z < 16; z++) {
6715
7318
  for (let y = 0; y < 16; y++) {
@@ -6728,14 +7331,14 @@ var Chunk = class _Chunk {
6728
7331
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
6729
7332
  }
6730
7333
  worldPosToChunkPos(pos) {
6731
- return new Vector310(
7334
+ return new Vector311(
6732
7335
  pos.x - this.x * 16,
6733
7336
  pos.y - this.y * 16,
6734
7337
  pos.z - this.z * 16
6735
7338
  );
6736
7339
  }
6737
7340
  chunkPosToWorldPos(pos) {
6738
- return new Vector310(
7341
+ return new Vector311(
6739
7342
  this.x * 16 + pos.x,
6740
7343
  this.y * 16 + pos.y,
6741
7344
  this.z * 16 + pos.z
@@ -6746,6 +7349,7 @@ export {
6746
7349
  AXES,
6747
7350
  ArchiveAssetSource,
6748
7351
  AssetKey,
7352
+ AssetLoadError,
6749
7353
  AssetLoader,
6750
7354
  AssetParser,
6751
7355
  AssetSource,
@@ -6755,9 +7359,9 @@ export {
6755
7359
  BUILTIN_GENERATED,
6756
7360
  BasicAssetKey,
6757
7361
  BatchedExecutor,
6758
- BlockEntityModels,
6759
7362
  BlockObject,
6760
7363
  BlockStates,
7364
+ BlockTints,
6761
7365
  BrowserArchiveProxy,
6762
7366
  CUBE_FACES,
6763
7367
  Caching,
@@ -6772,8 +7376,8 @@ export {
6772
7376
  DISPLAY_POSITIONS,
6773
7377
  DefaultBlockStates,
6774
7378
  DisplayPosition,
7379
+ DisplayTransforms,
6775
7380
  Entities,
6776
- EntityModels,
6777
7381
  EntityObject,
6778
7382
  Env,
6779
7383
  Geometries,
@@ -6803,6 +7407,7 @@ export {
6803
7407
  OrbitControls,
6804
7408
  PersistentCache,
6805
7409
  Renderer,
7410
+ RequestError,
6806
7411
  Requests,
6807
7412
  SKIN_PARTS,
6808
7413
  SceneInspector,
@@ -6812,6 +7417,7 @@ export {
6812
7417
  SchematicParser,
6813
7418
  SkinObject,
6814
7419
  SkinPart,
7420
+ SkinTextures,
6815
7421
  Skins,
6816
7422
  StructureParser,
6817
7423
  TextureAtlas,
@@ -6826,10 +7432,10 @@ export {
6826
7432
  addWireframeToObject,
6827
7433
  applyElementRotation,
6828
7434
  applyGenericRotation,
6829
- applyModelPartRotation,
6830
7435
  axisToVec3,
6831
7436
  base64decode,
6832
7437
  base64encode,
7438
+ capeTextureSizes,
6833
7439
  captureException,
6834
7440
  changeEvent,
6835
7441
  clampRotationDegrees,