Refactor terrain resampling to use local position sampling based on cell size
This commit is contained in:
@@ -1285,43 +1285,29 @@ export function sampleTerrainFoliageBlockerMaskAtWorldPosition(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTerrainPositionFromCenter(
|
function getTerrainSampleCountForFootprint(
|
||||||
center: Vec3,
|
footprint: number,
|
||||||
|
cellSize: number
|
||||||
|
): number {
|
||||||
|
return Math.max(
|
||||||
|
MIN_TERRAIN_SAMPLE_COUNT,
|
||||||
|
Math.round(footprint / cellSize) + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLocalPositionResampledTerrainHeights(
|
||||||
|
terrain: Terrain,
|
||||||
sampleCountX: number,
|
sampleCountX: number,
|
||||||
sampleCountZ: number,
|
sampleCountZ: number,
|
||||||
cellSize: number
|
cellSize: number
|
||||||
): Vec3 {
|
|
||||||
return {
|
|
||||||
x: center.x - ((sampleCountX - 1) * cellSize) * 0.5,
|
|
||||||
y: center.y,
|
|
||||||
z: center.z - ((sampleCountZ - 1) * cellSize) * 0.5
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTerrainFootprintCenter(terrain: Terrain): Vec3 {
|
|
||||||
return {
|
|
||||||
x: terrain.position.x + getTerrainFootprintWidth(terrain) * 0.5,
|
|
||||||
y: terrain.position.y,
|
|
||||||
z: terrain.position.z + getTerrainFootprintDepth(terrain) * 0.5
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createResampledTerrainHeights(
|
|
||||||
terrain: Terrain,
|
|
||||||
sampleCountX: number,
|
|
||||||
sampleCountZ: number
|
|
||||||
): number[] {
|
): number[] {
|
||||||
const heights = new Array<number>(sampleCountX * sampleCountZ);
|
const heights = new Array<number>(sampleCountX * sampleCountZ);
|
||||||
|
|
||||||
for (let sampleZ = 0; sampleZ < sampleCountZ; sampleZ += 1) {
|
for (let sampleZ = 0; sampleZ < sampleCountZ; sampleZ += 1) {
|
||||||
const normalizedSampleZ =
|
const sourceSampleZ = (sampleZ * cellSize) / terrain.cellSize;
|
||||||
sampleCountZ === 1 ? 0 : sampleZ / (sampleCountZ - 1);
|
|
||||||
const sourceSampleZ = normalizedSampleZ * (terrain.sampleCountZ - 1);
|
|
||||||
|
|
||||||
for (let sampleX = 0; sampleX < sampleCountX; sampleX += 1) {
|
for (let sampleX = 0; sampleX < sampleCountX; sampleX += 1) {
|
||||||
const normalizedSampleX =
|
const sourceSampleX = (sampleX * cellSize) / terrain.cellSize;
|
||||||
sampleCountX === 1 ? 0 : sampleX / (sampleCountX - 1);
|
|
||||||
const sourceSampleX = normalizedSampleX * (terrain.sampleCountX - 1);
|
|
||||||
|
|
||||||
heights[sampleZ * sampleCountX + sampleX] =
|
heights[sampleZ * sampleCountX + sampleX] =
|
||||||
sampleTerrainHeightAtGridCoordinate(
|
sampleTerrainHeightAtGridCoordinate(
|
||||||
@@ -1335,10 +1321,11 @@ function createResampledTerrainHeights(
|
|||||||
return heights;
|
return heights;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createResampledTerrainPaintWeights(
|
function createLocalPositionResampledTerrainPaintWeights(
|
||||||
terrain: Terrain,
|
terrain: Terrain,
|
||||||
sampleCountX: number,
|
sampleCountX: number,
|
||||||
sampleCountZ: number
|
sampleCountZ: number,
|
||||||
|
cellSize: number
|
||||||
): number[] {
|
): number[] {
|
||||||
const storedWeightCount = getTerrainStoredPaintWeightCount(
|
const storedWeightCount = getTerrainStoredPaintWeightCount(
|
||||||
terrain.layers.length
|
terrain.layers.length
|
||||||
@@ -1348,14 +1335,10 @@ function createResampledTerrainPaintWeights(
|
|||||||
);
|
);
|
||||||
|
|
||||||
for (let sampleZ = 0; sampleZ < sampleCountZ; sampleZ += 1) {
|
for (let sampleZ = 0; sampleZ < sampleCountZ; sampleZ += 1) {
|
||||||
const normalizedSampleZ =
|
const sourceSampleZ = (sampleZ * cellSize) / terrain.cellSize;
|
||||||
sampleCountZ === 1 ? 0 : sampleZ / (sampleCountZ - 1);
|
|
||||||
const sourceSampleZ = normalizedSampleZ * (terrain.sampleCountZ - 1);
|
|
||||||
|
|
||||||
for (let sampleX = 0; sampleX < sampleCountX; sampleX += 1) {
|
for (let sampleX = 0; sampleX < sampleCountX; sampleX += 1) {
|
||||||
const normalizedSampleX =
|
const sourceSampleX = (sampleX * cellSize) / terrain.cellSize;
|
||||||
sampleCountX === 1 ? 0 : sampleX / (sampleCountX - 1);
|
|
||||||
const sourceSampleX = normalizedSampleX * (terrain.sampleCountX - 1);
|
|
||||||
const offset =
|
const offset =
|
||||||
(sampleZ * sampleCountX + sampleX) * storedWeightCount;
|
(sampleZ * sampleCountX + sampleX) * storedWeightCount;
|
||||||
|
|
||||||
@@ -1374,24 +1357,21 @@ function createResampledTerrainPaintWeights(
|
|||||||
return paintWeights;
|
return paintWeights;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createResampledTerrainFoliageMasks(
|
function createLocalPositionResampledTerrainFoliageMasks(
|
||||||
terrain: Terrain,
|
terrain: Terrain,
|
||||||
sampleCountX: number,
|
sampleCountX: number,
|
||||||
sampleCountZ: number
|
sampleCountZ: number,
|
||||||
|
cellSize: number
|
||||||
): TerrainFoliageMaskRegistry {
|
): TerrainFoliageMaskRegistry {
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
Object.entries(terrain.foliageMasks).map(([layerId, mask]) => {
|
Object.entries(terrain.foliageMasks).map(([layerId, mask]) => {
|
||||||
const values = new Array<number>(sampleCountX * sampleCountZ);
|
const values = new Array<number>(sampleCountX * sampleCountZ);
|
||||||
|
|
||||||
for (let sampleZ = 0; sampleZ < sampleCountZ; sampleZ += 1) {
|
for (let sampleZ = 0; sampleZ < sampleCountZ; sampleZ += 1) {
|
||||||
const normalizedSampleZ =
|
const sourceSampleZ = (sampleZ * cellSize) / terrain.cellSize;
|
||||||
sampleCountZ === 1 ? 0 : sampleZ / (sampleCountZ - 1);
|
|
||||||
const sourceSampleZ = normalizedSampleZ * (mask.resolutionZ - 1);
|
|
||||||
|
|
||||||
for (let sampleX = 0; sampleX < sampleCountX; sampleX += 1) {
|
for (let sampleX = 0; sampleX < sampleCountX; sampleX += 1) {
|
||||||
const normalizedSampleX =
|
const sourceSampleX = (sampleX * cellSize) / terrain.cellSize;
|
||||||
sampleCountX === 1 ? 0 : sampleX / (sampleCountX - 1);
|
|
||||||
const sourceSampleX = normalizedSampleX * (mask.resolutionX - 1);
|
|
||||||
|
|
||||||
values[sampleZ * sampleCountX + sampleX] =
|
values[sampleZ * sampleCountX + sampleX] =
|
||||||
sampleTerrainFoliageMaskAtGridCoordinate(
|
sampleTerrainFoliageMaskAtGridCoordinate(
|
||||||
@@ -1415,24 +1395,19 @@ function createResampledTerrainFoliageMasks(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createResampledTerrainFoliageBlockerMask(
|
function createLocalPositionResampledTerrainFoliageBlockerMask(
|
||||||
terrain: Terrain,
|
terrain: Terrain,
|
||||||
sampleCountX: number,
|
sampleCountX: number,
|
||||||
sampleCountZ: number
|
sampleCountZ: number,
|
||||||
|
cellSize: number
|
||||||
): TerrainFoliageBlockerMask {
|
): TerrainFoliageBlockerMask {
|
||||||
const values = new Array<number>(sampleCountX * sampleCountZ);
|
const values = new Array<number>(sampleCountX * sampleCountZ);
|
||||||
|
|
||||||
for (let sampleZ = 0; sampleZ < sampleCountZ; sampleZ += 1) {
|
for (let sampleZ = 0; sampleZ < sampleCountZ; sampleZ += 1) {
|
||||||
const normalizedSampleZ =
|
const sourceSampleZ = (sampleZ * cellSize) / terrain.cellSize;
|
||||||
sampleCountZ === 1 ? 0 : sampleZ / (sampleCountZ - 1);
|
|
||||||
const sourceSampleZ =
|
|
||||||
normalizedSampleZ * (terrain.foliageBlockerMask.resolutionZ - 1);
|
|
||||||
|
|
||||||
for (let sampleX = 0; sampleX < sampleCountX; sampleX += 1) {
|
for (let sampleX = 0; sampleX < sampleCountX; sampleX += 1) {
|
||||||
const normalizedSampleX =
|
const sourceSampleX = (sampleX * cellSize) / terrain.cellSize;
|
||||||
sampleCountX === 1 ? 0 : sampleX / (sampleCountX - 1);
|
|
||||||
const sourceSampleX =
|
|
||||||
normalizedSampleX * (terrain.foliageBlockerMask.resolutionX - 1);
|
|
||||||
|
|
||||||
values[sampleZ * sampleCountX + sampleX] =
|
values[sampleZ * sampleCountX + sampleX] =
|
||||||
sampleTerrainFoliageBlockerMaskAtGridCoordinate(
|
sampleTerrainFoliageBlockerMaskAtGridCoordinate(
|
||||||
@@ -1450,56 +1425,118 @@ function createResampledTerrainFoliageBlockerMask(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resizeTerrainGrid(
|
export function changeTerrainCellSizePreservingFootprint(
|
||||||
terrain: Terrain,
|
terrain: Terrain,
|
||||||
options: Pick<Terrain, "sampleCountX" | "sampleCountZ" | "cellSize"> & {
|
requestedCellSize: number
|
||||||
preserveCenter?: boolean;
|
|
||||||
}
|
|
||||||
): Terrain {
|
): Terrain {
|
||||||
const sampleCountX = normalizeTerrainSampleCount(
|
const cellSize = normalizeTerrainCellSize(requestedCellSize);
|
||||||
options.sampleCountX,
|
const sampleCountX = getTerrainSampleCountForFootprint(
|
||||||
"Terrain sampleCountX"
|
getTerrainFootprintWidth(terrain),
|
||||||
|
cellSize
|
||||||
);
|
);
|
||||||
const sampleCountZ = normalizeTerrainSampleCount(
|
const sampleCountZ = getTerrainSampleCountForFootprint(
|
||||||
options.sampleCountZ,
|
getTerrainFootprintDepth(terrain),
|
||||||
"Terrain sampleCountZ"
|
cellSize
|
||||||
);
|
);
|
||||||
const cellSize = normalizeTerrainCellSize(options.cellSize);
|
|
||||||
const preserveCenter = options.preserveCenter ?? true;
|
|
||||||
const nextPosition = preserveCenter
|
|
||||||
? createTerrainPositionFromCenter(
|
|
||||||
getTerrainFootprintCenter(terrain),
|
|
||||||
sampleCountX,
|
|
||||||
sampleCountZ,
|
|
||||||
cellSize
|
|
||||||
)
|
|
||||||
: cloneVec3(terrain.position);
|
|
||||||
|
|
||||||
return createTerrain({
|
return createTerrain({
|
||||||
...terrain,
|
...terrain,
|
||||||
position: nextPosition,
|
position: cloneVec3(terrain.position),
|
||||||
sampleCountX,
|
sampleCountX,
|
||||||
sampleCountZ,
|
sampleCountZ,
|
||||||
cellSize,
|
cellSize,
|
||||||
heights: createResampledTerrainHeights(terrain, sampleCountX, sampleCountZ),
|
heights: createLocalPositionResampledTerrainHeights(
|
||||||
paintWeights: createResampledTerrainPaintWeights(
|
|
||||||
terrain,
|
terrain,
|
||||||
sampleCountX,
|
sampleCountX,
|
||||||
sampleCountZ
|
sampleCountZ,
|
||||||
|
cellSize
|
||||||
),
|
),
|
||||||
foliageMasks: createResampledTerrainFoliageMasks(
|
paintWeights: createLocalPositionResampledTerrainPaintWeights(
|
||||||
terrain,
|
terrain,
|
||||||
sampleCountX,
|
sampleCountX,
|
||||||
sampleCountZ
|
sampleCountZ,
|
||||||
|
cellSize
|
||||||
),
|
),
|
||||||
foliageBlockerMask: createResampledTerrainFoliageBlockerMask(
|
foliageMasks: createLocalPositionResampledTerrainFoliageMasks(
|
||||||
terrain,
|
terrain,
|
||||||
sampleCountX,
|
sampleCountX,
|
||||||
sampleCountZ
|
sampleCountZ,
|
||||||
|
cellSize
|
||||||
|
),
|
||||||
|
foliageBlockerMask: createLocalPositionResampledTerrainFoliageBlockerMask(
|
||||||
|
terrain,
|
||||||
|
sampleCountX,
|
||||||
|
sampleCountZ,
|
||||||
|
cellSize
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function changeTerrainSampleCountsByExtendingOrCropping(
|
||||||
|
terrain: Terrain,
|
||||||
|
requestedSampleCountX: number,
|
||||||
|
requestedSampleCountZ: number
|
||||||
|
): Terrain {
|
||||||
|
const sampleCountX = normalizeTerrainSampleCount(
|
||||||
|
requestedSampleCountX,
|
||||||
|
"Terrain sampleCountX"
|
||||||
|
);
|
||||||
|
const sampleCountZ = normalizeTerrainSampleCount(
|
||||||
|
requestedSampleCountZ,
|
||||||
|
"Terrain sampleCountZ"
|
||||||
|
);
|
||||||
|
const cellSize = terrain.cellSize;
|
||||||
|
|
||||||
|
return createTerrain({
|
||||||
|
...terrain,
|
||||||
|
position: cloneVec3(terrain.position),
|
||||||
|
sampleCountX,
|
||||||
|
sampleCountZ,
|
||||||
|
cellSize,
|
||||||
|
heights: createLocalPositionResampledTerrainHeights(
|
||||||
|
terrain,
|
||||||
|
sampleCountX,
|
||||||
|
sampleCountZ,
|
||||||
|
cellSize
|
||||||
|
),
|
||||||
|
paintWeights: createLocalPositionResampledTerrainPaintWeights(
|
||||||
|
terrain,
|
||||||
|
sampleCountX,
|
||||||
|
sampleCountZ,
|
||||||
|
cellSize
|
||||||
|
),
|
||||||
|
foliageMasks: createLocalPositionResampledTerrainFoliageMasks(
|
||||||
|
terrain,
|
||||||
|
sampleCountX,
|
||||||
|
sampleCountZ,
|
||||||
|
cellSize
|
||||||
|
),
|
||||||
|
foliageBlockerMask: createLocalPositionResampledTerrainFoliageBlockerMask(
|
||||||
|
terrain,
|
||||||
|
sampleCountX,
|
||||||
|
sampleCountZ,
|
||||||
|
cellSize
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resizeTerrainGrid(
|
||||||
|
terrain: Terrain,
|
||||||
|
options: Pick<Terrain, "sampleCountX" | "sampleCountZ" | "cellSize">
|
||||||
|
): Terrain {
|
||||||
|
const cellSize = normalizeTerrainCellSize(options.cellSize);
|
||||||
|
|
||||||
|
if (cellSize !== terrain.cellSize) {
|
||||||
|
return changeTerrainCellSizePreservingFootprint(terrain, cellSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return changeTerrainSampleCountsByExtendingOrCropping(
|
||||||
|
terrain,
|
||||||
|
options.sampleCountX,
|
||||||
|
options.sampleCountZ
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function createDefaultTerrainPosition(
|
function createDefaultTerrainPosition(
|
||||||
sampleCountX: number,
|
sampleCountX: number,
|
||||||
sampleCountZ: number,
|
sampleCountZ: number,
|
||||||
|
|||||||
Reference in New Issue
Block a user