minerender 2.1.0-alpha.114 → 2.1.0-alpha.116

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bundle.js CHANGED
@@ -4768,7 +4768,7 @@ var MineRender = (() => {
4768
4768
  }
4769
4769
  throw new Error(`THREE.TextureUtils: Unknown texture type ${type}.`);
4770
4770
  }
4771
- var REVISION, MOUSE, TOUCH, CullFaceNone, CullFaceBack, CullFaceFront, PCFShadowMap, PCFSoftShadowMap, VSMShadowMap, FrontSide, BackSide, DoubleSide, NoBlending, NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending, AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation, ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstAlphaFactor, OneMinusDstAlphaFactor, DstColorFactor, OneMinusDstColorFactor, SrcAlphaSaturateFactor, ConstantColorFactor, OneMinusConstantColorFactor, ConstantAlphaFactor, OneMinusConstantAlphaFactor, NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth, MultiplyOperation, MixOperation, AddOperation, NoToneMapping, LinearToneMapping, ReinhardToneMapping, CineonToneMapping, ACESFilmicToneMapping, CustomToneMapping, AgXToneMapping, NeutralToneMapping, UVMapping, CubeReflectionMapping, CubeRefractionMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping, CubeUVReflectionMapping, RepeatWrapping, ClampToEdgeWrapping, MirroredRepeatWrapping, NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearFilter, LinearMipmapNearestFilter, LinearMipmapLinearFilter, UnsignedByteType, ByteType, ShortType, UnsignedShortType, IntType, UnsignedIntType, FloatType, HalfFloatType, UnsignedShort4444Type, UnsignedShort5551Type, UnsignedInt248Type, UnsignedInt5999Type, UnsignedInt101111Type, AlphaFormat, RGBFormat, RGBAFormat, DepthFormat, DepthStencilFormat, RedFormat, RedIntegerFormat, RGFormat, RGIntegerFormat, RGBAIntegerFormat, RGB_S3TC_DXT1_Format, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, RGB_PVRTC_4BPPV1_Format, RGB_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGBA_PVRTC_2BPPV1_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGBA_ETC2_EAC_Format, R11_EAC_Format, SIGNED_R11_EAC_Format, RG11_EAC_Format, SIGNED_RG11_EAC_Format, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGBA_BPTC_Format, RGB_BPTC_SIGNED_Format, RGB_BPTC_UNSIGNED_Format, RED_RGTC1_Format, SIGNED_RED_RGTC1_Format, RED_GREEN_RGTC2_Format, SIGNED_RED_GREEN_RGTC2_Format, InterpolateDiscrete, InterpolateLinear, InterpolateSmooth, InterpolateBezier, ZeroCurvatureEnding, ZeroSlopeEnding, WrapAroundEnding, TrianglesDrawMode, TriangleStripDrawMode, TriangleFanDrawMode, BasicDepthPacking, TangentSpaceNormalMap, ObjectSpaceNormalMap, NoColorSpace, SRGBColorSpace, LinearSRGBColorSpace, LinearTransfer, SRGBTransfer, KeepStencilOp, AlwaysStencilFunc, NeverCompare, LessCompare, EqualCompare, LessEqualCompare, GreaterCompare, NotEqualCompare, GreaterEqualCompare, AlwaysCompare, StaticDrawUsage, GLSL3, WebGLCoordinateSystem, WebGPUCoordinateSystem, _cache, _setConsoleFunction, ReversedDepthFuncs, EventDispatcher, _lut, _seed, DEG2RAD, RAD2DEG, MathUtils, _Vector2, Vector2, Quaternion, _Vector3, Vector3, _vector$c, _quaternion$5, _Matrix3, Matrix3, _m3, LINEAR_REC709_TO_XYZ, XYZ_TO_LINEAR_REC709, ColorManagement, _canvas, ImageUtils, _sourceId, TextureSource, _textureId, _tempVec3, Texture, _Vector4, Vector4, RenderTarget, WebGLRenderTarget, DataArrayTexture, Data3DTexture, _Matrix4, Matrix4, _v1$7, _m1$2, _zero, _one, _x, _y, _z, _matrix$2, _quaternion$4, Euler, Layers, _object3DId, _v1$6, _q1, _m1$1, _target, _position$4, _scale$3, _quaternion$3, _xAxis, _yAxis, _zAxis, _addedEvent, _removedEvent, _childaddedEvent, _childremovedEvent, Object3D, Group, _moveEvent, WebXRController, _colorKeywords, _hslA, _hslB, Color, _color, Scene, _v0$2, _v1$5, _v2$4, _v3$2, _vab, _vac, _vbc, _vap, _vbp, _vcp, _v40, _v41, _v42, Triangle, Box3, _points, _vector$b, _box$4, _v0$1, _v1$4, _v2$3, _f0, _f1, _f2, _center, _extents, _triangleNormal, _testAxis, _vector$a, _vector2$1, _id$2, BufferAttribute, Uint16BufferAttribute, Uint32BufferAttribute, Float32BufferAttribute, _box$3, _v1$3, _v2$2, Sphere, _id$1, _m1, _obj, _offset, _box$2, _boxMorphTargets, _vector$9, BufferGeometry, InterleavedBuffer, _vector$8, InterleavedBufferAttribute, _vector1, _vector2, _normalMatrix, Plane, _materialId, Material, _vector$7, _segCenter, _segDir, _diff, Ray, MeshBasicMaterial, _inverseMatrix$3, _ray$3, _sphere$6, _sphereHitAt, _vA, _vB, _vC, _tempA, _morphA, _intersectionPoint, _intersectionPointWorld, Mesh, DataTexture, InstancedBufferAttribute, _instanceLocalMatrix, _instanceWorldMatrix, _instanceIntersects, _box3, _identity, _mesh$1, _sphere$4, InstancedMesh, _sphere$3, _defaultSpriteCenter, _vector$6, Frustum, LineBasicMaterial, _vStart, _vEnd, _inverseMatrix$1, _ray$1, _sphere$1, _intersectPointOnRay, _intersectPointOnSegment, Line, _start, _end, LineSegments, CompressedTexture, CubeTexture, CanvasTexture, DepthTexture, CubeDepthTexture, ExternalTexture, BoxGeometry, _v0, _v1$1, _normal, _triangle, EdgesGeometry, PlaneGeometry, UniformsUtils, default_vertex, default_fragment, ShaderMaterial, RawShaderMaterial, MeshStandardMaterial, MeshDepthMaterial, MeshDistanceMaterial, Interpolant, CubicInterpolant, LinearInterpolant, DiscreteInterpolant, BezierInterpolant, KeyframeTrack, BooleanKeyframeTrack, ColorKeyframeTrack, NumberKeyframeTrack, QuaternionLinearInterpolant, QuaternionKeyframeTrack, StringKeyframeTrack, VectorKeyframeTrack, LoadingManager, DefaultLoadingManager, Loader, _position$2, _quaternion$2, _scale$2, Camera, _v3$1, _minTarget, _maxTarget, PerspectiveCamera, OrthographicCamera, fov, aspect, CubeCamera, ArrayCamera, _RESERVED_CHARS_RE, _reservedRe, _wordChar, _wordCharOrDot, _directoryRe, _nodeRe, _objectRe, _propertyRe, _trackRe, _supportedObjectNames, Composite, PropertyBinding, _controlInterpolantsResultBuffer, Uniform, _matrix, Raycaster, Spherical, _Matrix2, Matrix2, _vector$4, Box2, GridHelper, AxesHelper;
4771
+ var REVISION, MOUSE, TOUCH, CullFaceNone, CullFaceBack, CullFaceFront, PCFShadowMap, PCFSoftShadowMap, VSMShadowMap, FrontSide, BackSide, DoubleSide, NoBlending, NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending, AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation, ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstAlphaFactor, OneMinusDstAlphaFactor, DstColorFactor, OneMinusDstColorFactor, SrcAlphaSaturateFactor, ConstantColorFactor, OneMinusConstantColorFactor, ConstantAlphaFactor, OneMinusConstantAlphaFactor, NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth, MultiplyOperation, MixOperation, AddOperation, NoToneMapping, LinearToneMapping, ReinhardToneMapping, CineonToneMapping, ACESFilmicToneMapping, CustomToneMapping, AgXToneMapping, NeutralToneMapping, UVMapping, CubeReflectionMapping, CubeRefractionMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping, CubeUVReflectionMapping, RepeatWrapping, ClampToEdgeWrapping, MirroredRepeatWrapping, NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearFilter, LinearMipmapNearestFilter, LinearMipmapLinearFilter, UnsignedByteType, ByteType, ShortType, UnsignedShortType, IntType, UnsignedIntType, FloatType, HalfFloatType, UnsignedShort4444Type, UnsignedShort5551Type, UnsignedInt248Type, UnsignedInt5999Type, UnsignedInt101111Type, AlphaFormat, RGBFormat, RGBAFormat, DepthFormat, DepthStencilFormat, RedFormat, RedIntegerFormat, RGFormat, RGIntegerFormat, RGBAIntegerFormat, RGB_S3TC_DXT1_Format, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, RGB_PVRTC_4BPPV1_Format, RGB_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGBA_PVRTC_2BPPV1_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGBA_ETC2_EAC_Format, R11_EAC_Format, SIGNED_R11_EAC_Format, RG11_EAC_Format, SIGNED_RG11_EAC_Format, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGBA_BPTC_Format, RGB_BPTC_SIGNED_Format, RGB_BPTC_UNSIGNED_Format, RED_RGTC1_Format, SIGNED_RED_RGTC1_Format, RED_GREEN_RGTC2_Format, SIGNED_RED_GREEN_RGTC2_Format, InterpolateDiscrete, InterpolateLinear, InterpolateSmooth, InterpolateBezier, ZeroCurvatureEnding, ZeroSlopeEnding, WrapAroundEnding, TrianglesDrawMode, TriangleStripDrawMode, TriangleFanDrawMode, BasicDepthPacking, TangentSpaceNormalMap, ObjectSpaceNormalMap, NoColorSpace, SRGBColorSpace, LinearSRGBColorSpace, LinearTransfer, SRGBTransfer, KeepStencilOp, AlwaysStencilFunc, NeverCompare, LessCompare, EqualCompare, LessEqualCompare, GreaterCompare, NotEqualCompare, GreaterEqualCompare, AlwaysCompare, StaticDrawUsage, GLSL3, WebGLCoordinateSystem, WebGPUCoordinateSystem, _cache, _setConsoleFunction, ReversedDepthFuncs, EventDispatcher, _lut, _seed, DEG2RAD, RAD2DEG, MathUtils, _Vector2, Vector2, Quaternion, _Vector3, Vector3, _vector$c, _quaternion$5, _Matrix3, Matrix3, _m3, LINEAR_REC709_TO_XYZ, XYZ_TO_LINEAR_REC709, ColorManagement, _canvas, ImageUtils, _sourceId, TextureSource, _textureId, _tempVec3, Texture, _Vector4, Vector4, RenderTarget, WebGLRenderTarget, DataArrayTexture, Data3DTexture, _Matrix4, Matrix4, _v1$7, _m1$2, _zero, _one, _x, _y, _z, _matrix$2, _quaternion$4, Euler, Layers, _object3DId, _v1$6, _q1, _m1$1, _target, _position$4, _scale$3, _quaternion$3, _xAxis, _yAxis, _zAxis, _addedEvent, _removedEvent, _childaddedEvent, _childremovedEvent, Object3D, Group, _moveEvent, WebXRController, _colorKeywords, _hslA, _hslB, Color, _color, Scene, _v0$2, _v1$5, _v2$4, _v3$2, _vab, _vac, _vbc, _vap, _vbp, _vcp, _v40, _v41, _v42, Triangle, Box3, _points, _vector$b, _box$4, _v0$1, _v1$4, _v2$3, _f0, _f1, _f2, _center, _extents, _triangleNormal, _testAxis, _vector$a, _vector2$1, _id$2, BufferAttribute, Uint16BufferAttribute, Uint32BufferAttribute, Float32BufferAttribute, _box$3, _v1$3, _v2$2, Sphere, _id$1, _m1, _obj, _offset, _box$2, _boxMorphTargets, _vector$9, BufferGeometry, InterleavedBuffer, _vector$8, InterleavedBufferAttribute, _vector1, _vector2, _normalMatrix, Plane, _materialId, Material, _vector$7, _segCenter, _segDir, _diff, Ray, MeshBasicMaterial, _inverseMatrix$3, _ray$3, _sphere$6, _sphereHitAt, _vA, _vB, _vC, _tempA, _morphA, _intersectionPoint, _intersectionPointWorld, Mesh, DataTexture, InstancedBufferAttribute, _instanceLocalMatrix, _instanceWorldMatrix, _instanceIntersects, _box3, _identity, _mesh$1, _sphere$4, InstancedMesh, _sphere$3, _defaultSpriteCenter, _vector$6, Frustum, LineBasicMaterial, _vStart, _vEnd, _inverseMatrix$1, _ray$1, _sphere$1, _intersectPointOnRay, _intersectPointOnSegment, Line, _start, _end, LineSegments, CompressedTexture, CubeTexture, CanvasTexture, DepthTexture, CubeDepthTexture, ExternalTexture, BoxGeometry, _v0, _v1$1, _normal, _triangle, EdgesGeometry, PlaneGeometry, UniformsUtils, default_vertex, default_fragment, ShaderMaterial, RawShaderMaterial, MeshStandardMaterial, MeshDepthMaterial, MeshDistanceMaterial, Interpolant, CubicInterpolant, LinearInterpolant, DiscreteInterpolant, BezierInterpolant, KeyframeTrack, BooleanKeyframeTrack, ColorKeyframeTrack, NumberKeyframeTrack, QuaternionLinearInterpolant, QuaternionKeyframeTrack, StringKeyframeTrack, VectorKeyframeTrack, LoadingManager, DefaultLoadingManager, Loader, _position$2, _quaternion$2, _scale$2, Camera, _v3$1, _minTarget, _maxTarget, PerspectiveCamera, OrthographicCamera, fov, aspect, CubeCamera, ArrayCamera, _RESERVED_CHARS_RE, _reservedRe, _wordChar, _wordCharOrDot, _directoryRe, _nodeRe, _objectRe, _propertyRe, _trackRe, _supportedObjectNames, Composite, PropertyBinding, _controlInterpolantsResultBuffer, Uniform, _matrix, Raycaster, Spherical, _Matrix2, Matrix2, _vector$4, Box2, GridHelper, AxesHelper, Controls;
4772
4772
  var init_three_core = __esm({
4773
4773
  "../../node_modules/three/build/three.core.js"() {
4774
4774
  init_dirname();
@@ -20101,8 +20101,8 @@ var MineRender = (() => {
20101
20101
  width *= view.width / fullWidth;
20102
20102
  height *= view.height / fullHeight;
20103
20103
  }
20104
- const skew = this.filmOffset;
20105
- if (skew !== 0) left += near * skew / this.getFilmWidth();
20104
+ const skew2 = this.filmOffset;
20105
+ if (skew2 !== 0) left += near * skew2 / this.getFilmWidth();
20106
20106
  this.projectionMatrix.makePerspective(left, left + width, top, top - height, near, this.far, this.coordinateSystem, this.reversedDepth);
20107
20107
  this.projectionMatrixInverse.copy(this.projectionMatrix).invert();
20108
20108
  }
@@ -21500,6 +21500,53 @@ var MineRender = (() => {
21500
21500
  this.material.dispose();
21501
21501
  }
21502
21502
  };
21503
+ Controls = class extends EventDispatcher {
21504
+ /**
21505
+ * Constructs a new controls instance.
21506
+ *
21507
+ * @param {Object3D} object - The object that is managed by the controls.
21508
+ * @param {?HTMLElement} domElement - The HTML element used for event listeners.
21509
+ */
21510
+ constructor(object4, domElement = null) {
21511
+ super();
21512
+ this.object = object4;
21513
+ this.domElement = domElement;
21514
+ this.enabled = true;
21515
+ this.state = -1;
21516
+ this.keys = {};
21517
+ this.mouseButtons = { LEFT: null, MIDDLE: null, RIGHT: null };
21518
+ this.touches = { ONE: null, TWO: null };
21519
+ }
21520
+ /**
21521
+ * Connects the controls to the DOM. This method has so called "side effects" since
21522
+ * it adds the module's event listeners to the DOM.
21523
+ *
21524
+ * @param {HTMLElement} element - The DOM element to connect to.
21525
+ */
21526
+ connect(element) {
21527
+ if (this.domElement !== null) this.disconnect();
21528
+ this.domElement = element;
21529
+ }
21530
+ /**
21531
+ * Disconnects the controls from the DOM.
21532
+ */
21533
+ disconnect() {
21534
+ }
21535
+ /**
21536
+ * Call this method if you no longer want use to the controls. It frees all internal
21537
+ * resources and removes all event listeners.
21538
+ */
21539
+ dispose() {
21540
+ }
21541
+ /**
21542
+ * Controls should implement this method if they have to update their internal state
21543
+ * per simulation step.
21544
+ *
21545
+ * @param {number} [delta] - The time delta in seconds.
21546
+ */
21547
+ update() {
21548
+ }
21549
+ };
21503
21550
  if (typeof __THREE_DEVTOOLS__ !== "undefined") {
21504
21551
  __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("register", { detail: {
21505
21552
  revision: REVISION
@@ -41442,7 +41489,8 @@ void main() {
41442
41489
  this.fontCache,
41443
41490
  this.bannerPatternCache,
41444
41491
  this.mineRenderDataCache,
41445
- this.usernameUuidCache
41492
+ this.usernameUuidCache,
41493
+ this.biomeCache
41446
41494
  ];
41447
41495
  }
41448
41496
  /** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
@@ -41493,6 +41541,7 @@ void main() {
41493
41541
  _Caching.bannerPatternCache = _Caching.createAssetCache();
41494
41542
  _Caching.mineRenderDataCache = _Caching.createAssetCache();
41495
41543
  _Caching.usernameUuidCache = Caches.builder().expireAfterWrite(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
41544
+ _Caching.biomeCache = _Caching.createAssetCache();
41496
41545
  Caching = _Caching;
41497
41546
  }
41498
41547
  });
@@ -43258,7 +43307,7 @@ ${key}` : key, async () => {
43258
43307
  case "blockStates":
43259
43308
  return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
43260
43309
  case "blockTints":
43261
- return ["constant", "grass", "stem", "redstone"].includes(value.source) && (value.source !== "constant" || Number.isInteger(value.color));
43310
+ return ["constant", "grass", "stem", "redstone"].includes(value.source) && (value.source !== "constant" || Number.isInteger(value.color)) && (value.biome === void 0 || ["grass", "foliage", "dry_foliage", "water"].includes(value.biome));
43262
43311
  case "fluids":
43263
43312
  return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
43264
43313
  case "dyes":
@@ -43414,15 +43463,19 @@ ${key}` : key, async () => {
43414
43463
  Colormaps = class {
43415
43464
  /** Samples the selected resource pack's grass colormap using Minecraft's climate coordinates. */
43416
43465
  static async grassColor(origin, temperature = 0.5, downfall = 1) {
43417
- const key = AssetKey.parse("textures", "minecraft:colormap/grass", origin);
43466
+ return this.getColor("grass", origin, temperature, downfall);
43467
+ }
43468
+ /** Samples a resource-pack colormap at the supplied climate coordinates. */
43469
+ static async getColor(kind, origin, temperature = 0.5, downfall = 1) {
43470
+ const key = AssetKey.parse("textures", `minecraft:colormap/${kind}`, origin);
43418
43471
  const temp = Math.fround(temperature);
43419
43472
  const rain = Math.fround(downfall) * temp;
43420
43473
  const x4 = Math.floor((1 - temp) * 255), y5 = Math.floor((1 - rain) * 255);
43421
43474
  return await Caching.blockTintCache.get(`${key.serialize()}|${x4},${y5}`, async () => {
43422
43475
  const image = await ModelTextures.get(key);
43423
- if (!image) throw new MineRenderError(`Missing grass colormap: ${key.toNamespacedString()}`);
43476
+ if (!image) throw new MineRenderError(`Missing ${kind} colormap: ${key.toNamespacedString()}`);
43424
43477
  if (image.width !== 256 || image.height !== 256) {
43425
- throw new MineRenderError("Grass colormaps must be 256 \xD7 256 pixels");
43478
+ throw new MineRenderError(`${kind} colormaps must be 256 \xD7 256 pixels`);
43426
43479
  }
43427
43480
  const [red, green, blue] = image.data.getImageData(x4, y5, 1, 1).data;
43428
43481
  return red << 16 | green << 8 | blue;
@@ -57484,7 +57537,7 @@ ${key}` : key, async () => {
57484
57537
  var base = 36;
57485
57538
  var tMin = 1;
57486
57539
  var tMax = 26;
57487
- var skew = 38;
57540
+ var skew2 = 38;
57488
57541
  var damp2 = 700;
57489
57542
  var initialBias = 72;
57490
57543
  var initialN = 128;
@@ -57573,7 +57626,7 @@ ${key}` : key, async () => {
57573
57626
  ) {
57574
57627
  delta = floor(delta / baseMinusTMin);
57575
57628
  }
57576
- return floor(k4 + (baseMinusTMin + 1) * delta / (delta + skew));
57629
+ return floor(k4 + (baseMinusTMin + 1) * delta / (delta + skew2));
57577
57630
  };
57578
57631
  var decode2 = function decode3(input) {
57579
57632
  var output = [];
@@ -95332,6 +95385,7 @@ ${shader.fragmentShader}`.replace(
95332
95385
  BannerPatterns: () => BannerPatterns,
95333
95386
  BasicAssetKey: () => BasicAssetKey,
95334
95387
  BatchedExecutor: () => BatchedExecutor,
95388
+ Biomes: () => Biomes,
95335
95389
  BlockEntities: () => BlockEntities,
95336
95390
  BlockObject: () => BlockObject,
95337
95391
  BlockStateResolver: () => BlockStateResolver,
@@ -95365,6 +95419,7 @@ ${shader.fragmentShader}`.replace(
95365
95419
  EntityObject: () => EntityObject,
95366
95420
  Env: () => Env,
95367
95421
  FluidModelObject: () => FluidModelObject,
95422
+ FlyControls: () => FlyControls,
95368
95423
  Fonts: () => Fonts,
95369
95424
  GUI_CONTAINER_LAYOUTS: () => GUI_CONTAINER_LAYOUTS,
95370
95425
  Geometries: () => Geometries,
@@ -95467,6 +95522,7 @@ ${shader.fragmentShader}`.replace(
95467
95522
  isDisposable: () => isDisposable,
95468
95523
  isDoubleArray: () => isDoubleArray,
95469
95524
  isEntityObject: () => isEntityObject,
95525
+ isFlyControls: () => isFlyControls,
95470
95526
  isGuiObject: () => isGuiObject,
95471
95527
  isInstanceManager: () => isInstanceManager,
95472
95528
  isInstanceReference: () => isInstanceReference,
@@ -95792,6 +95848,122 @@ ${shader.fragmentShader}`.replace(
95792
95848
  init_Assets();
95793
95849
  init_BannerPatterns();
95794
95850
 
95851
+ // src/assets/Biomes.ts
95852
+ init_dirname();
95853
+ init_buffer2();
95854
+ init_process2();
95855
+ init_AssetKey();
95856
+ init_AssetLoader();
95857
+ init_AssetParsers();
95858
+ init_Caching();
95859
+ init_MineRenderError();
95860
+ init_Colormaps();
95861
+
95862
+ // src/assets/_biomes/SwampGrassColor.ts
95863
+ init_dirname();
95864
+ init_buffer2();
95865
+ init_process2();
95866
+ var permutation;
95867
+ var gradients = [[1, 1], [-1, 1], [1, -1], [-1, -1], [1, 0], [-1, 0], [1, 0], [-1, 0], [0, 1], [0, -1], [0, 1], [0, -1]];
95868
+ var skew = (Math.sqrt(3) - 1) / 2;
95869
+ var unskew = (3 - Math.sqrt(3)) / 6;
95870
+ function createPermutation() {
95871
+ const multiplier = 0x5deece66dn, mask = (1n << 48n) - 1n;
95872
+ let seed = 2345n ^ multiplier;
95873
+ const next = (bits2) => {
95874
+ seed = seed * multiplier + 11n & mask;
95875
+ return Number(seed >> BigInt(48 - bits2));
95876
+ };
95877
+ for (let i4 = 0; i4 < 3; i4++) {
95878
+ next(26);
95879
+ next(27);
95880
+ }
95881
+ const values = Uint8Array.from({ length: 256 }, (_4, i4) => i4);
95882
+ for (let i4 = 0; i4 < 256; i4++) {
95883
+ const bound = 256 - i4;
95884
+ let offset;
95885
+ if ((bound & bound - 1) === 0) offset = Math.floor(bound * next(31) / 2147483648);
95886
+ else {
95887
+ let bits2;
95888
+ do {
95889
+ bits2 = next(31);
95890
+ offset = bits2 % bound;
95891
+ } while (bits2 - offset + bound - 1 > 2147483647);
95892
+ }
95893
+ const other = i4 + offset;
95894
+ [values[i4], values[other]] = [values[other], values[i4]];
95895
+ }
95896
+ return values;
95897
+ }
95898
+ function swampGrassColor(x4, z4) {
95899
+ const p13 = permutation ?? (permutation = createPermutation());
95900
+ x4 *= 0.0225;
95901
+ z4 *= 0.0225;
95902
+ const s4 = (x4 + z4) * skew;
95903
+ const i4 = Math.floor(x4 + s4), j4 = Math.floor(z4 + s4);
95904
+ const t5 = (i4 + j4) * unskew;
95905
+ const dx = x4 - (i4 - t5), dz = z4 - (j4 - t5);
95906
+ const stepX = dx > dz ? 1 : 0, stepZ = 1 - stepX;
95907
+ const corner = (gx, gz, a4, b4) => {
95908
+ let falloff = 0.5 - a4 * a4 - b4 * b4;
95909
+ if (falloff < 0) return 0;
95910
+ const gradient = gradients[p13[gx + p13[gz & 255] & 255] % 12];
95911
+ falloff *= falloff;
95912
+ return falloff * falloff * (gradient[0] * a4 + gradient[1] * b4);
95913
+ };
95914
+ const noise = 70 * (corner(i4, j4, dx, dz) + corner(i4 + stepX, j4 + stepZ, dx - stepX + unskew, dz - stepZ + unskew) + corner(i4 + 1, j4 + 1, dx - 1 + 2 * unskew, dz - 1 + 2 * unskew));
95915
+ return noise < -0.1 ? 5011004 : 6975545;
95916
+ }
95917
+
95918
+ // src/assets/Biomes.ts
95919
+ var Biomes = class {
95920
+ /** Loads a biome registry entry, or returns `undefined` when no source provides it. */
95921
+ static async get(id, origin) {
95922
+ if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(id)) throw new MineRenderError(`Invalid biome identifier: ${id}`);
95923
+ const [namespace, path] = id.includes(":") ? id.split(":") : ["minecraft", id];
95924
+ const key = new AssetKey(namespace, path, "worldgen", "biome", "data", ".json", origin?.root);
95925
+ return Caching.biomeCache.get(key.serialize(), async () => {
95926
+ const asset2 = await AssetLoader.get(key, "json" /* JSON */);
95927
+ if (asset2 === void 0) return void 0;
95928
+ if (!asset2 || typeof asset2 !== "object" || Array.isArray(asset2) || !asset2.effects || typeof asset2.effects !== "object" || Array.isArray(asset2.effects)) {
95929
+ throw new MineRenderError(`Invalid biome effects: ${id}`);
95930
+ }
95931
+ if (typeof asset2.temperature !== "number" || !Number.isFinite(asset2.temperature) || typeof asset2.downfall !== "number" || !Number.isFinite(asset2.downfall)) {
95932
+ throw new MineRenderError(`Invalid biome climate: ${id}`);
95933
+ }
95934
+ for (const kind of ["grass", "foliage", "dry_foliage", "water"]) {
95935
+ const color3 = asset2.effects[`${kind}_color`];
95936
+ if (color3 !== void 0) this.parseColor(color3, id, kind);
95937
+ }
95938
+ const modifier = asset2.effects.grass_color_modifier;
95939
+ if (modifier !== void 0 && modifier !== "none" && modifier !== "dark_forest" && modifier !== "swamp") {
95940
+ throw new MineRenderError(`Invalid biome grass color modifier: ${id}`);
95941
+ }
95942
+ return asset2;
95943
+ });
95944
+ }
95945
+ /** Returns packed sRGB, or `undefined` when the biome or requested water color is missing. */
95946
+ static async getColor(id, kind, origin, x4 = 0, z4 = 0) {
95947
+ const biome = await this.get(id, origin);
95948
+ if (!biome) return void 0;
95949
+ const rawColor = biome.effects[`${kind}_color`];
95950
+ let color3 = rawColor === void 0 ? void 0 : this.parseColor(rawColor, id, kind);
95951
+ if (kind === "water") return color3;
95952
+ const modifier = kind === "grass" ? biome.effects.grass_color_modifier : void 0;
95953
+ if (modifier === "swamp") return swampGrassColor(x4, z4);
95954
+ if (color3 === void 0) {
95955
+ const clamp2 = (value) => Math.max(0, Math.min(1, Math.fround(value)));
95956
+ color3 = await Colormaps.getColor(kind, origin, clamp2(biome.temperature), clamp2(biome.downfall));
95957
+ }
95958
+ return modifier === "dark_forest" ? (color3 & 16711422) + 2634762 >> 1 : color3;
95959
+ }
95960
+ static parseColor(value, id, kind) {
95961
+ if (typeof value === "string" && /^#[0-9a-fA-F]{6}$/.test(value)) return parseInt(value.slice(1), 16);
95962
+ if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 16777215) return value;
95963
+ throw new MineRenderError(`Invalid biome ${kind} color: ${id}`);
95964
+ }
95965
+ };
95966
+
95795
95967
  // src/assets/BlockEntities.ts
95796
95968
  init_dirname();
95797
95969
  init_buffer2();
@@ -100396,8 +100568,20 @@ property list uchar int vertex_index
100396
100568
  init_Colormaps();
100397
100569
  init_MineRenderData();
100398
100570
  var BlockTints = class {
100399
- /** Resolves block preview colors without biome context; explicit tints take precedence. */
100400
- static async get(key, state, model, tints) {
100571
+ /** Returns a block's versioned biome color source, leaving fixed-color blocks unchanged. */
100572
+ static async biomeSource(key) {
100573
+ const rule = key && (await MineRenderData.get("blockTints", key.root))[key.toNamespacedString()];
100574
+ return rule ? rule.biome ?? (rule.source === "grass" ? "grass" : void 0) : void 0;
100575
+ }
100576
+ /** Resolves one block's biome tint at world block coordinates, without biome blending. */
100577
+ static async getBiomeColor(key, biome, position) {
100578
+ if (!biome) return void 0;
100579
+ const x4 = position?.[0], z4 = position?.[2];
100580
+ const source = await this.biomeSource(key);
100581
+ return source ? Biomes.getColor(biome, source, key, x4, z4) : void 0;
100582
+ }
100583
+ /** Resolves used tint indices from biome or preview colors; explicit tints take precedence. */
100584
+ static async get(key, state, model, tints, biomeColor) {
100401
100585
  if (!key) return tints;
100402
100586
  const candidates = (model.elements ?? []).flatMap((element) => Object.values(element.faces)).some((face) => face?.tintindex !== void 0 && face.tintindex >= 0 && tints?.[face.tintindex] === void 0);
100403
100587
  if (!candidates) return tints;
@@ -100413,8 +100597,12 @@ property list uchar int vertex_index
100413
100597
  }
100414
100598
  }
100415
100599
  if (!indices.size) return tints;
100600
+ const source = rule.biome ?? (rule.source === "grass" ? "grass" : void 0);
100601
+ const biome = source ? typeof biomeColor === "function" ? await biomeColor() : biomeColor : void 0;
100416
100602
  let color3;
100417
- switch (rule.source) {
100603
+ if (biome !== void 0) {
100604
+ color3 = biome;
100605
+ } else switch (rule.source) {
100418
100606
  case "constant":
100419
100607
  color3 = rule.color;
100420
100608
  break;
@@ -100481,6 +100669,7 @@ property list uchar int vertex_index
100481
100669
  this._entityGeneration = 0;
100482
100670
  this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
100483
100671
  this.variantPosition = options?.variantPosition ? [...options.variantPosition] : void 0;
100672
+ this.biomePosition = options?.biomePosition ? [...options.biomePosition] : void 0;
100484
100673
  }
100485
100674
  async init() {
100486
100675
  const root = AssetLoader.ROOT, scope = AssetLoader.persistentScope;
@@ -100673,11 +100862,16 @@ property list uchar int vertex_index
100673
100862
  const { FluidModelObject: FluidModelObject2, sampleFluid: sampleFluid2 } = await Promise.resolve().then(() => (init_FluidModelObject(), FluidModelObject_exports));
100674
100863
  this._fluidSampler = sample;
100675
100864
  const surface = sampleFluid2(kind, (x4, y5, z4) => x4 === 0 && y5 === 0 && z4 === 0 ? { fluid: kind, level: this.fluidLevel } : sample?.(x4, y5, z4) ?? {});
100676
- if (this._fluidKey === surface.key) return;
100865
+ const tint = this.options.tints?.[0] ?? this.waterTint;
100866
+ const fluidKey = `${surface.key}|${tint ?? "default"}`;
100867
+ if (this._fluidKey === fluidKey) return;
100677
100868
  const key = new AssetKey("minecraft", `${kind}/${surface.key}`, "models", "fluid", "assets", ".json", this.blockState.key?.root);
100678
100869
  const replacement = await this.scene.addSceneObject(
100679
100870
  { key },
100680
- () => new FluidModelObject2(kind, surface.sample, this.blockState.key, this.options)
100871
+ () => new FluidModelObject2(kind, surface.sample, this.blockState.key, {
100872
+ ...this.options,
100873
+ tints: tint === void 0 ? this.options.tints : { ...this.options.tints, 0: tint }
100874
+ })
100681
100875
  );
100682
100876
  const previous = this._fluidModel;
100683
100877
  const matrix = previous ? this.getModelMatrix(previous) : new Matrix4().makeTranslation(...this.position.toArray());
@@ -100686,7 +100880,7 @@ property list uchar int vertex_index
100686
100880
  this._models = this._models.filter((model) => model !== previous);
100687
100881
  this._models.push(replacement);
100688
100882
  this._fluidModel = replacement;
100689
- this._fluidKey = surface.key;
100883
+ this._fluidKey = fluidKey;
100690
100884
  this._isInstanced || (this._isInstanced = isInstanceReference(replacement));
100691
100885
  this._instanceCounter = this._isInstanced ? 1 : 0;
100692
100886
  if (previous) this.removeModels([previous]);
@@ -100709,6 +100903,7 @@ property list uchar int vertex_index
100709
100903
  }
100710
100904
  async recreateModels() {
100711
100905
  this.clearModels();
100906
+ this.waterTint = this.fluidKind === "water" && this.options.biome && this.options.tints?.[0] === void 0 ? await Biomes.getColor(this.options.biome, "water", this.blockState.key) : void 0;
100712
100907
  if (resolveBlockFluidState(this.blockState.key, this.state, this._fluidRules)?.renderModel === false) {
100713
100908
  await this.updateFluid(this._fluidSampler);
100714
100909
  return;
@@ -100743,7 +100938,13 @@ property list uchar int vertex_index
100743
100938
  const model = await Models.getMerged(modelKey);
100744
100939
  const options = {
100745
100940
  ...this.options,
100746
- tints: model ? await BlockTints.get(this.blockState.key, this.state, model, this.options.tints) : this.options.tints,
100941
+ tints: model ? await BlockTints.get(
100942
+ this.blockState.key,
100943
+ this.state,
100944
+ model,
100945
+ this.options.tints,
100946
+ () => BlockTints.getBiomeColor(this.blockState.key, this.options.biome, this.biomePosition)
100947
+ ) : this.options.tints,
100747
100948
  uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0,
100748
100949
  cullMask: this.options.displayPosition ? 0 : ModelCulling.toLocalMask(this._cullMask, rotation)
100749
100950
  };
@@ -101366,6 +101567,324 @@ property list uchar int vertex_index
101366
101567
  init_buffer2();
101367
101568
  init_process2();
101368
101569
 
101570
+ // src/renderer/FlyControls.ts
101571
+ init_dirname();
101572
+ init_buffer2();
101573
+ init_process2();
101574
+ init_three_module();
101575
+ var VANILLA_FLY_SPEED = 10.89;
101576
+ var PITCH_LIMIT = Math.PI / 2;
101577
+ var MOVE_EPSILON = 1e-6;
101578
+ var _euler = new Euler(0, 0, 0, "YXZ");
101579
+ var _forward = new Vector3();
101580
+ var _right = new Vector3();
101581
+ var _targetVelocity = new Vector3();
101582
+ var FlyControls = class extends Controls {
101583
+ constructor(camera, domElement) {
101584
+ super(camera, domElement ?? null);
101585
+ this.isFlyControls = true;
101586
+ /** Horizontal and vertical speed in scene units per second; vanilla creative flight by default. */
101587
+ this.movementSpeed = 16 * VANILLA_FLY_SPEED;
101588
+ /** Speed multiplier while the sprint key is held. */
101589
+ this.sprintMultiplier = 2;
101590
+ /** Radians per pixel of pointer movement. */
101591
+ this.lookSpeed = 2e-3;
101592
+ /** Smooth acceleration and deceleration, approximating vanilla flight momentum. */
101593
+ this.enableDamping = true;
101594
+ /** Portion of the remaining velocity change applied per 60 Hz frame (0–1) when damping is enabled. */
101595
+ this.dampingFactor = 0.25;
101596
+ /** Capture the pointer on click so the mouse turns the camera without dragging; dragging works regardless. */
101597
+ this.pointerLock = true;
101598
+ /** Scroll the wheel to change `movementSpeed` within `speedRange`. */
101599
+ this.enableWheelSpeed = true;
101600
+ /** Minimum and maximum `movementSpeed` reachable with the wheel. */
101601
+ this.speedRange = [16, 16 * 500];
101602
+ /** Touch joystick travel in CSS pixels for full speed. */
101603
+ this.joystickRadius = 64;
101604
+ /** Key bindings by `KeyboardEvent.code`. */
101605
+ this.keys = {
101606
+ forward: ["KeyW", "ArrowUp"],
101607
+ back: ["KeyS", "ArrowDown"],
101608
+ left: ["KeyA", "ArrowLeft"],
101609
+ right: ["KeyD", "ArrowRight"],
101610
+ up: ["Space"],
101611
+ down: ["ShiftLeft", "ShiftRight"],
101612
+ sprint: ["ControlLeft", "ControlRight"]
101613
+ };
101614
+ this._pressed = /* @__PURE__ */ new Set();
101615
+ this._velocity = new Vector3();
101616
+ this._lastPosition = new Vector3();
101617
+ this._lastQuaternion = new Quaternion();
101618
+ this._position0 = new Vector3();
101619
+ this._quaternion0 = new Quaternion();
101620
+ this._dragMoved = false;
101621
+ this._locked = false;
101622
+ this._onKeyDown = (event) => this.handleKey(event, true);
101623
+ this._onKeyUp = (event) => this.handleKey(event, false);
101624
+ this._onPointerDown = (event) => this.handlePointerDown(event);
101625
+ this._onPointerMove = (event) => this.handlePointerMove(event);
101626
+ this._onPointerUp = (event) => this.handlePointerUp(event);
101627
+ this._onPointerLockChange = () => this.handlePointerLockChange();
101628
+ this._onContextMenu = (event) => event.preventDefault();
101629
+ this._onWheel = (event) => this.handleWheel(event);
101630
+ this._onBlur = () => this.releaseKeys();
101631
+ this._lastPosition.copy(camera.position);
101632
+ this._lastQuaternion.copy(camera.quaternion);
101633
+ this.saveState();
101634
+ if (domElement) this.connect(domElement);
101635
+ }
101636
+ connect(element) {
101637
+ super.connect(element);
101638
+ const document2 = element.ownerDocument;
101639
+ document2.addEventListener("keydown", this._onKeyDown);
101640
+ document2.addEventListener("keyup", this._onKeyUp);
101641
+ document2.addEventListener("pointerlockchange", this._onPointerLockChange);
101642
+ document2.defaultView?.addEventListener("blur", this._onBlur);
101643
+ element.addEventListener("pointerdown", this._onPointerDown);
101644
+ element.addEventListener("pointermove", this._onPointerMove);
101645
+ element.addEventListener("pointerup", this._onPointerUp);
101646
+ element.addEventListener("pointercancel", this._onPointerUp);
101647
+ element.addEventListener("contextmenu", this._onContextMenu);
101648
+ element.addEventListener("wheel", this._onWheel, { passive: false });
101649
+ element.addEventListener("blur", this._onBlur);
101650
+ element.style.touchAction = "none";
101651
+ if (element.tabIndex < 0) element.tabIndex = 0;
101652
+ }
101653
+ disconnect() {
101654
+ const element = this.domElement;
101655
+ if (!element) return;
101656
+ const document2 = element.ownerDocument;
101657
+ document2.removeEventListener("keydown", this._onKeyDown);
101658
+ document2.removeEventListener("keyup", this._onKeyUp);
101659
+ document2.removeEventListener("pointerlockchange", this._onPointerLockChange);
101660
+ document2.defaultView?.removeEventListener("blur", this._onBlur);
101661
+ element.removeEventListener("pointerdown", this._onPointerDown);
101662
+ element.removeEventListener("pointermove", this._onPointerMove);
101663
+ element.removeEventListener("pointerup", this._onPointerUp);
101664
+ element.removeEventListener("pointercancel", this._onPointerUp);
101665
+ element.removeEventListener("contextmenu", this._onContextMenu);
101666
+ element.removeEventListener("wheel", this._onWheel);
101667
+ element.removeEventListener("blur", this._onBlur);
101668
+ element.style.touchAction = "auto";
101669
+ this.unlock();
101670
+ this._locked = false;
101671
+ this.releaseKeys();
101672
+ this._dragPointer = void 0;
101673
+ this._joystick = void 0;
101674
+ }
101675
+ dispose() {
101676
+ this.disconnect();
101677
+ }
101678
+ /** Whether mouse look currently owns the pointer. */
101679
+ get locked() {
101680
+ return this._locked;
101681
+ }
101682
+ /** Requests pointer capture; browsers only allow this from a user gesture. */
101683
+ lock() {
101684
+ const element = this.domElement;
101685
+ if (!element || this._locked || typeof element.requestPointerLock !== "function") return;
101686
+ try {
101687
+ const result = element.requestPointerLock();
101688
+ if (result instanceof Promise) result.catch(() => void 0);
101689
+ } catch {
101690
+ }
101691
+ }
101692
+ /** Releases a captured pointer. */
101693
+ unlock() {
101694
+ const document2 = this.domElement?.ownerDocument;
101695
+ if (document2?.pointerLockElement && document2.pointerLockElement === this.domElement) {
101696
+ document2.exitPointerLock();
101697
+ }
101698
+ }
101699
+ /** Stores the camera pose that {@link reset} restores. */
101700
+ saveState() {
101701
+ this._position0.copy(this.object.position);
101702
+ this._quaternion0.copy(this.object.quaternion);
101703
+ }
101704
+ /** Restores the saved camera pose and stops movement. */
101705
+ reset() {
101706
+ this.object.position.copy(this._position0);
101707
+ this.object.quaternion.copy(this._quaternion0);
101708
+ this._velocity.set(0, 0, 0);
101709
+ this.notifyChange();
101710
+ }
101711
+ /** Horizontal yaw in radians, where zero looks toward -Z. */
101712
+ get yaw() {
101713
+ return _euler.setFromQuaternion(this.object.quaternion).y;
101714
+ }
101715
+ /** Vertical pitch in radians, positive looking up. */
101716
+ get pitch() {
101717
+ return _euler.setFromQuaternion(this.object.quaternion).x;
101718
+ }
101719
+ /** Turns the camera; pitch is clamped to straight up and down. */
101720
+ setRotation(yaw, pitch) {
101721
+ _euler.set(MathUtils.clamp(pitch, -PITCH_LIMIT, PITCH_LIMIT), yaw, 0);
101722
+ this.object.quaternion.setFromEuler(_euler);
101723
+ this.notifyChange();
101724
+ }
101725
+ /** Points the camera at a position without rolling it. */
101726
+ lookAt(target) {
101727
+ _forward.copy(target).sub(this.object.position);
101728
+ if (_forward.lengthSq() < MOVE_EPSILON) return;
101729
+ _forward.normalize();
101730
+ this.setRotation(Math.atan2(-_forward.x, -_forward.z), Math.asin(MathUtils.clamp(_forward.y, -1, 1)));
101731
+ }
101732
+ /** Whether an action's key is held. */
101733
+ isPressed(action) {
101734
+ return this._pressed.has(action);
101735
+ }
101736
+ /**
101737
+ * Advances movement by `delta` seconds (measured since the previous call when omitted)
101738
+ * and dispatches `change` when the camera moved.
101739
+ */
101740
+ update(delta) {
101741
+ if (delta === void 0) {
101742
+ const now = typeof performance !== "undefined" ? performance.now() : Date.now();
101743
+ delta = this._lastTime === void 0 ? 0 : (now - this._lastTime) / 1e3;
101744
+ this._lastTime = now;
101745
+ }
101746
+ if (!this.enabled) {
101747
+ this._velocity.set(0, 0, 0);
101748
+ return;
101749
+ }
101750
+ if (delta > 0) this.move(delta);
101751
+ this.notifyChange();
101752
+ }
101753
+ move(delta) {
101754
+ const camera = this.object;
101755
+ let forward = (this._pressed.has("forward") ? 1 : 0) - (this._pressed.has("back") ? 1 : 0);
101756
+ let strafe = (this._pressed.has("right") ? 1 : 0) - (this._pressed.has("left") ? 1 : 0);
101757
+ const climb = (this._pressed.has("up") ? 1 : 0) - (this._pressed.has("down") ? 1 : 0);
101758
+ const joystick = this._joystick;
101759
+ if (joystick) {
101760
+ const radius = Math.max(this.joystickRadius, 1);
101761
+ forward = MathUtils.clamp(forward + (joystick.originY - joystick.y) / radius, -1, 1);
101762
+ strafe = MathUtils.clamp(strafe + (joystick.x - joystick.originX) / radius, -1, 1);
101763
+ }
101764
+ _euler.setFromQuaternion(camera.quaternion);
101765
+ const yaw = _euler.y;
101766
+ _forward.set(-Math.sin(yaw), 0, -Math.cos(yaw));
101767
+ _right.set(Math.cos(yaw), 0, -Math.sin(yaw));
101768
+ _targetVelocity.set(0, 0, 0);
101769
+ if (forward || strafe || climb) {
101770
+ _targetVelocity.addScaledVector(_forward, forward).addScaledVector(_right, strafe);
101771
+ if (_targetVelocity.lengthSq() > 1) _targetVelocity.normalize();
101772
+ _targetVelocity.y = climb;
101773
+ const speed = this.movementSpeed * (this._pressed.has("sprint") ? this.sprintMultiplier : 1);
101774
+ _targetVelocity.multiplyScalar(speed);
101775
+ }
101776
+ if (this.enableDamping) {
101777
+ const blend = 1 - Math.pow(1 - MathUtils.clamp(this.dampingFactor, 0, 1), delta * 60);
101778
+ this._velocity.lerp(_targetVelocity, blend);
101779
+ if (_targetVelocity.lengthSq() === 0 && this._velocity.lengthSq() < MOVE_EPSILON) this._velocity.set(0, 0, 0);
101780
+ } else {
101781
+ this._velocity.copy(_targetVelocity);
101782
+ }
101783
+ camera.position.addScaledVector(this._velocity, delta);
101784
+ }
101785
+ notifyChange() {
101786
+ const camera = this.object;
101787
+ if (this._lastPosition.distanceToSquared(camera.position) > MOVE_EPSILON || 8 * (1 - this._lastQuaternion.dot(camera.quaternion)) > MOVE_EPSILON) {
101788
+ this._lastPosition.copy(camera.position);
101789
+ this._lastQuaternion.copy(camera.quaternion);
101790
+ this.dispatchEvent({ type: "change" });
101791
+ }
101792
+ }
101793
+ releaseKeys() {
101794
+ this._pressed.clear();
101795
+ }
101796
+ handleKey(event, down) {
101797
+ const action = this.actionFor(event.code);
101798
+ if (!action) return;
101799
+ const ownsInput = this.enabled && (this._locked || event.target === this.domElement);
101800
+ if (down && !ownsInput) return;
101801
+ const pressed = this._pressed.has(action);
101802
+ if (down) this._pressed.add(action);
101803
+ else this._pressed.delete(action);
101804
+ if (ownsInput || this.enabled && pressed) event.preventDefault();
101805
+ }
101806
+ actionFor(code2) {
101807
+ for (const action in this.keys) {
101808
+ if (this.keys[action].includes(code2)) return action;
101809
+ }
101810
+ return void 0;
101811
+ }
101812
+ handlePointerDown(event) {
101813
+ const element = this.domElement;
101814
+ if (!this.enabled || !element) return;
101815
+ element.focus?.({ preventScroll: true });
101816
+ if (this._locked) return;
101817
+ if (event.pointerType === "touch" && !this._joystick && this.onJoystickSide(event)) {
101818
+ this._joystick = { id: event.pointerId, originX: event.clientX, originY: event.clientY, x: event.clientX, y: event.clientY };
101819
+ element.setPointerCapture?.(event.pointerId);
101820
+ return;
101821
+ }
101822
+ if (this._dragPointer !== void 0) return;
101823
+ if (event.pointerType === "mouse" && event.button !== 0 && event.button !== 2) return;
101824
+ this._dragPointer = event.pointerId;
101825
+ this._dragMoved = false;
101826
+ element.setPointerCapture?.(event.pointerId);
101827
+ }
101828
+ onJoystickSide(event) {
101829
+ const rect = this.domElement?.getBoundingClientRect?.();
101830
+ return !!rect && event.clientX < rect.left + rect.width / 2;
101831
+ }
101832
+ handlePointerMove(event) {
101833
+ if (!this.enabled) return;
101834
+ const joystick = this._joystick;
101835
+ if (joystick && event.pointerId === joystick.id) {
101836
+ joystick.x = event.clientX;
101837
+ joystick.y = event.clientY;
101838
+ return;
101839
+ }
101840
+ if (!this._locked && event.pointerId !== this._dragPointer) return;
101841
+ const dx = event.movementX ?? 0;
101842
+ const dy = event.movementY ?? 0;
101843
+ if (!dx && !dy) return;
101844
+ this._dragMoved = true;
101845
+ this.turn(dx, dy);
101846
+ }
101847
+ handlePointerUp(event) {
101848
+ if (this._joystick?.id === event.pointerId) {
101849
+ this._joystick = void 0;
101850
+ this.domElement?.releasePointerCapture?.(event.pointerId);
101851
+ return;
101852
+ }
101853
+ if (event.pointerId !== this._dragPointer) return;
101854
+ this._dragPointer = void 0;
101855
+ this.domElement?.releasePointerCapture?.(event.pointerId);
101856
+ if (this.enabled && this.pointerLock && !this._dragMoved && event.pointerType === "mouse" && event.button === 0) {
101857
+ this.lock();
101858
+ }
101859
+ }
101860
+ handleWheel(event) {
101861
+ if (!this.enabled || !this.enableWheelSpeed) return;
101862
+ event.preventDefault();
101863
+ const steps = event.deltaMode === 0 ? event.deltaY / 100 : event.deltaY;
101864
+ const [min, max2] = this.speedRange;
101865
+ this.movementSpeed = MathUtils.clamp(this.movementSpeed * Math.pow(1.15, -steps), Math.min(min, max2), Math.max(min, max2));
101866
+ }
101867
+ handlePointerLockChange() {
101868
+ const document2 = this.domElement?.ownerDocument;
101869
+ const locked = !!document2 && document2.pointerLockElement === this.domElement;
101870
+ if (locked === this._locked) return;
101871
+ this._locked = locked;
101872
+ if (!locked) this.releaseKeys();
101873
+ this.dispatchEvent({ type: locked ? "lock" : "unlock" });
101874
+ }
101875
+ turn(dx, dy) {
101876
+ _euler.setFromQuaternion(this.object.quaternion);
101877
+ _euler.y -= dx * this.lookSpeed;
101878
+ _euler.x = MathUtils.clamp(_euler.x - dy * this.lookSpeed, -PITCH_LIMIT, PITCH_LIMIT);
101879
+ _euler.z = 0;
101880
+ this.object.quaternion.setFromEuler(_euler);
101881
+ this.notifyChange();
101882
+ }
101883
+ };
101884
+ function isFlyControls(obj) {
101885
+ return obj?.isFlyControls === true;
101886
+ }
101887
+
101369
101888
  // src/renderer/MineRenderScene.ts
101370
101889
  init_dirname();
101371
101890
  init_buffer2();
@@ -105204,20 +105723,77 @@ gl_FragDepth=readDepth(vUv);
105204
105723
  }
105205
105724
  createControls() {
105206
105725
  if (!this.options.controls?.enabled) return void 0;
105207
- const position = this.camera.position.clone();
105208
- const controls = new OrbitControls(this.camera, this.renderer.domElement);
105209
- this.camera.position.copy(position);
105726
+ return this.createControlsFor(this.options.controls.mode ?? "orbit", this.initialTarget());
105727
+ }
105728
+ initialTarget() {
105210
105729
  const target = this.options.camera.lookingAt;
105211
- if (isVector3(target)) {
105730
+ if (isVector3(target)) return target.clone();
105731
+ if (isTripleArray(target)) return new Vector3(target[0], target[1], target[2]);
105732
+ return new Vector3();
105733
+ }
105734
+ createControlsFor(mode2, target) {
105735
+ let controls;
105736
+ if (mode2 === "fly") {
105737
+ controls = new FlyControls(this.camera, this.renderer.domElement);
105738
+ controls.lookAt(target);
105739
+ controls.saveState();
105740
+ } else {
105741
+ const position = this.camera.position.clone();
105742
+ controls = new OrbitControls(this.camera, this.renderer.domElement);
105743
+ this.camera.position.copy(position);
105212
105744
  controls.target.copy(target);
105213
- } else if (isTripleArray(target)) {
105214
- controls.target.set(target[0], target[1], target[2]);
105745
+ controls.update();
105746
+ controls.saveState();
105215
105747
  }
105216
- controls.update();
105217
- controls.saveState();
105748
+ this._controlsTime = void 0;
105218
105749
  this.registerEventDispatcher(controls);
105219
105750
  return controls;
105220
105751
  }
105752
+ /**
105753
+ * Replaces renderer-owned controls with the selected mode, keeping the camera where it is.
105754
+ * Switching to orbit controls targets the point the camera looks at, at the previous orbit distance.
105755
+ * Returns the new controls, or undefined when controls were disabled at construction or after disposal.
105756
+ */
105757
+ setControlsMode(mode2) {
105758
+ const current = this._controls;
105759
+ if (this._disposed || !current) return void 0;
105760
+ if (this.controlsMode === mode2) return current;
105761
+ let target;
105762
+ if (isFlyControls(current)) {
105763
+ const distance = this._orbitDistance ?? (this.camera.position.distanceTo(this.initialTarget()) || 1);
105764
+ target = new Vector3(0, 0, -distance).applyQuaternion(this.camera.quaternion).add(this.camera.position);
105765
+ } else {
105766
+ target = current.target.clone();
105767
+ this._orbitDistance = this.camera.position.distanceTo(target);
105768
+ }
105769
+ const enabled = current.enabled;
105770
+ this.disposeControls(current);
105771
+ this._controls = this.createControlsFor(mode2, target);
105772
+ this._controls.enabled = enabled;
105773
+ this.dirty = true;
105774
+ return this._controls;
105775
+ }
105776
+ disposeControls(controls) {
105777
+ this._controls = void 0;
105778
+ this.unregisterEventDispatcher(controls);
105779
+ controls.enabled = false;
105780
+ controls.dispose();
105781
+ }
105782
+ /** Advances owned controls; without a frame time, flight movement does not progress. */
105783
+ updateControls(time) {
105784
+ const controls = this._controls;
105785
+ if (!controls?.enabled) {
105786
+ this._controlsTime = void 0;
105787
+ return;
105788
+ }
105789
+ if (!isFlyControls(controls)) {
105790
+ controls.update();
105791
+ return;
105792
+ }
105793
+ const previous = this._controlsTime;
105794
+ if (time !== void 0) this._controlsTime = time;
105795
+ controls.update(time === void 0 || previous === void 0 ? 0 : (time - previous) / 1e3);
105796
+ }
105221
105797
  //</editor-fold>
105222
105798
  init() {
105223
105799
  if (this._disposed) return;
@@ -105294,6 +105870,12 @@ gl_FragDepth=readDepth(vUv);
105294
105870
  events.add(changeEvent2);
105295
105871
  dispatcher.addEventListener(changeEvent2, this._changeListener);
105296
105872
  }
105873
+ unregisterEventDispatcher(dispatcher) {
105874
+ const events = this._eventDispatchers.get(dispatcher);
105875
+ if (!events) return;
105876
+ for (const event of events) dispatcher.removeEventListener(event, this._changeListener);
105877
+ this._eventDispatchers.delete(dispatcher);
105878
+ }
105297
105879
  /** Updates the camera and canvas size. Width and height are in CSS pixels. */
105298
105880
  resize(width, height) {
105299
105881
  if (this._disposed) return;
@@ -105370,6 +105952,7 @@ gl_FragDepth=readDepth(vUv);
105370
105952
  this.renderer.setAnimationLoop(null);
105371
105953
  }
105372
105954
  this._nextFrameTime = void 0;
105955
+ this._controlsTime = void 0;
105373
105956
  for (const subscription of this._frameCallbacks.values()) subscription.previous = void 0;
105374
105957
  }
105375
105958
  /**
@@ -105399,10 +105982,7 @@ gl_FragDepth=readDepth(vUv);
105399
105982
  }
105400
105983
  this._element = void 0;
105401
105984
  if (this._controls) {
105402
- const controls = this._controls;
105403
- this._controls = void 0;
105404
- controls.enabled = false;
105405
- controls.dispose();
105985
+ this.disposeControls(this._controls);
105406
105986
  }
105407
105987
  this.scene.clear();
105408
105988
  for (const helper of this._debugHelpers) {
@@ -105419,9 +105999,7 @@ gl_FragDepth=readDepth(vUv);
105419
105999
  }
105420
106000
  animate(t5 = performance.now()) {
105421
106001
  if (!this._running) return;
105422
- if (this._controls?.enabled) {
105423
- this._controls.update();
105424
- }
106002
+ this.updateControls(t5);
105425
106003
  if (!this._running) return;
105426
106004
  if (!this.dirty && !this.options.render.renderAlways && !this._frameCallbacks.size && !this._videoExporter) return;
105427
106005
  const interval = this._frameInterval;
@@ -105458,7 +106036,7 @@ gl_FragDepth=readDepth(vUv);
105458
106036
  /** Draws a fresh frame without starting the animation loop or invoking frame callbacks. */
105459
106037
  renderOnce() {
105460
106038
  if (this._disposed) throw new Error("Cannot render a disposed renderer");
105461
- if (this._controls?.enabled) this._controls.update();
106039
+ this.updateControls();
105462
106040
  this.drawFrame();
105463
106041
  }
105464
106042
  /**
@@ -105488,7 +106066,7 @@ gl_FragDepth=readDepth(vUv);
105488
106066
  try {
105489
106067
  if (!wasRunning) this.start();
105490
106068
  this._videoExporter = exporter;
105491
- if (this._controls?.enabled) this._controls.update();
106069
+ this.updateControls();
105492
106070
  this.drawFrame();
105493
106071
  } catch (error2) {
105494
106072
  exporter.cancel(error2);
@@ -105539,6 +106117,19 @@ gl_FragDepth=readDepth(vUv);
105539
106117
  get controls() {
105540
106118
  return this._controls;
105541
106119
  }
106120
+ /** Renderer-owned OrbitControls, or undefined in fly mode or without controls. */
106121
+ get orbitControls() {
106122
+ return this._controls && !isFlyControls(this._controls) ? this._controls : void 0;
106123
+ }
106124
+ /** Renderer-owned {@link FlyControls}, or undefined in orbit mode or without controls. */
106125
+ get flyControls() {
106126
+ return isFlyControls(this._controls) ? this._controls : void 0;
106127
+ }
106128
+ /** The active renderer-owned controls mode, or undefined without controls. */
106129
+ get controlsMode() {
106130
+ if (!this._controls) return void 0;
106131
+ return isFlyControls(this._controls) ? "fly" : "orbit";
106132
+ }
105542
106133
  ///
105543
106134
  };
105544
106135
  _Renderer.DEFAULT_OPTIONS = (0, import_ts_deepmerge9.default)({}, {
@@ -105572,7 +106163,8 @@ gl_FragDepth=readDepth(vUv);
105572
106163
  enabled: true
105573
106164
  },
105574
106165
  controls: {
105575
- enabled: false
106166
+ enabled: false,
106167
+ mode: "orbit"
105576
106168
  },
105577
106169
  debug: {
105578
106170
  grid: false,
@@ -106936,24 +107528,48 @@ gl_FragDepth=readDepth(vUv);
106936
107528
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
106937
107529
  }
106938
107530
  /** Returns templates for the matched parts, or `undefined` when the block needs an individual render object. */
106939
- async get(blockState3, properties = {}, position) {
106940
- const variantPosition = position ? [...position] : void 0;
106941
- return (await this.prepareState(blockState3, properties))?.pick(variantPosition);
107531
+ async get(blockState3, properties = {}, position, biome) {
107532
+ const biomePosition = position ? [...position] : void 0;
107533
+ const prepared = await this.state(blockState3, properties);
107534
+ if (!prepared) return void 0;
107535
+ return Promise.all(prepared.groups.map(async (group) => {
107536
+ const variant = BlockStateResolver.choose(group, biomePosition);
107537
+ const choice = prepared.choices.get(variant);
107538
+ const tints = await BlockTints.get(
107539
+ blockState3.key,
107540
+ prepared.state,
107541
+ choice.model,
107542
+ void 0,
107543
+ () => BlockTints.getBiomeColor(blockState3.key, biome, biomePosition)
107544
+ );
107545
+ return this.template(variant, choice, tints);
107546
+ }));
106942
107547
  }
106943
- /** Resolves section templates once, with a synchronous weighted choice for each cell. */
106944
- async prepareState(blockState3, properties = {}) {
107548
+ /** Resolves section templates for one tint palette, with a synchronous weighted choice for each cell. */
107549
+ async prepareState(blockState3, properties = {}, biomeColor) {
107550
+ const prepared = await this.state(blockState3, properties);
107551
+ if (!prepared) return void 0;
107552
+ let color3;
107553
+ const resolveColor = typeof biomeColor === "function" ? () => color3 ?? (color3 = biomeColor()) : biomeColor;
107554
+ const templates = /* @__PURE__ */ new Map();
107555
+ await Promise.all([...prepared.choices].map(async ([variant, choice]) => {
107556
+ const tints = await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, resolveColor);
107557
+ templates.set(variant, await this.template(variant, choice, tints));
107558
+ }));
107559
+ return { pick: (position) => prepared.groups.map((group) => templates.get(BlockStateResolver.choose(group, position))) };
107560
+ }
107561
+ async state(blockState3, properties) {
106945
107562
  let states = this.states.get(blockState3);
106946
107563
  if (!states) this.states.set(blockState3, states = /* @__PURE__ */ new Map());
106947
107564
  const key = JSON.stringify(Object.entries(properties).sort(([a4], [b4]) => a4.localeCompare(b4)));
106948
- const prepared = await cached(states, key, () => this.prepare(blockState3, properties));
106949
- if (!prepared) return void 0;
106950
- return { pick: (position) => prepared.groups.map((group) => prepared.templates.get(BlockStateResolver.choose(group, position))) };
107565
+ return cached(states, key, () => this.prepare(blockState3, properties));
106951
107566
  }
106952
107567
  /** Returns the shared still/flow atlas used by per-block fluid objects. */
106953
107568
  fluidAtlas(kind, root) {
106954
- return cached(this.fluidAtlases, kind, async () => {
107569
+ const key = new AssetKey("minecraft", kind, "models", "fluid", "assets", ".json", root);
107570
+ return cached(this.fluidAtlases, key.serialize(), async () => {
106955
107571
  const atlas = await UVMapper.getAtlas({
106956
- key: new AssetKey("minecraft", kind, "models", "fluid", "assets", ".json", root),
107572
+ key,
106957
107573
  textures: { still: `minecraft:block/${kind}_still`, flow: `minecraft:block/${kind}_flow` },
106958
107574
  elements: []
106959
107575
  });
@@ -106965,64 +107581,67 @@ gl_FragDepth=readDepth(vUv);
106965
107581
  const state = { ...await BlockStateResolver.defaults(blockState3), ...properties };
106966
107582
  const groups = BlockStateResolver.matching(blockState3, state);
106967
107583
  if (!groups.length) return void 0;
106968
- const templates = /* @__PURE__ */ new Map();
107584
+ const prepared = /* @__PURE__ */ new Map();
106969
107585
  for (const group of groups) {
106970
107586
  const choices = Array.isArray(group) ? group : [group];
106971
107587
  if (!choices.length) return void 0;
106972
107588
  for (const variant of choices) {
106973
107589
  if (!variant.model) return void 0;
106974
- const model = await Models.getMerged(AssetKey.parse("models", variant.model));
107590
+ const modelKey = AssetKey.parse("models", variant.model);
107591
+ modelKey.root = blockState3.key?.root;
107592
+ const model = await Models.getMerged(modelKey);
106975
107593
  if (!model?.elements?.length) return void 0;
106976
107594
  const atlas = await UVMapper.getAtlas(model);
106977
107595
  const rotation = BlockStateResolver.rotation(variant);
106978
107596
  if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
106979
- const tints = await BlockTints.get(blockState3.key, state, model);
106980
- let models = this.models.get(model);
106981
- if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
106982
- const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
106983
- const template = await cached(models, key, async () => {
106984
- const object4 = new SectionModel(model, {
106985
- instanceMeshes: false,
106986
- mergeMeshes: true,
106987
- tints,
106988
- uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
106989
- });
106990
- await object4.init();
106991
- let geometry = object4.children[0].geometry;
106992
- geometry.applyMatrix4(new Matrix4().makeRotationFromEuler(rotation));
106993
- object4.clear();
106994
- const keep = [];
106995
- const cullFaces = [];
106996
- for (const element of atlas.model.elements) {
106997
- for (const face of CUBE_FACES) {
106998
- const data = element.faces[face];
106999
- keep.push(data !== void 0);
107000
- if (!data) continue;
107001
- const local = CUBE_FACES.findIndex((name2) => name2 === data.cullface);
107002
- if (local < 0) {
107003
- cullFaces.push(0);
107004
- continue;
107005
- }
107006
- const normal = new Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
107007
- const world = CUBE_FACE_OFFSETS.findIndex(([x4, y5, z4]) => Math.abs(normal.x - x4) < 1e-6 && Math.abs(normal.y - y5) < 1e-6 && Math.abs(normal.z - z4) < 1e-6);
107008
- cullFaces.push(world < 0 ? 0 : 1 << world);
107009
- }
107010
- }
107011
- geometry = compactQuads(geometry, keep);
107012
- const template2 = {
107013
- geometry,
107014
- atlas,
107015
- cullFaces: new Uint8Array(cullFaces),
107016
- occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
107017
- layer: atlas.hasTranslucency ? 1 : 0
107018
- };
107019
- this.templates.add(template2);
107020
- return template2;
107021
- });
107022
- templates.set(variant, template);
107597
+ prepared.set(variant, { model, atlas, rotation });
107023
107598
  }
107024
107599
  }
107025
- return { groups, templates };
107600
+ return { state, groups, choices: prepared };
107601
+ }
107602
+ async template(variant, { model, atlas, rotation }, tints) {
107603
+ let models = this.models.get(model);
107604
+ if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
107605
+ const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
107606
+ return cached(models, key, async () => {
107607
+ const object4 = new SectionModel(model, {
107608
+ instanceMeshes: false,
107609
+ mergeMeshes: true,
107610
+ tints,
107611
+ uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
107612
+ });
107613
+ await object4.init();
107614
+ let geometry = object4.children[0].geometry;
107615
+ geometry.applyMatrix4(new Matrix4().makeRotationFromEuler(rotation));
107616
+ object4.clear();
107617
+ const keep = [];
107618
+ const cullFaces = [];
107619
+ for (const element of atlas.model.elements) {
107620
+ for (const face of CUBE_FACES) {
107621
+ const data = element.faces[face];
107622
+ keep.push(data !== void 0);
107623
+ if (!data) continue;
107624
+ const local = CUBE_FACES.findIndex((name2) => name2 === data.cullface);
107625
+ if (local < 0) {
107626
+ cullFaces.push(0);
107627
+ continue;
107628
+ }
107629
+ const normal = new Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
107630
+ const world = CUBE_FACE_OFFSETS.findIndex(([x4, y5, z4]) => Math.abs(normal.x - x4) < 1e-6 && Math.abs(normal.y - y5) < 1e-6 && Math.abs(normal.z - z4) < 1e-6);
107631
+ cullFaces.push(world < 0 ? 0 : 1 << world);
107632
+ }
107633
+ }
107634
+ geometry = compactQuads(geometry, keep);
107635
+ const template = {
107636
+ geometry,
107637
+ atlas,
107638
+ cullFaces: new Uint8Array(cullFaces),
107639
+ occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
107640
+ layer: atlas.hasTranslucency ? 1 : 0
107641
+ };
107642
+ this.templates.add(template);
107643
+ return template;
107644
+ });
107026
107645
  }
107027
107646
  /** Disposes prepared geometry and clears this world's template caches. */
107028
107647
  clear() {
@@ -107358,7 +107977,6 @@ gl_FragDepth=readDepth(vUv);
107358
107977
  }
107359
107978
  try {
107360
107979
  await this.clearChunkColumn(chunk.x, chunk.z);
107361
- let sliceStart = performance.now();
107362
107980
  for (const section of chunk.sections) {
107363
107981
  if (section.biomes) {
107364
107982
  const position = new Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
@@ -107372,6 +107990,9 @@ gl_FragDepth=readDepth(vUv);
107372
107990
  if (!column) this.biomes.set(key, column = /* @__PURE__ */ new Map());
107373
107991
  column.set(section.y, copy);
107374
107992
  }
107993
+ }
107994
+ let sliceStart = performance.now();
107995
+ for (const section of chunk.sections) {
107375
107996
  if (!executor) {
107376
107997
  if (!occupiedSections.has(section.y)) continue;
107377
107998
  const position = new Vector3(chunk.x * 16, section.y * 16, chunk.z * 16);
@@ -107536,18 +108157,34 @@ gl_FragDepth=readDepth(vUv);
107536
108157
  const key = this.worldPosToChunkKey(pos);
107537
108158
  let chunk = this._chunks.get(key);
107538
108159
  if (typeof chunk === "undefined") {
108160
+ const x4 = Math.floor(pos.x / 16), y5 = Math.floor(pos.y / 16), z4 = Math.floor(pos.z / 16);
107539
108161
  chunk = new Chunk(
107540
108162
  this.scene,
107541
- Math.floor(pos.x / 16),
107542
- Math.floor(pos.y / 16),
107543
- Math.floor(pos.z / 16),
108163
+ x4,
108164
+ y5,
108165
+ z4,
107544
108166
  (positions) => this.updateCulling(positions),
107545
108167
  this.sectionModels,
107546
- (x4, y5, z4) => {
107547
- const cx = Math.floor(x4 / 16), cy = Math.floor(y5 / 16), cz = Math.floor(z4 / 16);
108168
+ (x5, y6, z5) => {
108169
+ const cx = Math.floor(x5 / 16), cy = Math.floor(y6 / 16), cz = Math.floor(z5 / 16);
107548
108170
  const section = this._chunks.get(`${cx}_${cy}_${cz}`);
107549
- const index = (y5 - cy * 16) * 256 + (z4 - cz * 16) * 16 + x4 - cx * 16;
108171
+ const index = (y6 - cy * 16) * 256 + (z5 - cz * 16) * 16 + x5 - cx * 16;
107550
108172
  return (section?.fluidByteIndex(index) ?? 0) | (section?.isOccludingIndex(index) ? 64 : 0);
108173
+ },
108174
+ (x5, y6, z5) => this.getBiomeAt(x5, y6, z5),
108175
+ async (biomes) => {
108176
+ if (this._chunks.get(key) !== chunk) return;
108177
+ const columnKey = `${x4}_${z4}`;
108178
+ let column = this.biomes.get(columnKey);
108179
+ if (biomes) {
108180
+ if (!column) this.biomes.set(columnKey, column = /* @__PURE__ */ new Map());
108181
+ column.set(y5, biomes);
108182
+ } else {
108183
+ column?.delete(y5);
108184
+ if (!column?.size) this.biomes.delete(columnKey);
108185
+ }
108186
+ await chunk.refreshBiomes();
108187
+ await this._chunks.get(`${x4}_${y5 + 1}_${z4}`)?.refreshBiomes();
107551
108188
  }
107552
108189
  );
107553
108190
  this._chunks.set(key, chunk);
@@ -107608,6 +108245,7 @@ gl_FragDepth=readDepth(vUv);
107608
108245
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
107609
108246
  if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
107610
108247
  if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
108248
+ if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
107611
108249
  for (const template of templateData) {
107612
108250
  const { quads } = template;
107613
108251
  if (template.positions.length !== quads * 12 || template.normals.length !== quads * 12 || template.uvs.length !== quads * 8 || template.uvBounds.length !== quads * 16 || template.colors.length !== quads * 12 || template.indices.length !== quads * 6 || template.cullFaces.length !== quads) {
@@ -107685,7 +108323,9 @@ gl_FragDepth=readDepth(vUv);
107685
108323
  height
107686
108324
  });
107687
108325
  }
107688
- fluidPages.get(fluid).fluids.push({ quads, x: x4, y: y5, z: z4, data });
108326
+ const tintIndex = (x4 + z4 * 16 + y5 * 256) * 3;
108327
+ const tint = fluid === "water" && fluids.waterTints ? fluids.waterTints.subarray(tintIndex, tintIndex + 3) : data.tint;
108328
+ fluidPages.get(fluid).fluids.push({ quads, x: x4, y: y5, z: z4, data, tint });
107689
108329
  }
107690
108330
  pages.push(...[...fluidPages.values()].sort((a4, b4) => Number(atlases[a4.placements[0].atlas].animated) - Number(atlases[b4.placements[0].atlas].animated) || a4.placements[0].atlas - b4.placements[0].atlas));
107691
108331
  }
@@ -107734,7 +108374,7 @@ gl_FragDepth=readDepth(vUv);
107734
108374
  for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
107735
108375
  }
107736
108376
  }
107737
- for (const { quads, x: x4, y: y5, z: z4, data } of page.fluids) {
108377
+ for (const { quads, x: x4, y: y5, z: z4, data, tint } of page.fluids) {
107738
108378
  const atlas = atlases[data.atlas];
107739
108379
  const placement = page.placements[0];
107740
108380
  const mapU = (u4) => (placement.x + u4 * atlas.width) / page.width;
@@ -107758,7 +108398,7 @@ gl_FragDepth=readDepth(vUv);
107758
108398
  mapU((rect[0] + rect[2]) / atlas.width),
107759
108399
  mapV(1 - rect[1] / atlas.height)
107760
108400
  ], vertex2 * 4);
107761
- colors.set(data.tint, vertex2 * 3);
108401
+ colors.set(tint, vertex2 * 3);
107762
108402
  }
107763
108403
  outputIndices.set([start, start + 1, start + 2, start, start + 2, start + 3], indexOffset);
107764
108404
  indexOffset += 6;
@@ -107902,7 +108542,7 @@ gl_FragDepth=readDepth(vUv);
107902
108542
  input.cullMasks[i4] = cullMask;
107903
108543
  }
107904
108544
  if (fluids) {
107905
- input.fluids = { cells: fluids.cells };
108545
+ input.fluids = { cells: fluids.cells, waterTints: fluids.waterTints };
107906
108546
  for (const kind of ["water", "lava"]) {
107907
108547
  const atlas = fluids[kind];
107908
108548
  if (!atlas) continue;
@@ -108003,11 +108643,14 @@ gl_FragDepth=readDepth(vUv);
108003
108643
  init_FluidGeometry();
108004
108644
  init_FluidQuads();
108005
108645
  init_CubeFace();
108646
+ var WATER_TINT2 = new Color(4159204).toArray();
108006
108647
  var Chunk = class _Chunk {
108007
- constructor(scene, x4, y5, z4, onBlocksChanged, sectionModels, neighborCell) {
108648
+ constructor(scene, x4, y5, z4, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged) {
108008
108649
  this.onBlocksChanged = onBlocksChanged;
108009
108650
  this.sectionModels = sectionModels;
108010
108651
  this.neighborCell = neighborCell;
108652
+ this.biomeAt = biomeAt;
108653
+ this.onBiomesChanged = onBiomesChanged;
108011
108654
  this.data = new ChunkData();
108012
108655
  this.renderedBlocks = /* @__PURE__ */ new Map();
108013
108656
  this.sectionBlocks = /* @__PURE__ */ new Map();
@@ -108032,6 +108675,56 @@ gl_FragDepth=readDepth(vUv);
108032
108675
  const index = _Chunk.chunkPosToBlockIndex(pos);
108033
108676
  return this.renderedBlocks.get(index);
108034
108677
  }
108678
+ getBiomeAt(posOrX, y5, z4) {
108679
+ if (typeof posOrX === "number") return this.getBiomeAt(new Vector3(posOrX, y5, z4));
108680
+ if (isTripleArray(posOrX)) return this.getBiomeAt(new Vector3(...posOrX));
108681
+ const pos = this.worldPosToChunkPos(posOrX);
108682
+ _Chunk.chunkPosToBlockIndex(pos);
108683
+ return this.biomeAt ? this.biomeAt(posOrX.x, posOrX.y, posOrX.z) : this.biomes?.[Math.floor(pos.x / 4) + Math.floor(pos.z / 4) * 4 + Math.floor(pos.y / 4) * 16];
108684
+ }
108685
+ /** Copies 64 biome IDs in x + z * 4 + y * 16 order and refreshes placed blocks. Omit the array to clear biome data. */
108686
+ setBiomes(biomes) {
108687
+ const copy = biomes ? [...biomes] : void 0;
108688
+ if (copy && (copy.length !== 64 || !copy.every((id) => typeof id === "string" && id.length > 0))) {
108689
+ throw new RangeError("Section biomes must contain 64 nonempty biome IDs");
108690
+ }
108691
+ if (this.onBiomesChanged) return this.onBiomesChanged(copy);
108692
+ this.biomes = copy;
108693
+ return this.refreshBiomes();
108694
+ }
108695
+ /** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
108696
+ async refreshBiomes() {
108697
+ const affected = /* @__PURE__ */ new Map();
108698
+ const blocks = [];
108699
+ for (const index of this.renderedBlocks.keys()) {
108700
+ const block = this.data.get(index);
108701
+ const stateKey = ChunkData.paletteKey(block);
108702
+ let matches = affected.get(stateKey);
108703
+ if (!matches) {
108704
+ const key = AssetKey.parse("blockstates", block.type);
108705
+ matches = BlockStates.get(key).then(async (state) => {
108706
+ const origin = state?.key ?? key;
108707
+ const [source, fluid] = await Promise.all([BlockTints.biomeSource(origin), getBlockFluidState(origin, block.properties)]);
108708
+ return !!source || fluid?.kind === "water";
108709
+ });
108710
+ affected.set(stateKey, matches);
108711
+ }
108712
+ if (await matches) blocks.push({ index, block });
108713
+ }
108714
+ if (!blocks.length) return;
108715
+ const hidden = new Set(this.hiddenBlocks);
108716
+ try {
108717
+ await this.placeBlocks(blocks);
108718
+ } finally {
108719
+ for (const index of hidden) {
108720
+ this.hiddenBlocks.add(index);
108721
+ this.renderedBlocks.get(index)?.object?.setVisible(false);
108722
+ }
108723
+ const positions = blocks.map(({ index }) => new Vector3(...this.positionAt(index)));
108724
+ if (this.onBlocksChanged) await this.onBlocksChanged(positions);
108725
+ else await this.rebuildSectionMesh();
108726
+ }
108727
+ }
108035
108728
  async setBlockAt(posOrX, yOrBlock, z4, block) {
108036
108729
  if (typeof posOrX == "number") {
108037
108730
  return this.setBlockAt(new Vector3(posOrX, yOrBlock, z4), block);
@@ -108070,14 +108763,15 @@ gl_FragDepth=readDepth(vUv);
108070
108763
  nbt: block.nbt === void 0 ? void 0 : structuredClone(block.nbt)
108071
108764
  } : void 0
108072
108765
  }));
108073
- const states = new Map(placements.filter(({ key }) => key !== void 0).map(({ key, block }) => [key, block]));
108074
- const settled = await Promise.allSettled([...states.values()].map(async (stored) => {
108075
- const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
108076
- const perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
108077
- const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
108078
- const prepared = perBlock || fluid?.renderModel === false ? void 0 : await this.sectionModels.prepareState(blockState3, stored.properties);
108079
- return { blockState: blockState3, perBlock, fluid, prepared, stored };
108080
- }));
108766
+ const states = /* @__PURE__ */ new Map();
108767
+ for (const { index, key, block } of placements) {
108768
+ if (key === void 0) continue;
108769
+ let state = states.get(key);
108770
+ if (!state) states.set(key, state = { stored: block, indices: [] });
108771
+ state.indices.push(index);
108772
+ }
108773
+ const biomes = /* @__PURE__ */ new Map();
108774
+ const settled = await Promise.allSettled([...states.values()].map(({ stored, indices }) => this.resolveState(stored, indices, biomes)));
108081
108775
  const resolutions = new Map([...states.keys()].map((key, i4) => [key, settled[i4]]));
108082
108776
  const positions = [];
108083
108777
  let failed = false, failure;
@@ -108101,7 +108795,8 @@ gl_FragDepth=readDepth(vUv);
108101
108795
  if (!readBlock) continue;
108102
108796
  const resolved = resolutions.get(key);
108103
108797
  if (resolved.status === "rejected") throw resolved.reason;
108104
- const { blockState: blockState3, perBlock, fluid, prepared, stored } = resolved.value;
108798
+ const { blockState: blockState3, perBlock, fluid, prepared, contexts, stored } = resolved.value;
108799
+ const context = contexts.get(index);
108105
108800
  if (!blockState3) {
108106
108801
  this.data.assign(index, 0);
108107
108802
  continue;
@@ -108109,9 +108804,10 @@ gl_FragDepth=readDepth(vUv);
108109
108804
  const variantPosition = worldPos.toArray();
108110
108805
  if (fluid) {
108111
108806
  this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
108807
+ if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
108112
108808
  this.meshDirty = true;
108113
108809
  }
108114
- const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
108810
+ const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
108115
108811
  if (templates) {
108116
108812
  this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
108117
108813
  this.meshDirty = true;
@@ -108121,7 +108817,9 @@ gl_FragDepth=readDepth(vUv);
108121
108817
  instanceMeshes: true,
108122
108818
  maxInstanceCount: 2e3,
108123
108819
  initialState: stored.properties,
108124
- variantPosition
108820
+ variantPosition,
108821
+ biome: context?.biome,
108822
+ biomePosition: this.biomePosition(index, blockState3, stored)
108125
108823
  });
108126
108824
  object4.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
108127
108825
  }
@@ -108144,24 +108842,21 @@ gl_FragDepth=readDepth(vUv);
108144
108842
  for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
108145
108843
  this.sectionBlocks.clear();
108146
108844
  this.fluidCells.fill(0);
108845
+ this.waterTints = void 0;
108147
108846
  this.renderedBlocks.clear();
108148
108847
  this.hiddenBlocks.clear();
108149
108848
  this.meshDirty = true;
108150
108849
  this.data.copyFrom(data);
108151
- const ids = /* @__PURE__ */ new Set();
108850
+ const ids = /* @__PURE__ */ new Map();
108152
108851
  for (let index = 0; index < 4096; index++) {
108153
108852
  const id = this.data.idAt(index);
108154
- if (id) ids.add(id);
108155
- }
108156
- const palette = [...ids];
108157
- const settled = await Promise.allSettled(palette.map(async (id) => {
108158
- const state = this.data.stateAt(id);
108159
- const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", state.type));
108160
- const perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
108161
- const fluid = await getBlockFluidState(blockState3?.key, state.properties);
108162
- const prepared = perBlock || fluid?.renderModel === false ? void 0 : await this.sectionModels.prepareState(blockState3, state.properties);
108163
- return { blockState: blockState3, perBlock, fluid, prepared };
108164
- }));
108853
+ if (!id) continue;
108854
+ let indices = ids.get(id);
108855
+ if (!indices) ids.set(id, indices = []);
108856
+ indices.push(index);
108857
+ }
108858
+ const palette = [...ids.keys()], biomes = /* @__PURE__ */ new Map();
108859
+ const settled = await Promise.allSettled(palette.map((id) => this.resolveState(this.data.stateAt(id), ids.get(id), biomes)));
108165
108860
  const resolutions = new Map(palette.map((id, i4) => [id, settled[i4]]));
108166
108861
  const perBlocks = [];
108167
108862
  let failed = false, failure;
@@ -108171,24 +108866,28 @@ gl_FragDepth=readDepth(vUv);
108171
108866
  try {
108172
108867
  const resolved = resolutions.get(id);
108173
108868
  if (resolved.status === "rejected") throw resolved.reason;
108174
- const { blockState: blockState3, perBlock, fluid, prepared } = resolved.value;
108869
+ const { blockState: blockState3, perBlock, fluid, prepared, contexts } = resolved.value;
108870
+ const context = contexts.get(index);
108175
108871
  if (!blockState3) {
108176
108872
  this.data.assign(index, 0);
108177
108873
  this.fluidCells[index] = 0;
108178
108874
  continue;
108179
108875
  }
108180
- if (fluid) this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
108876
+ if (fluid) {
108877
+ this.fluidCells[index] = (fluid.kind === "water" ? 16 : 32) | Math.min(fluid.level, 8);
108878
+ if (fluid.kind === "water") this.setWaterTint(index, context?.waterTint);
108879
+ }
108181
108880
  const variantPosition = [
108182
108881
  this.x * 16 + index % 16,
108183
108882
  this.y * 16 + Math.floor(index / 256),
108184
108883
  this.z * 16 + Math.floor(index / 16) % 16
108185
108884
  ];
108186
- const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : prepared?.pick(variantPosition);
108885
+ const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
108187
108886
  if (templates) {
108188
108887
  this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
108189
108888
  this.meshDirty = true;
108190
108889
  } else {
108191
- perBlocks.push({ index, blockState: blockState3 });
108890
+ perBlocks.push({ index, blockState: blockState3, biome: context?.biome });
108192
108891
  }
108193
108892
  const readBlock = this.data.snapshot(index);
108194
108893
  this.renderedBlocks.set(index, { get block() {
@@ -108201,7 +108900,7 @@ gl_FragDepth=readDepth(vUv);
108201
108900
  failed = true;
108202
108901
  }
108203
108902
  }
108204
- for (const { index, blockState: blockState3 } of perBlocks) {
108903
+ for (const { index, blockState: blockState3, biome } of perBlocks) {
108205
108904
  let object4;
108206
108905
  try {
108207
108906
  object4 = await this.scene.addBlock(blockState3, {
@@ -108209,11 +108908,9 @@ gl_FragDepth=readDepth(vUv);
108209
108908
  instanceMeshes: true,
108210
108909
  maxInstanceCount: 2e3,
108211
108910
  initialState: this.data.stateAt(this.data.idAt(index)).properties,
108212
- variantPosition: [
108213
- this.x * 16 + index % 16,
108214
- this.y * 16 + Math.floor(index / 256),
108215
- this.z * 16 + Math.floor(index / 16) % 16
108216
- ]
108911
+ variantPosition: this.positionAt(index),
108912
+ biome,
108913
+ biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
108217
108914
  });
108218
108915
  object4.setPosition(new Vector3(
108219
108916
  this.x * 256 + index % 16 * 16,
@@ -108232,6 +108929,68 @@ gl_FragDepth=readDepth(vUv);
108232
108929
  }
108233
108930
  if (failed) throw failure;
108234
108931
  }
108932
+ positionAt(index) {
108933
+ return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
108934
+ }
108935
+ biomePosition(index, blockState3, stored) {
108936
+ const position = this.positionAt(index), name2 = blockState3.key?.toNamespacedString();
108937
+ if (stored.properties?.half === "upper" && (name2 === "minecraft:tall_grass" || name2 === "minecraft:large_fern")) position[1]--;
108938
+ return position;
108939
+ }
108940
+ async resolveState(stored, indices, biomes) {
108941
+ const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
108942
+ const perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
108943
+ const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
108944
+ const hasBiomes = this.getBiomeAt(this.x * 16, this.y * 16, this.z * 16) !== void 0 || this.biomeAt?.(this.x * 16, this.y * 16 - 1, this.z * 16) !== void 0;
108945
+ const source = hasBiomes && blockState3 ? await BlockTints.biomeSource(blockState3.key) : void 0;
108946
+ const prepare = (color3) => perBlock || fluid?.renderModel === false ? void 0 : this.sectionModels.prepareState(blockState3, stored.properties, color3);
108947
+ const contexts = /* @__PURE__ */ new Map();
108948
+ const groups = /* @__PURE__ */ new Map();
108949
+ if (blockState3 && (source || fluid?.kind === "water")) {
108950
+ for (const index of indices) {
108951
+ const position = this.biomePosition(index, blockState3, stored);
108952
+ const biome = this.biomeAt ? this.biomeAt(...position) : position[1] >= this.y * 16 ? this.getBiomeAt(position) : void 0;
108953
+ if (!biome) continue;
108954
+ const key = JSON.stringify(source === "grass" ? [biome, position[0], position[2]] : [biome]);
108955
+ let group = groups.get(key);
108956
+ if (!group) groups.set(key, group = { biome, position, indices: [] });
108957
+ group.indices.push(index);
108958
+ }
108959
+ }
108960
+ const groupedCount = [...groups.values()].reduce((sum, group) => sum + group.indices.length, 0);
108961
+ const prepared = groupedCount < indices.length ? await prepare() : void 0;
108962
+ await Promise.all([...groups.values()].map(async ({ biome, position, indices: indices2 }) => {
108963
+ const available = () => {
108964
+ const key = JSON.stringify([biome, blockState3?.key?.root]);
108965
+ let found = biomes.get(key);
108966
+ if (!found) {
108967
+ found = Biomes.get(biome, blockState3?.key).then((value) => value !== void 0);
108968
+ biomes.set(key, found);
108969
+ }
108970
+ return found;
108971
+ };
108972
+ const context = { biome };
108973
+ if (perBlock) {
108974
+ if (!await available()) context.biome = void 0;
108975
+ } else {
108976
+ context.prepared = await prepare(async () => await available() ? BlockTints.getBiomeColor(blockState3?.key, biome, position) : void 0);
108977
+ if (fluid?.kind === "water" && await available()) {
108978
+ const color3 = await Biomes.getColor(biome, "water", blockState3?.key);
108979
+ if (color3 !== void 0 && color3 !== 4159204) context.waterTint = new Color(color3).toArray();
108980
+ }
108981
+ }
108982
+ for (const index of indices2) contexts.set(index, context);
108983
+ }));
108984
+ return { blockState: blockState3, perBlock, fluid, prepared, contexts, stored };
108985
+ }
108986
+ setWaterTint(index, tint) {
108987
+ if (!this.sectionModels || !this.waterTints && tint === void 0) return;
108988
+ if (!this.waterTints) {
108989
+ this.waterTints = new Float32Array(4096 * 3);
108990
+ for (let cell = 0; cell < 4096; cell++) this.waterTints.set(WATER_TINT2, cell * 3);
108991
+ }
108992
+ this.waterTints.set(tint ?? WATER_TINT2, index * 3);
108993
+ }
108235
108994
  /** Changes visibility at world block coordinates while preserving the block's data. */
108236
108995
  async setBlockVisibleAt(pos, visible) {
108237
108996
  const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
@@ -108335,7 +109094,7 @@ gl_FragDepth=readDepth(vUv);
108335
109094
  if (block && !block.object) origins.set(kind, block.block.type);
108336
109095
  }
108337
109096
  if (origins.size) {
108338
- fluids = { cells: new Uint8Array(18 * 18 * 18) };
109097
+ fluids = { cells: new Uint8Array(18 * 18 * 18), waterTints: this.waterTints?.slice() };
108339
109098
  for (let y5 = -1; y5 <= 16; y5++) {
108340
109099
  for (let z4 = -1; z4 <= 16; z4++) {
108341
109100
  for (let x4 = -1; x4 <= 16; x4++) {
@@ -108377,8 +109136,13 @@ gl_FragDepth=readDepth(vUv);
108377
109136
  this.renderedBlocks.clear();
108378
109137
  this.hiddenBlocks.clear();
108379
109138
  this.fluidCells.fill(0);
109139
+ this.waterTints = void 0;
108380
109140
  this.data.clear();
108381
- if (notify) await this.onBlocksChanged?.(positions);
109141
+ this.biomes = void 0;
109142
+ if (notify) {
109143
+ await this.onBiomesChanged?.(void 0);
109144
+ await this.onBlocksChanged?.(positions);
109145
+ }
108382
109146
  }
108383
109147
  async dispose() {
108384
109148
  await this.clear();