auto-git:

[change] src/geometry/terrain-brush.ts
This commit is contained in:
2026-05-01 17:56:39 +02:00
parent bee39c70b3
commit 5f01182675

View File

@@ -30,6 +30,8 @@ export interface TerrainBrushDirtySampleBounds {
export interface TerrainBrushStampMutationResult {
changed: boolean;
dirtyBounds: TerrainBrushDirtySampleBounds | null;
heightSampleIndices: number[];
paintWeightIndices: number[];
}
interface TerrainSmoothHeightSource {
@@ -243,11 +245,27 @@ function setTerrainSamplePaintWeights(
sampleX: number,
sampleZ: number,
weights: readonly [number, number, number, number]
) {
): number[] {
const offset = getTerrainPaintWeightSampleOffset(terrain, sampleX, sampleZ);
paintWeights[offset] = weights[1];
paintWeights[offset + 1] = weights[2];
paintWeights[offset + 2] = weights[3];
const changedIndices: number[] = [];
for (
let layerOffset = 0;
layerOffset < TERRAIN_LAYER_COUNT - 1;
layerOffset += 1
) {
const paintWeightIndex = offset + layerOffset;
const nextWeight = weights[layerOffset + 1] ?? 0;
if ((paintWeights[paintWeightIndex] ?? 0) === nextWeight) {
continue;
}
paintWeights[paintWeightIndex] = nextWeight;
changedIndices.push(paintWeightIndex);
}
return changedIndices;
}
export function applyTerrainBrushStamp(options: {
@@ -304,7 +322,9 @@ export function applyTerrainBrushStampInPlace(options: {
if (minSampleX > maxSampleX || minSampleZ > maxSampleZ) {
return {
changed: false,
dirtyBounds: null
dirtyBounds: null,
heightSampleIndices: [],
paintWeightIndices: []
};
}
@@ -320,6 +340,8 @@ export function applyTerrainBrushStampInPlace(options: {
: null;
const smoothingStrength = clamp01(strength);
let dirtyBounds: TerrainBrushDirtySampleBounds | null = null;
const heightSampleIndices: number[] = [];
const paintWeightIndices: number[] = [];
const markDirty = (sampleX: number, sampleZ: number) => {
if (dirtyBounds === null) {
@@ -411,21 +433,26 @@ export function applyTerrainBrushStampInPlace(options: {
nextWeights[2] !== currentWeights[2] ||
nextWeights[3] !== currentWeights[3]
) {
setTerrainSamplePaintWeights(
const changedPaintWeightIndices = setTerrainSamplePaintWeights(
terrain.paintWeights,
terrain,
sampleX,
sampleZ,
nextWeights
);
if (changedPaintWeightIndices.length > 0) {
paintWeightIndices.push(...changedPaintWeightIndices);
markDirty(sampleX, sampleZ);
}
}
continue;
}
}
if (nextHeight !== currentHeight) {
terrain.heights[sampleIndex] = nextHeight;
heightSampleIndices.push(sampleIndex);
markDirty(sampleX, sampleZ);
}
}
@@ -433,16 +460,19 @@ export function applyTerrainBrushStampInPlace(options: {
return {
changed: dirtyBounds !== null,
dirtyBounds
dirtyBounds,
heightSampleIndices,
paintWeightIndices
};
}
export function createTerrainBrushPatchFromTerrains(options: {
before: Terrain;
after: Terrain;
dirtyBounds: TerrainBrushDirtySampleBounds;
heightSampleIndices: Iterable<number>;
paintWeightIndices: Iterable<number>;
}): TerrainBrushPatch {
const { before, after, dirtyBounds } = options;
const { before, after } = options;
if (before.id !== after.id) {
throw new Error("Terrain brush patches require matching terrain ids.");
@@ -461,34 +491,36 @@ export function createTerrainBrushPatchFromTerrains(options: {
const heightSamples: TerrainBrushPatch["heightSamples"] = [];
const paintWeights: TerrainBrushPatch["paintWeights"] = [];
const minSampleX = clamp(
Math.floor(dirtyBounds.minSampleX),
0,
before.sampleCountX - 1
);
const maxSampleX = clamp(
Math.ceil(dirtyBounds.maxSampleX),
0,
before.sampleCountX - 1
);
const minSampleZ = clamp(
Math.floor(dirtyBounds.minSampleZ),
0,
before.sampleCountZ - 1
);
const maxSampleZ = clamp(
Math.ceil(dirtyBounds.maxSampleZ),
0,
before.sampleCountZ - 1
);
const normalizeIndices = (
indices: Iterable<number>,
length: number,
label: string
): number[] => {
const uniqueIndices = new Set<number>();
for (let sampleZ = minSampleZ; sampleZ <= maxSampleZ; sampleZ += 1) {
for (let sampleX = minSampleX; sampleX <= maxSampleX; sampleX += 1) {
const sampleIndex = getTerrainSampleIndex(before, sampleX, sampleZ);
for (const index of indices) {
if (!Number.isInteger(index) || index < 0 || index >= length) {
throw new Error(`${label} patch index ${index} is out of range.`);
}
uniqueIndices.add(index);
}
return [...uniqueIndices].sort((left, right) => left - right);
};
for (const sampleIndex of normalizeIndices(
options.heightSampleIndices,
before.heights.length,
"Terrain height"
)) {
const beforeHeight = before.heights[sampleIndex] ?? 0;
const afterHeight = after.heights[sampleIndex] ?? 0;
if (beforeHeight !== afterHeight) {
if (beforeHeight === afterHeight) {
continue;
}
heightSamples.push({
index: sampleIndex,
before: beforeHeight,
@@ -496,18 +528,11 @@ export function createTerrainBrushPatchFromTerrains(options: {
});
}
const paintOffset = getTerrainPaintWeightSampleOffset(
before,
sampleX,
sampleZ
);
for (
let layerOffset = 0;
layerOffset < TERRAIN_LAYER_COUNT - 1;
layerOffset += 1
) {
const paintWeightIndex = paintOffset + layerOffset;
for (const paintWeightIndex of normalizeIndices(
options.paintWeightIndices,
before.paintWeights.length,
"Terrain paint weight"
)) {
const beforeWeight = before.paintWeights[paintWeightIndex] ?? 0;
const afterWeight = after.paintWeights[paintWeightIndex] ?? 0;
@@ -521,8 +546,6 @@ export function createTerrainBrushPatchFromTerrains(options: {
after: afterWeight
});
}
}
}
return {
terrainId: before.id,