auto-git:
[change] src/document/terrains.ts
This commit is contained in:
@@ -21,6 +21,26 @@ export interface Terrain {
|
|||||||
paintWeights: number[];
|
paintWeights: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TerrainHeightPatchEntry {
|
||||||
|
index: number;
|
||||||
|
before: number;
|
||||||
|
after: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TerrainBoundsCacheEntry {
|
||||||
|
heights: number[];
|
||||||
|
position: Vec3;
|
||||||
|
sampleCountX: number;
|
||||||
|
sampleCountZ: number;
|
||||||
|
cellSize: number;
|
||||||
|
minHeight: number;
|
||||||
|
maxHeight: number;
|
||||||
|
bounds: {
|
||||||
|
min: Vec3;
|
||||||
|
max: Vec3;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export const DEFAULT_TERRAIN_VISIBLE = true;
|
export const DEFAULT_TERRAIN_VISIBLE = true;
|
||||||
export const DEFAULT_TERRAIN_ENABLED = true;
|
export const DEFAULT_TERRAIN_ENABLED = true;
|
||||||
export const DEFAULT_TERRAIN_COLLISION_ENABLED = true;
|
export const DEFAULT_TERRAIN_COLLISION_ENABLED = true;
|
||||||
@@ -45,6 +65,16 @@ function cloneVec3(vector: Vec3): Vec3 {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cloneTerrainBounds(bounds: { min: Vec3; max: Vec3 }): {
|
||||||
|
min: Vec3;
|
||||||
|
max: Vec3;
|
||||||
|
} {
|
||||||
|
return {
|
||||||
|
min: cloneVec3(bounds.min),
|
||||||
|
max: cloneVec3(bounds.max)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function areVec3Equal(left: Vec3, right: Vec3): boolean {
|
function areVec3Equal(left: Vec3, right: Vec3): boolean {
|
||||||
return left.x === right.x && left.y === right.y && left.z === right.z;
|
return left.x === right.x && left.y === right.y && left.z === right.z;
|
||||||
}
|
}
|
||||||
@@ -352,9 +382,125 @@ export function getTerrainFootprintDepth(
|
|||||||
return (terrain.sampleCountZ - 1) * terrain.cellSize;
|
return (terrain.sampleCountZ - 1) * terrain.cellSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTerrainBounds(terrain: Terrain): { min: Vec3; max: Vec3 } {
|
const terrainBoundsCache = new WeakMap<Terrain, TerrainBoundsCacheEntry>();
|
||||||
|
|
||||||
|
function createTerrainBoundsCacheEntry(
|
||||||
|
terrain: Terrain,
|
||||||
|
minHeight: number,
|
||||||
|
maxHeight: number
|
||||||
|
): TerrainBoundsCacheEntry {
|
||||||
const width = getTerrainFootprintWidth(terrain);
|
const width = getTerrainFootprintWidth(terrain);
|
||||||
const depth = getTerrainFootprintDepth(terrain);
|
const depth = getTerrainFootprintDepth(terrain);
|
||||||
|
const bounds = {
|
||||||
|
min: {
|
||||||
|
x: terrain.position.x,
|
||||||
|
y: terrain.position.y + minHeight,
|
||||||
|
z: terrain.position.z
|
||||||
|
},
|
||||||
|
max: {
|
||||||
|
x: terrain.position.x + width,
|
||||||
|
y: terrain.position.y + maxHeight,
|
||||||
|
z: terrain.position.z + depth
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
heights: terrain.heights,
|
||||||
|
position: cloneVec3(terrain.position),
|
||||||
|
sampleCountX: terrain.sampleCountX,
|
||||||
|
sampleCountZ: terrain.sampleCountZ,
|
||||||
|
cellSize: terrain.cellSize,
|
||||||
|
minHeight,
|
||||||
|
maxHeight,
|
||||||
|
bounds
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTerrainBoundsCacheEntryCurrent(
|
||||||
|
terrain: Terrain,
|
||||||
|
entry: TerrainBoundsCacheEntry
|
||||||
|
): boolean {
|
||||||
|
return (
|
||||||
|
entry.heights === terrain.heights &&
|
||||||
|
entry.sampleCountX === terrain.sampleCountX &&
|
||||||
|
entry.sampleCountZ === terrain.sampleCountZ &&
|
||||||
|
entry.cellSize === terrain.cellSize &&
|
||||||
|
areVec3Equal(entry.position, terrain.position)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function invalidateTerrainBoundsCache(terrain: Terrain) {
|
||||||
|
terrainBoundsCache.delete(terrain);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateTerrainBoundsCacheAfterHeightPatch(
|
||||||
|
terrain: Terrain,
|
||||||
|
patch: readonly TerrainHeightPatchEntry[]
|
||||||
|
) {
|
||||||
|
if (patch.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedEntry = terrainBoundsCache.get(terrain);
|
||||||
|
|
||||||
|
if (
|
||||||
|
cachedEntry === undefined ||
|
||||||
|
!isTerrainBoundsCacheEntryCurrent(terrain, cachedEntry)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let minHeight = cachedEntry.minHeight;
|
||||||
|
let maxHeight = cachedEntry.maxHeight;
|
||||||
|
let requiresRescan = false;
|
||||||
|
|
||||||
|
for (const entry of patch) {
|
||||||
|
if (
|
||||||
|
!Number.isInteger(entry.index) ||
|
||||||
|
entry.index < 0 ||
|
||||||
|
entry.index >= terrain.heights.length ||
|
||||||
|
!Number.isFinite(entry.before) ||
|
||||||
|
!Number.isFinite(entry.after)
|
||||||
|
) {
|
||||||
|
requiresRescan = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.before === minHeight && entry.after > entry.before) {
|
||||||
|
requiresRescan = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.before === maxHeight && entry.after < entry.before) {
|
||||||
|
requiresRescan = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
minHeight = Math.min(minHeight, entry.after);
|
||||||
|
maxHeight = Math.max(maxHeight, entry.after);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requiresRescan) {
|
||||||
|
invalidateTerrainBoundsCache(terrain);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
terrainBoundsCache.set(
|
||||||
|
terrain,
|
||||||
|
createTerrainBoundsCacheEntry(terrain, minHeight, maxHeight)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTerrainBounds(terrain: Terrain): { min: Vec3; max: Vec3 } {
|
||||||
|
const cachedEntry = terrainBoundsCache.get(terrain);
|
||||||
|
|
||||||
|
if (
|
||||||
|
cachedEntry !== undefined &&
|
||||||
|
isTerrainBoundsCacheEntryCurrent(terrain, cachedEntry)
|
||||||
|
) {
|
||||||
|
return cloneTerrainBounds(cachedEntry.bounds);
|
||||||
|
}
|
||||||
|
|
||||||
let minHeight = Number.POSITIVE_INFINITY;
|
let minHeight = Number.POSITIVE_INFINITY;
|
||||||
let maxHeight = Number.NEGATIVE_INFINITY;
|
let maxHeight = Number.NEGATIVE_INFINITY;
|
||||||
|
|
||||||
@@ -368,18 +514,14 @@ export function getTerrainBounds(terrain: Terrain): { min: Vec3; max: Vec3 } {
|
|||||||
maxHeight = 0;
|
maxHeight = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
const nextEntry = createTerrainBoundsCacheEntry(
|
||||||
min: {
|
terrain,
|
||||||
x: terrain.position.x,
|
minHeight,
|
||||||
y: terrain.position.y + minHeight,
|
maxHeight
|
||||||
z: terrain.position.z
|
);
|
||||||
},
|
terrainBoundsCache.set(terrain, nextEntry);
|
||||||
max: {
|
|
||||||
x: terrain.position.x + width,
|
return cloneTerrainBounds(nextEntry.bounds);
|
||||||
y: terrain.position.y + maxHeight,
|
|
||||||
z: terrain.position.z + depth
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function clamp(value: number, min: number, max: number): number {
|
function clamp(value: number, min: number, max: number): number {
|
||||||
|
|||||||
Reference in New Issue
Block a user