upatde(fabrik): zone + herbe
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled

This commit is contained in:
Tom Boullay
2026-06-01 00:14:39 +02:00
parent c33d973f12
commit 27b4a2c392
12 changed files with 176 additions and 11 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ export const EBIKE_DROP_PLAYER_TRANSFORM: CameraTransform = {
rotation: [0, 0, 0],
};
export const EBIKE_WORLD_POSITION: Vector3Tuple = [61.5, 10, 62.4];
export const EBIKE_WORLD_POSITION: Vector3Tuple = [61.5, 8.4, 62.4];
export const EBIKE_WORLD_ROTATION_Y = 2.4107;
export const EBIKE_INTRO_RIDE_DURATION_MS = 5000;
+2 -1
View File
@@ -1,4 +1,5 @@
import type { Vector3Tuple } from "@/types/three/three";
import { LA_FABRIK_PLAYER_SPAWN } from "@/data/world/laFabrikConfig";
export const PLAYER_EYE_HEIGHT = 1.75;
export const PLAYER_CAPSULE_RADIUS = 0.35;
@@ -14,5 +15,5 @@ export const PLAYER_XZ_DAMPING_FACTOR = 8;
export const PLAYER_FALL_RESPAWN_Y = -20;
export const PLAYER_FALL_RESPAWN_DELAY = 3;
export const PLAYER_SPAWN_POSITION_GAME: Vector3Tuple = [59.5, 10, 64.64];
export const PLAYER_SPAWN_POSITION_GAME: Vector3Tuple = LA_FABRIK_PLAYER_SPAWN;
export const PLAYER_SPAWN_POSITION_PHYSICS: Vector3Tuple = [0, 3, 0];
+3 -1
View File
@@ -11,6 +11,7 @@ export interface CharacterConfig {
scale: Vector3Tuple;
animations: readonly string[];
defaultAnimation: string;
snapToTerrain?: boolean;
}
export const CHARACTER_CONFIGS = {
@@ -28,11 +29,12 @@ export const CHARACTER_CONFIGS = {
id: "gerant",
label: "Gerant",
modelPath: "/models/gerant-animated/model.gltf",
position: [59.5, 0, 64.64],
position: [59.5, 6.3, 64.64],
rotation: [0, 2.41, 0],
scale: [1, 1, 1],
animations: ["idle", "walk"],
defaultAnimation: "idle",
snapToTerrain: false,
},
fermier: {
id: "fermier",
+83
View File
@@ -0,0 +1,83 @@
import * as THREE from "three";
import type { Vector3Tuple } from "@/types/three/three";
export const LA_FABRIK_CENTER: Vector3Tuple = [59.4973, 6.2746, 64.6354];
export const LA_FABRIK_ROTATION_Y = 2.4107;
export const LA_FABRIK_HALF_EXTENTS = {
x: 8.5,
z: 7.5,
} as const;
export const LA_FABRIK_FLOOR_Y = 6.3;
export const LA_FABRIK_PLAYER_SPAWN: Vector3Tuple = [59.5, 8.05, 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(
x: number,
z: number,
padding = 0,
): boolean {
const dx = x - LA_FABRIK_CENTER[0];
const dz = z - LA_FABRIK_CENTER[2];
const cos = Math.cos(-LA_FABRIK_ROTATION_Y);
const sin = Math.sin(-LA_FABRIK_ROTATION_Y);
const localX = dx * cos - dz * sin;
const localZ = dx * sin + dz * cos;
return (
Math.abs(localX) <= LA_FABRIK_HALF_EXTENTS.x + 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();
});
}