minerender 2.1.0-alpha.94 → 2.1.0-alpha.95

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.
@@ -1374,8 +1374,9 @@ declare class BlockStateResolver {
1374
1374
  static select(blockState: BlockState, state: BlockStateProperties, choose?: (variants: BlockStateVariant | BlockStateVariant[]) => BlockStateVariant): BlockStateVariant[];
1375
1375
  /** Returns matching model groups before choosing weighted alternatives. */
1376
1376
  static matching(blockState: BlockState, state: BlockStateProperties): (BlockStateVariant | BlockStateVariant[])[];
1377
- /** Selects a random alternative using positive integer weights, each defaulting to 1. */
1378
- static choose(variants: BlockStateVariant | BlockStateVariant[]): BlockStateVariant;
1377
+ /** Selects by positive integer weights (default 1); world block coordinates make the choice repeatable. */
1378
+ static choose(variants: BlockStateVariant | BlockStateVariant[], position?: Readonly<TripleArray>): BlockStateVariant;
1379
+ private static positionSample;
1379
1380
  /** Converts blockstate rotations in degrees into the Euler rotation used by model rendering. */
1380
1381
  static rotation(variant: BlockStateVariant): Euler;
1381
1382
  }
@@ -1945,6 +1946,7 @@ declare class BlockObject extends SceneObject {
1945
1946
  readonly isBlockObject: true;
1946
1947
  static readonly DEFAULT_OPTIONS: BlockObjectOptions;
1947
1948
  readonly options: BlockObjectOptions;
1949
+ private readonly variantPosition?;
1948
1950
  private _previousState;
1949
1951
  private _state;
1950
1952
  private _variant;
@@ -2018,6 +2020,8 @@ interface BlockObjectOptions extends ModelObjectOptions {
2018
2020
  applyDefaultState: boolean;
2019
2021
  /** Properties applied before model creation, overriding defaults when applyDefaultState is enabled. */
2020
2022
  initialState?: BlockStateProperties;
2023
+ /** Absolute block coordinates for repeatable variants, copied at construction. Omit for random previews. */
2024
+ variantPosition?: TripleArray;
2021
2025
  }
2022
2026
  declare function isBlockObject(obj: any): obj is BlockObject;
2023
2027
 
@@ -3299,7 +3303,7 @@ declare class SectionModels {
3299
3303
  private readonly templates;
3300
3304
  constructor(maxAtlasSize?: number);
3301
3305
  /** Returns templates for the matched parts, or `undefined` when the block needs an individual render object. */
3302
- get(blockState: BlockState, properties?: BlockStateProperties): Promise<SectionMeshTemplate[] | undefined>;
3306
+ get(blockState: BlockState, properties?: BlockStateProperties, position?: Readonly<TripleArray>): Promise<SectionMeshTemplate[] | undefined>;
3303
3307
  private prepare;
3304
3308
  /** Disposes prepared geometry and clears this world's template caches. */
3305
3309
  clear(): void;
@@ -1374,8 +1374,9 @@ declare class BlockStateResolver {
1374
1374
  static select(blockState: BlockState, state: BlockStateProperties, choose?: (variants: BlockStateVariant | BlockStateVariant[]) => BlockStateVariant): BlockStateVariant[];
1375
1375
  /** Returns matching model groups before choosing weighted alternatives. */
1376
1376
  static matching(blockState: BlockState, state: BlockStateProperties): (BlockStateVariant | BlockStateVariant[])[];
1377
- /** Selects a random alternative using positive integer weights, each defaulting to 1. */
1378
- static choose(variants: BlockStateVariant | BlockStateVariant[]): BlockStateVariant;
1377
+ /** Selects by positive integer weights (default 1); world block coordinates make the choice repeatable. */
1378
+ static choose(variants: BlockStateVariant | BlockStateVariant[], position?: Readonly<TripleArray>): BlockStateVariant;
1379
+ private static positionSample;
1379
1380
  /** Converts blockstate rotations in degrees into the Euler rotation used by model rendering. */
1380
1381
  static rotation(variant: BlockStateVariant): Euler;
1381
1382
  }
@@ -1945,6 +1946,7 @@ declare class BlockObject extends SceneObject {
1945
1946
  readonly isBlockObject: true;
1946
1947
  static readonly DEFAULT_OPTIONS: BlockObjectOptions;
1947
1948
  readonly options: BlockObjectOptions;
1949
+ private readonly variantPosition?;
1948
1950
  private _previousState;
1949
1951
  private _state;
1950
1952
  private _variant;
@@ -2018,6 +2020,8 @@ interface BlockObjectOptions extends ModelObjectOptions {
2018
2020
  applyDefaultState: boolean;
2019
2021
  /** Properties applied before model creation, overriding defaults when applyDefaultState is enabled. */
2020
2022
  initialState?: BlockStateProperties;
2023
+ /** Absolute block coordinates for repeatable variants, copied at construction. Omit for random previews. */
2024
+ variantPosition?: TripleArray;
2021
2025
  }
2022
2026
  declare function isBlockObject(obj: any): obj is BlockObject;
2023
2027
 
@@ -3299,7 +3303,7 @@ declare class SectionModels {
3299
3303
  private readonly templates;
3300
3304
  constructor(maxAtlasSize?: number);
3301
3305
  /** Returns templates for the matched parts, or `undefined` when the block needs an individual render object. */
3302
- get(blockState: BlockState, properties?: BlockStateProperties): Promise<SectionMeshTemplate[] | undefined>;
3306
+ get(blockState: BlockState, properties?: BlockStateProperties, position?: Readonly<TripleArray>): Promise<SectionMeshTemplate[] | undefined>;
3303
3307
  private prepare;
3304
3308
  /** Disposes prepared geometry and clears this world's template caches. */
3305
3309
  clear(): void;
@@ -23407,7 +23407,7 @@ function matchesCondition(condition, state) {
23407
23407
  return typeof value === "string" && state[key] !== void 0 && value.split("|").includes(`${state[key]}`);
23408
23408
  });
23409
23409
  }
23410
- var BlockStateResolver = class {
23410
+ var BlockStateResolver = class _BlockStateResolver {
23411
23411
  /** Loads vanilla property defaults, falling back to preview values inferred from the blockstate file. */
23412
23412
  static async defaults(blockState3) {
23413
23413
  const defaults = blockState3.key ? await BlockStates.getDefaultState(blockState3.key) : void 0;
@@ -23430,7 +23430,7 @@ var BlockStateResolver = class {
23430
23430
  }
23431
23431
  /** Selects matching models. Supply `choose` to control selection within each alternatives array. */
23432
23432
  static select(blockState3, state, choose = this.choose) {
23433
- return this.matching(blockState3, state).map(choose);
23433
+ return this.matching(blockState3, state).map((variants) => choose(variants));
23434
23434
  }
23435
23435
  /** Returns matching model groups before choosing weighted alternatives. */
23436
23436
  static matching(blockState3, state) {
@@ -23456,8 +23456,8 @@ var BlockStateResolver = class {
23456
23456
  }
23457
23457
  return out;
23458
23458
  }
23459
- /** Selects a random alternative using positive integer weights, each defaulting to 1. */
23460
- static choose(variants) {
23459
+ /** Selects by positive integer weights (default 1); world block coordinates make the choice repeatable. */
23460
+ static choose(variants, position) {
23461
23461
  if (!Array.isArray(variants)) return variants;
23462
23462
  if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
23463
23463
  const total = variants.reduce((sum, variant) => {
@@ -23467,13 +23467,29 @@ var BlockStateResolver = class {
23467
23467
  }
23468
23468
  return sum + weight;
23469
23469
  }, 0);
23470
- let choice = Math.random() * total;
23470
+ let choice = position === void 0 ? Math.random() * total : _BlockStateResolver.positionSample(position, total);
23471
23471
  for (const variant of variants) {
23472
23472
  choice -= variant.weight ?? 1;
23473
23473
  if (choice < 0) return variant;
23474
23474
  }
23475
23475
  return variants[variants.length - 1];
23476
23476
  }
23477
+ static positionSample(position, bound) {
23478
+ if (bound > 2147483647) {
23479
+ throw new MineRenderError("Position-based blockstate variant total weight must not exceed 2147483647");
23480
+ }
23481
+ let mixed = BigInt(Math.imul(position[0], 3129871)) ^ BigInt(position[2]) * 116129781n ^ BigInt(position[1]);
23482
+ mixed = BigInt.asIntN(64, mixed * mixed * 42317861n + mixed * 11n) >> 16n;
23483
+ let seed = (mixed ^ 0x5deece66dn) & 0xffffffffffffn;
23484
+ let bits, value;
23485
+ do {
23486
+ seed = seed * 0x5deece66dn + 11n & 0xffffffffffffn;
23487
+ bits = Number(seed >> 17n);
23488
+ if ((bound & bound - 1) === 0) return Number(BigInt(bound) * BigInt(bits) >> 31n);
23489
+ value = bits % bound;
23490
+ } while ((bits - value + bound - 1 | 0) < 0);
23491
+ return value;
23492
+ }
23477
23493
  /** Converts blockstate rotations in degrees into the Euler rotation used by model rendering. */
23478
23494
  static rotation(variant) {
23479
23495
  const rotation = new import_three29.Euler();
@@ -23593,6 +23609,7 @@ var BlockObject = class _BlockObject extends SceneObject {
23593
23609
  this._entityPlacement = new import_three32.Matrix4();
23594
23610
  this._entityGeneration = 0;
23595
23611
  this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
23612
+ this.variantPosition = options?.variantPosition ? [...options.variantPosition] : void 0;
23596
23613
  }
23597
23614
  static {
23598
23615
  this.DEFAULT_OPTIONS = (0, import_ts_deepmerge6.default)({}, ModelObject.DEFAULT_OPTIONS, {
@@ -23834,7 +23851,7 @@ var BlockObject = class _BlockObject extends SceneObject {
23834
23851
  return !!(await Models.getMerged(modelKey))?.elements?.length;
23835
23852
  }
23836
23853
  getSingleVariant(variants) {
23837
- return BlockStateResolver.choose(variants);
23854
+ return BlockStateResolver.choose(variants, this.variantPosition);
23838
23855
  }
23839
23856
  // @deprecated
23840
23857
  async createAVariant(variants, instanceInfo) {
@@ -29725,13 +29742,14 @@ var SectionModels = class {
29725
29742
  if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
29726
29743
  }
29727
29744
  /** Returns templates for the matched parts, or `undefined` when the block needs an individual render object. */
29728
- async get(blockState3, properties = {}) {
29745
+ async get(blockState3, properties = {}, position) {
29746
+ const variantPosition = position ? [...position] : void 0;
29729
29747
  let states = this.states.get(blockState3);
29730
29748
  if (!states) this.states.set(blockState3, states = /* @__PURE__ */ new Map());
29731
29749
  const key = JSON.stringify(Object.entries(properties).sort(([a], [b]) => a.localeCompare(b)));
29732
29750
  const prepared = await cached(states, key, () => this.prepare(blockState3, properties));
29733
29751
  if (!prepared) return void 0;
29734
- return prepared.groups.map((group) => prepared.templates.get(BlockStateResolver.choose(group)));
29752
+ return prepared.groups.map((group) => prepared.templates.get(BlockStateResolver.choose(group, variantPosition)));
29735
29753
  }
29736
29754
  async prepare(blockState3, properties) {
29737
29755
  const state = { ...await BlockStateResolver.defaults(blockState3), ...properties };
@@ -30521,7 +30539,8 @@ var Chunk = class _Chunk {
30521
30539
  this.data.set(index, void 0);
30522
30540
  continue;
30523
30541
  }
30524
- const templates = perBlock ? void 0 : await this.sectionModels.get(blockState3, stored.properties);
30542
+ const variantPosition = worldPos.toArray();
30543
+ const templates = perBlock ? void 0 : await this.sectionModels.get(blockState3, stored.properties, variantPosition);
30525
30544
  if (templates) {
30526
30545
  this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
30527
30546
  this.meshDirty = true;
@@ -30530,7 +30549,8 @@ var Chunk = class _Chunk {
30530
30549
  mergeMeshes: true,
30531
30550
  instanceMeshes: true,
30532
30551
  maxInstanceCount: 2e3,
30533
- initialState: stored.properties
30552
+ initialState: stored.properties,
30553
+ variantPosition
30534
30554
  });
30535
30555
  object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
30536
30556
  }