auto-git:

[change] src/commands/apply-terrain-brush-patch-command.ts
This commit is contained in:
2026-05-13 00:14:19 +02:00
parent 21955f147a
commit 83f8d2a860

View File

@@ -81,6 +81,101 @@ function mergeTerrainSampleIndexIntoBounds(
};
}
export function applyTerrainBrushPatchToDocument(
currentDocument: ReturnType<CommandContext["getDocument"]>,
patch: TerrainBrushPatch,
direction: "forward" | "backward"
): ReturnType<CommandContext["getDocument"]> {
const terrain = currentDocument.terrains[patch.terrainId];
if (terrain === undefined) {
throw new Error(`Terrain ${patch.terrainId} does not exist.`);
}
const heightPatchForBounds = patch.heightSamples.map((entry) => ({
index: entry.index,
before: direction === "forward" ? entry.before : entry.after,
after: direction === "forward" ? entry.after : entry.before
}));
let renderDirtyBounds: TerrainSampleBounds | null = null;
for (const entry of patch.heightSamples) {
assertValidPatchEntry(entry, terrain.heights.length, "Terrain height");
terrain.heights[entry.index] =
direction === "forward" ? entry.after : entry.before;
renderDirtyBounds = mergeTerrainSampleIndexIntoBounds(
renderDirtyBounds,
terrain,
entry.index
);
}
updateTerrainBoundsCacheAfterHeightPatch(terrain, heightPatchForBounds);
for (const entry of patch.paintWeights) {
assertValidPatchEntry(
entry,
terrain.paintWeights.length,
"Terrain paint weight"
);
terrain.paintWeights[entry.index] =
direction === "forward" ? entry.after : entry.before;
renderDirtyBounds = mergeTerrainSampleIndexIntoBounds(
renderDirtyBounds,
terrain,
Math.floor(entry.index / (TERRAIN_LAYER_COUNT - 1))
);
}
for (const entry of patch.foliageMaskValues) {
if (currentDocument.foliageLayers[entry.layerId] === undefined) {
throw new Error(
`Foliage layer ${entry.layerId} does not exist for terrain mask patch.`
);
}
const mask = getOrCreateTerrainFoliageMask(terrain, entry.layerId);
assertValidPatchEntry(entry, mask.values.length, "Terrain foliage mask");
mask.values[entry.index] =
direction === "forward" ? entry.after : entry.before;
renderDirtyBounds = mergeTerrainSampleIndexIntoBounds(
renderDirtyBounds,
terrain,
entry.index
);
if (isTerrainFoliageMaskEmpty(mask)) {
delete terrain.foliageMasks[entry.layerId];
}
}
for (const entry of patch.foliageBlockerMaskValues) {
assertValidPatchEntry(
entry,
terrain.foliageBlockerMask.values.length,
"Terrain foliage blocker mask"
);
terrain.foliageBlockerMask.values[entry.index] =
direction === "forward" ? entry.after : entry.before;
renderDirtyBounds = mergeTerrainSampleIndexIntoBounds(
renderDirtyBounds,
terrain,
entry.index
);
}
markTerrainRenderSamplesDirty(terrain, renderDirtyBounds);
return {
...currentDocument,
terrains: {
...currentDocument.terrains,
[patch.terrainId]: terrain
}
};
}
export function createApplyTerrainBrushPatchCommand(
options: ApplyTerrainBrushPatchCommandOptions
): EditorCommand {
@@ -102,99 +197,13 @@ export function createApplyTerrainBrushPatchCommand(
context: CommandContext,
direction: "forward" | "backward"
) => {
const currentDocument = context.getDocument();
const terrain = currentDocument.terrains[patch.terrainId];
if (terrain === undefined) {
throw new Error(`Terrain ${patch.terrainId} does not exist.`);
}
const heightPatchForBounds = patch.heightSamples.map((entry) => ({
index: entry.index,
before: direction === "forward" ? entry.before : entry.after,
after: direction === "forward" ? entry.after : entry.before
}));
let renderDirtyBounds: TerrainSampleBounds | null = null;
for (const entry of patch.heightSamples) {
assertValidPatchEntry(entry, terrain.heights.length, "Terrain height");
terrain.heights[entry.index] =
direction === "forward" ? entry.after : entry.before;
renderDirtyBounds = mergeTerrainSampleIndexIntoBounds(
renderDirtyBounds,
terrain,
entry.index
);
}
updateTerrainBoundsCacheAfterHeightPatch(terrain, heightPatchForBounds);
for (const entry of patch.paintWeights) {
assertValidPatchEntry(
entry,
terrain.paintWeights.length,
"Terrain paint weight"
);
terrain.paintWeights[entry.index] =
direction === "forward" ? entry.after : entry.before;
renderDirtyBounds = mergeTerrainSampleIndexIntoBounds(
renderDirtyBounds,
terrain,
Math.floor(entry.index / (TERRAIN_LAYER_COUNT - 1))
);
}
for (const entry of patch.foliageMaskValues) {
if (currentDocument.foliageLayers[entry.layerId] === undefined) {
throw new Error(
`Foliage layer ${entry.layerId} does not exist for terrain mask patch.`
);
}
const mask = getOrCreateTerrainFoliageMask(terrain, entry.layerId);
assertValidPatchEntry(
entry,
mask.values.length,
"Terrain foliage mask"
);
mask.values[entry.index] =
direction === "forward" ? entry.after : entry.before;
renderDirtyBounds = mergeTerrainSampleIndexIntoBounds(
renderDirtyBounds,
terrain,
entry.index
);
if (isTerrainFoliageMaskEmpty(mask)) {
delete terrain.foliageMasks[entry.layerId];
}
}
for (const entry of patch.foliageBlockerMaskValues) {
assertValidPatchEntry(
entry,
terrain.foliageBlockerMask.values.length,
"Terrain foliage blocker mask"
);
terrain.foliageBlockerMask.values[entry.index] =
direction === "forward" ? entry.after : entry.before;
renderDirtyBounds = mergeTerrainSampleIndexIntoBounds(
renderDirtyBounds,
terrain,
entry.index
);
}
markTerrainRenderSamplesDirty(terrain, renderDirtyBounds);
context.setDocument({
...currentDocument,
terrains: {
...currentDocument.terrains,
[patch.terrainId]: terrain
}
});
context.setDocument(
applyTerrainBrushPatchToDocument(
context.getDocument(),
patch,
direction
)
);
};
return {