fix(world): stabilize lafabrik spawn and vegetation
This commit is contained in:
@@ -3,7 +3,6 @@ import * as THREE from "three";
|
|||||||
import { useGLTF } from "@react-three/drei";
|
import { useGLTF } from "@react-three/drei";
|
||||||
import { useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
import { TERRAIN_MODEL_PATH } from "@/data/world/terrainConfig";
|
import { TERRAIN_MODEL_PATH } from "@/data/world/terrainConfig";
|
||||||
import { flattenLaFabrikTerrainFootprint } from "@/data/world/laFabrikConfig";
|
|
||||||
import type { Vector3Tuple } from "@/types/three/three";
|
import type { Vector3Tuple } from "@/types/three/three";
|
||||||
import { optimizeGLTFSceneTextures } from "@/utils/three/optimizeGLTFScene";
|
import { optimizeGLTFSceneTextures } from "@/utils/three/optimizeGLTFScene";
|
||||||
|
|
||||||
@@ -66,10 +65,9 @@ export function TerrainModel({
|
|||||||
const terrainModel = useMemo(() => {
|
const terrainModel = useMemo(() => {
|
||||||
optimizeGLTFSceneTextures(scene, maxAnisotropy);
|
optimizeGLTFSceneTextures(scene, maxAnisotropy);
|
||||||
const model = scene.clone(true);
|
const model = scene.clone(true);
|
||||||
flattenLaFabrikTerrainFootprint(model, position, rotation, scale);
|
|
||||||
applyTerrainMaterialSettings(model, receiveShadow);
|
applyTerrainMaterialSettings(model, receiveShadow);
|
||||||
return model;
|
return model;
|
||||||
}, [maxAnisotropy, position, receiveShadow, rotation, scale, scene]);
|
}, [maxAnisotropy, scene, receiveShadow]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onLoaded?.();
|
onLoaded?.();
|
||||||
|
|||||||
@@ -15,5 +15,9 @@ export const PLAYER_XZ_DAMPING_FACTOR = 8;
|
|||||||
export const PLAYER_FALL_RESPAWN_Y = -20;
|
export const PLAYER_FALL_RESPAWN_Y = -20;
|
||||||
export const PLAYER_FALL_RESPAWN_DELAY = 3;
|
export const PLAYER_FALL_RESPAWN_DELAY = 3;
|
||||||
|
|
||||||
export const PLAYER_SPAWN_POSITION_GAME: Vector3Tuple = LA_FABRIK_PLAYER_SPAWN;
|
export const PLAYER_SPAWN_POSITION_GAME: Vector3Tuple = [
|
||||||
|
LA_FABRIK_PLAYER_SPAWN[0] + 5,
|
||||||
|
LA_FABRIK_PLAYER_SPAWN[1],
|
||||||
|
LA_FABRIK_PLAYER_SPAWN[2] + 5,
|
||||||
|
];
|
||||||
export const PLAYER_SPAWN_POSITION_PHYSICS: Vector3Tuple = [0, 3, 0];
|
export const PLAYER_SPAWN_POSITION_PHYSICS: Vector3Tuple = [0, 3, 0];
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ export interface CharacterConfig {
|
|||||||
scale: Vector3Tuple;
|
scale: Vector3Tuple;
|
||||||
animations: readonly string[];
|
animations: readonly string[];
|
||||||
defaultAnimation: string;
|
defaultAnimation: string;
|
||||||
snapToTerrain?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CHARACTER_CONFIGS = {
|
export const CHARACTER_CONFIGS = {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as THREE from "three";
|
|
||||||
import type { Vector3Tuple } from "@/types/three/three";
|
import type { Vector3Tuple } from "@/types/three/three";
|
||||||
|
|
||||||
export const LA_FABRIK_CENTER: Vector3Tuple = [59.4973, 6.2746, 64.6354];
|
export const LA_FABRIK_CENTER: Vector3Tuple = [59.4973, 6.2746, 64.6354];
|
||||||
@@ -7,15 +6,10 @@ export const LA_FABRIK_HALF_EXTENTS = {
|
|||||||
x: 8.5,
|
x: 8.5,
|
||||||
z: 7.5,
|
z: 7.5,
|
||||||
} as const;
|
} as const;
|
||||||
export const LA_FABRIK_FLOOR_Y = 6.3;
|
export const LA_FABRIK_PLAYER_SPAWN: Vector3Tuple = [59.5, 7.8, 64.64];
|
||||||
export const LA_FABRIK_PLAYER_SPAWN: Vector3Tuple = [59.5, 8.05, 64.64];
|
export const LA_FABRIK_INITIAL_LOOK_AT: Vector3Tuple = [58, 7.8, 62.5];
|
||||||
export const LA_FABRIK_INTERIOR_LIGHT_POSITION: Vector3Tuple = [59.5, 9, 64.64];
|
export const LA_FABRIK_INTERIOR_LIGHT_POSITION: Vector3Tuple = [59.5, 9, 64.64];
|
||||||
|
|
||||||
const _terrainMatrix = new THREE.Matrix4();
|
|
||||||
const _meshWorldMatrix = new THREE.Matrix4();
|
|
||||||
const _inverseMeshWorldMatrix = new THREE.Matrix4();
|
|
||||||
const _worldPosition = new THREE.Vector3();
|
|
||||||
|
|
||||||
export function isInsideLaFabrikFootprint(
|
export function isInsideLaFabrikFootprint(
|
||||||
x: number,
|
x: number,
|
||||||
z: number,
|
z: number,
|
||||||
@@ -33,51 +27,3 @@ export function isInsideLaFabrikFootprint(
|
|||||||
Math.abs(localZ) <= LA_FABRIK_HALF_EXTENTS.z + padding
|
Math.abs(localZ) <= LA_FABRIK_HALF_EXTENTS.z + padding
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function flattenLaFabrikTerrainFootprint(
|
|
||||||
object: THREE.Object3D,
|
|
||||||
position: Vector3Tuple,
|
|
||||||
rotation: Vector3Tuple,
|
|
||||||
scale: Vector3Tuple,
|
|
||||||
): void {
|
|
||||||
_terrainMatrix.compose(
|
|
||||||
new THREE.Vector3(...position),
|
|
||||||
new THREE.Quaternion().setFromEuler(new THREE.Euler(...rotation)),
|
|
||||||
new THREE.Vector3(...scale),
|
|
||||||
);
|
|
||||||
object.updateMatrixWorld(true);
|
|
||||||
|
|
||||||
object.traverse((child) => {
|
|
||||||
if (!(child instanceof THREE.Mesh)) return;
|
|
||||||
const geometry = child.geometry;
|
|
||||||
const positions = geometry.getAttribute("position");
|
|
||||||
if (!positions) return;
|
|
||||||
|
|
||||||
_meshWorldMatrix.multiplyMatrices(_terrainMatrix, child.matrixWorld);
|
|
||||||
_inverseMeshWorldMatrix.copy(_meshWorldMatrix).invert();
|
|
||||||
|
|
||||||
for (let index = 0; index < positions.count; index++) {
|
|
||||||
_worldPosition
|
|
||||||
.fromBufferAttribute(positions, index)
|
|
||||||
.applyMatrix4(_meshWorldMatrix);
|
|
||||||
|
|
||||||
if (!isInsideLaFabrikFootprint(_worldPosition.x, _worldPosition.z, 0.8)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_worldPosition.y = Math.min(_worldPosition.y, LA_FABRIK_FLOOR_Y - 0.35);
|
|
||||||
_worldPosition.applyMatrix4(_inverseMeshWorldMatrix);
|
|
||||||
positions.setXYZ(
|
|
||||||
index,
|
|
||||||
_worldPosition.x,
|
|
||||||
_worldPosition.y,
|
|
||||||
_worldPosition.z,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
positions.needsUpdate = true;
|
|
||||||
geometry.computeVertexNormals();
|
|
||||||
geometry.computeBoundingBox();
|
|
||||||
geometry.computeBoundingSphere();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,10 +2,6 @@ import { useMemo } from "react";
|
|||||||
import { useGLTF } from "@react-three/drei";
|
import { useGLTF } from "@react-three/drei";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { TERRAIN_MODEL_PATH } from "@/data/world/terrainConfig";
|
import { TERRAIN_MODEL_PATH } from "@/data/world/terrainConfig";
|
||||||
import {
|
|
||||||
isInsideLaFabrikFootprint,
|
|
||||||
LA_FABRIK_FLOOR_Y,
|
|
||||||
} from "@/data/world/laFabrikConfig";
|
|
||||||
import type { Vector3Tuple } from "@/types/three/three";
|
import type { Vector3Tuple } from "@/types/three/three";
|
||||||
import { getMapNodesByName } from "@/utils/map/loadMapSceneData";
|
import { getMapNodesByName } from "@/utils/map/loadMapSceneData";
|
||||||
|
|
||||||
@@ -70,10 +66,6 @@ function createTerrainHeightSampler(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
getHeight: (x, z) => {
|
getHeight: (x, z) => {
|
||||||
if (isInsideLaFabrikFootprint(x, z, 0.6)) {
|
|
||||||
return LA_FABRIK_FLOOR_Y;
|
|
||||||
}
|
|
||||||
|
|
||||||
localOrigin.set(x, RAYCAST_Y, z).applyMatrix4(inverseTerrainMatrix);
|
localOrigin.set(x, RAYCAST_Y, z).applyMatrix4(inverseTerrainMatrix);
|
||||||
raycaster.set(localOrigin, localDirection);
|
raycaster.set(localOrigin, localDirection);
|
||||||
hits.length = 0;
|
hits.length = 0;
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import {
|
|||||||
useTerrainHeightSampler,
|
useTerrainHeightSampler,
|
||||||
} from "@/hooks/three/useTerrainHeight";
|
} from "@/hooks/three/useTerrainHeight";
|
||||||
import { WorldBoundsCollision } from "@/world/collision/WorldBoundsCollision";
|
import { WorldBoundsCollision } from "@/world/collision/WorldBoundsCollision";
|
||||||
import { flattenLaFabrikTerrainFootprint } from "@/data/world/laFabrikConfig";
|
|
||||||
import type { MapNode } from "@/types/map/mapScene";
|
import type { MapNode } from "@/types/map/mapScene";
|
||||||
import type { OctreeReadyHandler } from "@/types/three/three";
|
import type { OctreeReadyHandler } from "@/types/three/three";
|
||||||
import type { SceneLoadingChangeHandler } from "@/types/world/sceneLoading";
|
import type { SceneLoadingChangeHandler } from "@/types/world/sceneLoading";
|
||||||
@@ -214,7 +213,7 @@ function CollisionModelInstance({
|
|||||||
modelUrl: string;
|
modelUrl: string;
|
||||||
onLoaded: () => void;
|
onLoaded: () => void;
|
||||||
terrainHeight: TerrainHeightSampler;
|
terrainHeight: TerrainHeightSampler;
|
||||||
}): React.JSX.Element | null {
|
}): React.JSX.Element {
|
||||||
const { position, rotation, scale } = node;
|
const { position, rotation, scale } = node;
|
||||||
const normalizedScale = normalizeMapScale(scale);
|
const normalizedScale = normalizeMapScale(scale);
|
||||||
const { scene } = useLoggedGLTF(modelUrl, {
|
const { scene } = useLoggedGLTF(modelUrl, {
|
||||||
@@ -224,46 +223,22 @@ function CollisionModelInstance({
|
|||||||
scale: normalizedScale,
|
scale: normalizedScale,
|
||||||
});
|
});
|
||||||
const sceneInstance = useClonedObject(scene);
|
const sceneInstance = useClonedObject(scene);
|
||||||
const collisionSceneInstance = useMemo(() => {
|
|
||||||
if (node.name === "terrain") {
|
|
||||||
flattenLaFabrikTerrainFootprint(
|
|
||||||
sceneInstance,
|
|
||||||
position,
|
|
||||||
rotation,
|
|
||||||
normalizedScale,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return sceneInstance;
|
|
||||||
}, [node.name, normalizedScale, position, rotation, sceneInstance]);
|
|
||||||
const collisionPosition = useMemo(() => {
|
const collisionPosition = useMemo(() => {
|
||||||
if (node.name === "terrain") return position;
|
if (node.name === "terrain") return position;
|
||||||
|
|
||||||
const [x, y, z] = position;
|
const [x, y, z] = position;
|
||||||
const height = terrainHeight.getHeight(x, z);
|
const height = terrainHeight.getHeight(x, z);
|
||||||
const bottomOffset = getObjectBottomOffset(
|
const bottomOffset = getObjectBottomOffset(sceneInstance, normalizedScale);
|
||||||
collisionSceneInstance,
|
|
||||||
normalizedScale,
|
|
||||||
);
|
|
||||||
return [x, height !== null ? height + bottomOffset : y, z] as const;
|
return [x, height !== null ? height + bottomOffset : y, z] as const;
|
||||||
}, [
|
}, [node.name, normalizedScale, position, sceneInstance, terrainHeight]);
|
||||||
node.name,
|
|
||||||
normalizedScale,
|
|
||||||
position,
|
|
||||||
collisionSceneInstance,
|
|
||||||
terrainHeight,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onLoaded();
|
onLoaded();
|
||||||
}, [onLoaded]);
|
}, [onLoaded]);
|
||||||
|
|
||||||
if (node.name === "lafabrik") {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<primitive
|
<primitive
|
||||||
object={collisionSceneInstance}
|
object={sceneInstance}
|
||||||
position={collisionPosition}
|
position={collisionPosition}
|
||||||
rotation={rotation}
|
rotation={rotation}
|
||||||
scale={normalizedScale}
|
scale={normalizedScale}
|
||||||
|
|||||||
+6
-1
@@ -4,6 +4,7 @@ import {
|
|||||||
PLAYER_SPAWN_POSITION_GAME,
|
PLAYER_SPAWN_POSITION_GAME,
|
||||||
PLAYER_SPAWN_POSITION_PHYSICS,
|
PLAYER_SPAWN_POSITION_PHYSICS,
|
||||||
} from "@/data/player/playerConfig";
|
} from "@/data/player/playerConfig";
|
||||||
|
import { LA_FABRIK_INITIAL_LOOK_AT } from "@/data/world/laFabrikConfig";
|
||||||
import { useCameraMode } from "@/hooks/debug/useCameraMode";
|
import { useCameraMode } from "@/hooks/debug/useCameraMode";
|
||||||
import { useEnvironmentDebug } from "@/hooks/debug/useEnvironmentDebug";
|
import { useEnvironmentDebug } from "@/hooks/debug/useEnvironmentDebug";
|
||||||
import { useMapPerformanceDebug } from "@/hooks/debug/useMapPerformanceDebug";
|
import { useMapPerformanceDebug } from "@/hooks/debug/useMapPerformanceDebug";
|
||||||
@@ -99,7 +100,11 @@ export function World({ onLoadingStateChange }: WorldProps): React.JSX.Element {
|
|||||||
<GameMusic />
|
<GameMusic />
|
||||||
{mainState === "outro" ? <GameCinematics /> : null}
|
{mainState === "outro" ? <GameCinematics /> : null}
|
||||||
{mainState !== "intro" ? <GameDialogues /> : null}
|
{mainState !== "intro" ? <GameDialogues /> : null}
|
||||||
<Player octree={octree} spawnPosition={playerSpawnPosition} />
|
<Player
|
||||||
|
initialLookAt={LA_FABRIK_INITIAL_LOOK_AT}
|
||||||
|
octree={octree}
|
||||||
|
spawnPosition={playerSpawnPosition}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -3,18 +3,15 @@ import { AnimatedModel } from "@/components/three/models/AnimatedModel";
|
|||||||
import {
|
import {
|
||||||
CHARACTER_CONFIGS,
|
CHARACTER_CONFIGS,
|
||||||
CHARACTER_IDS,
|
CHARACTER_IDS,
|
||||||
type CharacterConfig,
|
|
||||||
type CharacterId,
|
type CharacterId,
|
||||||
} from "@/data/world/characters/characterConfig";
|
} from "@/data/world/characters/characterConfig";
|
||||||
import { useTerrainSnappedPosition } from "@/hooks/three/useTerrainHeight";
|
import { useTerrainSnappedPosition } from "@/hooks/three/useTerrainHeight";
|
||||||
import { useCharacterDebugStore } from "@/managers/stores/useCharacterDebugStore";
|
import { useCharacterDebugStore } from "@/managers/stores/useCharacterDebugStore";
|
||||||
|
|
||||||
function CharacterModel({ id }: { id: CharacterId }): React.JSX.Element {
|
function CharacterModel({ id }: { id: CharacterId }): React.JSX.Element {
|
||||||
const config: CharacterConfig = CHARACTER_CONFIGS[id];
|
const config = CHARACTER_CONFIGS[id];
|
||||||
const state = useCharacterDebugStore((store) => store.characters[id]);
|
const state = useCharacterDebugStore((store) => store.characters[id]);
|
||||||
const snappedPosition = useTerrainSnappedPosition(state.position);
|
const position = useTerrainSnappedPosition(state.position);
|
||||||
const position =
|
|
||||||
config.snapToTerrain === false ? state.position : snappedPosition;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatedModel
|
<AnimatedModel
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ export function GeneratedMapNodeInstance({
|
|||||||
node,
|
node,
|
||||||
onLoaded,
|
onLoaded,
|
||||||
}: GeneratedMapNodeInstanceProps): React.JSX.Element | null {
|
}: GeneratedMapNodeInstanceProps): React.JSX.Element | null {
|
||||||
const snappedPosition = useTerrainSnappedPosition(node.position);
|
const position = useTerrainSnappedPosition(node.position);
|
||||||
const position = node.name === "lafabrik" ? node.position : snappedPosition;
|
|
||||||
const scale = normalizeMapScale(node.scale);
|
const scale = normalizeMapScale(node.scale);
|
||||||
|
|
||||||
if (node.name === "ecole") {
|
if (node.name === "ecole") {
|
||||||
|
|||||||
@@ -7,10 +7,12 @@ import { PlayerController } from "@/world/player/PlayerController";
|
|||||||
|
|
||||||
interface PlayerProps {
|
interface PlayerProps {
|
||||||
octree: Octree | null;
|
octree: Octree | null;
|
||||||
|
initialLookAt?: Vector3Tuple | undefined;
|
||||||
spawnPosition: Vector3Tuple;
|
spawnPosition: Vector3Tuple;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Player({
|
export function Player({
|
||||||
|
initialLookAt,
|
||||||
spawnPosition,
|
spawnPosition,
|
||||||
octree,
|
octree,
|
||||||
}: PlayerProps): React.JSX.Element {
|
}: PlayerProps): React.JSX.Element {
|
||||||
@@ -18,12 +20,17 @@ export function Player({
|
|||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
camera.position.set(...spawnPosition);
|
camera.position.set(...spawnPosition);
|
||||||
}, [camera, spawnPosition]);
|
if (initialLookAt) camera.lookAt(...initialLookAt);
|
||||||
|
}, [camera, initialLookAt, spawnPosition]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PlayerCamera />
|
<PlayerCamera />
|
||||||
<PlayerController octree={octree} spawnPosition={spawnPosition} />
|
<PlayerController
|
||||||
|
initialLookAt={initialLookAt}
|
||||||
|
octree={octree}
|
||||||
|
spawnPosition={spawnPosition}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ const PLAYER_FLOOR_NORMAL_MIN = 0.15;
|
|||||||
const PLAYER_GROUND_SNAP_DISTANCE = 0.22;
|
const PLAYER_GROUND_SNAP_DISTANCE = 0.22;
|
||||||
|
|
||||||
interface PlayerControllerProps {
|
interface PlayerControllerProps {
|
||||||
|
initialLookAt?: Vector3Tuple | undefined;
|
||||||
octree: Octree | null;
|
octree: Octree | null;
|
||||||
spawnPosition: Vector3Tuple;
|
spawnPosition: Vector3Tuple;
|
||||||
}
|
}
|
||||||
@@ -89,6 +90,7 @@ const _collisionCorrection = new THREE.Vector3();
|
|||||||
function resetPlayerCapsule(
|
function resetPlayerCapsule(
|
||||||
capsule: Capsule,
|
capsule: Capsule,
|
||||||
spawnPosition: Vector3Tuple,
|
spawnPosition: Vector3Tuple,
|
||||||
|
initialLookAt: Vector3Tuple | undefined,
|
||||||
camera: THREE.Camera,
|
camera: THREE.Camera,
|
||||||
velocity: THREE.Vector3,
|
velocity: THREE.Vector3,
|
||||||
): void {
|
): void {
|
||||||
@@ -100,6 +102,7 @@ function resetPlayerCapsule(
|
|||||||
capsule.end.set(...spawnPosition);
|
capsule.end.set(...spawnPosition);
|
||||||
velocity.set(0, 0, 0);
|
velocity.set(0, 0, 0);
|
||||||
camera.position.copy(capsule.end);
|
camera.position.copy(capsule.end);
|
||||||
|
if (initialLookAt) camera.lookAt(...initialLookAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSpawnCapsule(spawnPosition: Vector3Tuple): Capsule {
|
function createSpawnCapsule(spawnPosition: Vector3Tuple): Capsule {
|
||||||
@@ -145,6 +148,7 @@ function getCapsuleFootY(capsule: Capsule): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function PlayerController({
|
export function PlayerController({
|
||||||
|
initialLookAt,
|
||||||
octree,
|
octree,
|
||||||
spawnPosition,
|
spawnPosition,
|
||||||
}: PlayerControllerProps): null {
|
}: PlayerControllerProps): null {
|
||||||
@@ -234,6 +238,7 @@ export function PlayerController({
|
|||||||
resetPlayerCapsule(
|
resetPlayerCapsule(
|
||||||
capsule.current,
|
capsule.current,
|
||||||
spawnPosition,
|
spawnPosition,
|
||||||
|
initialLookAt,
|
||||||
camera,
|
camera,
|
||||||
velocity.current,
|
velocity.current,
|
||||||
);
|
);
|
||||||
@@ -241,7 +246,7 @@ export function PlayerController({
|
|||||||
onFloor.current = false;
|
onFloor.current = false;
|
||||||
wantsJump.current = false;
|
wantsJump.current = false;
|
||||||
initializedRef.current = true;
|
initializedRef.current = true;
|
||||||
}, [camera, spawnPosition]);
|
}, [camera, initialLookAt, spawnPosition]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
movementLockedRef.current = movementLocked;
|
movementLockedRef.current = movementLocked;
|
||||||
@@ -339,6 +344,7 @@ export function PlayerController({
|
|||||||
resetPlayerCapsule(
|
resetPlayerCapsule(
|
||||||
capsule.current,
|
capsule.current,
|
||||||
spawnPosition,
|
spawnPosition,
|
||||||
|
initialLookAt,
|
||||||
camera,
|
camera,
|
||||||
velocity.current,
|
velocity.current,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
VEGETATION_TYPES,
|
VEGETATION_TYPES,
|
||||||
type VegetationType,
|
type VegetationType,
|
||||||
} from "@/data/world/vegetationConfig";
|
} from "@/data/world/vegetationConfig";
|
||||||
|
import { isInsideLaFabrikFootprint } from "@/data/world/laFabrikConfig";
|
||||||
import { createWorldInstanceChunks } from "@/utils/world/chunkInstances";
|
import { createWorldInstanceChunks } from "@/utils/world/chunkInstances";
|
||||||
|
|
||||||
interface VegetationSystemProps {
|
interface VegetationSystemProps {
|
||||||
@@ -60,6 +61,15 @@ function createVegetationChunks(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeLaFabrikVegetation(
|
||||||
|
instances: VegetationInstance[],
|
||||||
|
): VegetationInstance[] {
|
||||||
|
return instances.filter((instance) => {
|
||||||
|
const [x, , z] = instance.position;
|
||||||
|
return !isInsideLaFabrikFootprint(x, z, 1.2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function VegetationSystem({
|
export function VegetationSystem({
|
||||||
onlyMapName = null,
|
onlyMapName = null,
|
||||||
streaming = true,
|
streaming = true,
|
||||||
@@ -90,7 +100,10 @@ export function VegetationSystem({
|
|||||||
const entry = data.get(config.mapName);
|
const entry = data.get(config.mapName);
|
||||||
if (!entry || entry.instances.length === 0) return [];
|
if (!entry || entry.instances.length === 0) return [];
|
||||||
|
|
||||||
return createVegetationChunks(type, entry.instances);
|
const instances = removeLaFabrikVegetation(entry.instances);
|
||||||
|
if (instances.length === 0) return [];
|
||||||
|
|
||||||
|
return createVegetationChunks(type, instances);
|
||||||
});
|
});
|
||||||
}, [data, groups, models, onlyMapName]);
|
}, [data, groups, models, onlyMapName]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user