diff --git a/src/commands/apply-terrain-brush-patch-command.ts b/src/commands/apply-terrain-brush-patch-command.ts index ce59ebf7..c9122e65 100644 --- a/src/commands/apply-terrain-brush-patch-command.ts +++ b/src/commands/apply-terrain-brush-patch-command.ts @@ -81,6 +81,101 @@ function mergeTerrainSampleIndexIntoBounds( }; } +export function applyTerrainBrushPatchToDocument( + currentDocument: ReturnType, + patch: TerrainBrushPatch, + direction: "forward" | "backward" +): ReturnType { + 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 {