Feat/polish-mission1 #12
@@ -3,6 +3,7 @@ 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";
|
||||||
|
|
||||||
@@ -65,9 +66,10 @@ 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, scene, receiveShadow]);
|
}, [maxAnisotropy, position, receiveShadow, rotation, scale, scene]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onLoaded?.();
|
onLoaded?.();
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const EBIKE_DROP_PLAYER_TRANSFORM: CameraTransform = {
|
|||||||
rotation: [0, 0, 0],
|
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_WORLD_ROTATION_Y = 2.4107;
|
||||||
|
|
||||||
export const EBIKE_INTRO_RIDE_DURATION_MS = 5000;
|
export const EBIKE_INTRO_RIDE_DURATION_MS = 5000;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { Vector3Tuple } from "@/types/three/three";
|
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_EYE_HEIGHT = 1.75;
|
||||||
export const PLAYER_CAPSULE_RADIUS = 0.35;
|
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_Y = -20;
|
||||||
export const PLAYER_FALL_RESPAWN_DELAY = 3;
|
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];
|
export const PLAYER_SPAWN_POSITION_PHYSICS: Vector3Tuple = [0, 3, 0];
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ 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 = {
|
||||||
@@ -28,11 +29,12 @@ export const CHARACTER_CONFIGS = {
|
|||||||
id: "gerant",
|
id: "gerant",
|
||||||
label: "Gerant",
|
label: "Gerant",
|
||||||
modelPath: "/models/gerant-animated/model.gltf",
|
modelPath: "/models/gerant-animated/model.gltf",
|
||||||
position: [59.5, 0, 64.64],
|
position: [59.5, 6.3, 64.64],
|
||||||
rotation: [0, 2.41, 0],
|
rotation: [0, 2.41, 0],
|
||||||
scale: [1, 1, 1],
|
scale: [1, 1, 1],
|
||||||
animations: ["idle", "walk"],
|
animations: ["idle", "walk"],
|
||||||
defaultAnimation: "idle",
|
defaultAnimation: "idle",
|
||||||
|
snapToTerrain: false,
|
||||||
},
|
},
|
||||||
fermier: {
|
fermier: {
|
||||||
id: "fermier",
|
id: "fermier",
|
||||||
|
|||||||
@@ -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();
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -2,6 +2,10 @@ 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";
|
||||||
|
|
||||||
@@ -66,6 +70,10 @@ 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,6 +18,7 @@ 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";
|
||||||
@@ -213,7 +214,7 @@ function CollisionModelInstance({
|
|||||||
modelUrl: string;
|
modelUrl: string;
|
||||||
onLoaded: () => void;
|
onLoaded: () => void;
|
||||||
terrainHeight: TerrainHeightSampler;
|
terrainHeight: TerrainHeightSampler;
|
||||||
}): React.JSX.Element {
|
}): React.JSX.Element | null {
|
||||||
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, {
|
||||||
@@ -223,22 +224,46 @@ 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(sceneInstance, normalizedScale);
|
const bottomOffset = getObjectBottomOffset(
|
||||||
|
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={sceneInstance}
|
object={collisionSceneInstance}
|
||||||
position={collisionPosition}
|
position={collisionPosition}
|
||||||
rotation={rotation}
|
rotation={rotation}
|
||||||
scale={normalizedScale}
|
scale={normalizedScale}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
SUN_Z_MIN,
|
SUN_Z_MIN,
|
||||||
SUN_Z_STEP,
|
SUN_Z_STEP,
|
||||||
} from "@/data/world/lightingConfig";
|
} from "@/data/world/lightingConfig";
|
||||||
|
import { LA_FABRIK_INTERIOR_LIGHT_POSITION } from "@/data/world/laFabrikConfig";
|
||||||
import { useDebugFolder } from "@/hooks/debug/useDebugFolder";
|
import { useDebugFolder } from "@/hooks/debug/useDebugFolder";
|
||||||
import { LIGHTING_STATE } from "@/world/lightingState";
|
import { LIGHTING_STATE } from "@/world/lightingState";
|
||||||
|
|
||||||
@@ -121,6 +122,13 @@ export function Lighting(): React.JSX.Element {
|
|||||||
castShadow
|
castShadow
|
||||||
/>
|
/>
|
||||||
<object3D ref={sunTarget} />
|
<object3D ref={sunTarget} />
|
||||||
|
<pointLight
|
||||||
|
position={LA_FABRIK_INTERIOR_LIGHT_POSITION}
|
||||||
|
color="#dbeafe"
|
||||||
|
intensity={1.2}
|
||||||
|
distance={14}
|
||||||
|
decay={1.6}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,18 @@ 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 = CHARACTER_CONFIGS[id];
|
const config: CharacterConfig = CHARACTER_CONFIGS[id];
|
||||||
const state = useCharacterDebugStore((store) => store.characters[id]);
|
const state = useCharacterDebugStore((store) => store.characters[id]);
|
||||||
const position = useTerrainSnappedPosition(state.position);
|
const snappedPosition = useTerrainSnappedPosition(state.position);
|
||||||
|
const position =
|
||||||
|
config.snapToTerrain === false ? state.position : snappedPosition;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatedModel
|
<AnimatedModel
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ import {
|
|||||||
GRASS_COLORS,
|
GRASS_COLORS,
|
||||||
GRASS_CONFIG,
|
GRASS_CONFIG,
|
||||||
} from "@/data/world/grassConfig";
|
} from "@/data/world/grassConfig";
|
||||||
|
import {
|
||||||
|
LA_FABRIK_CENTER,
|
||||||
|
LA_FABRIK_HALF_EXTENTS,
|
||||||
|
LA_FABRIK_ROTATION_Y,
|
||||||
|
} from "@/data/world/laFabrikConfig";
|
||||||
import {
|
import {
|
||||||
grassFragmentShader,
|
grassFragmentShader,
|
||||||
grassVertexShader,
|
grassVertexShader,
|
||||||
@@ -169,6 +174,17 @@ function createGrassMaterial(
|
|||||||
uMaxBladeHeight: { value: GRASS_CONFIG.maxBladeHeight },
|
uMaxBladeHeight: { value: GRASS_CONFIG.maxBladeHeight },
|
||||||
uRandomHeightAmount: { value: GRASS_CONFIG.randomHeightAmount },
|
uRandomHeightAmount: { value: GRASS_CONFIG.randomHeightAmount },
|
||||||
uSurfaceOffset: { value: GRASS_CONFIG.surfaceOffset },
|
uSurfaceOffset: { value: GRASS_CONFIG.surfaceOffset },
|
||||||
|
uLaFabrikCenter: {
|
||||||
|
value: new THREE.Vector2(LA_FABRIK_CENTER[0], LA_FABRIK_CENTER[2]),
|
||||||
|
},
|
||||||
|
uLaFabrikHalfExtents: {
|
||||||
|
value: new THREE.Vector2(
|
||||||
|
LA_FABRIK_HALF_EXTENTS.x,
|
||||||
|
LA_FABRIK_HALF_EXTENTS.z,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
uLaFabrikRotation: { value: LA_FABRIK_ROTATION_Y },
|
||||||
|
uLaFabrikNoGrassFeather: { value: 1.4 },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ export const grassVertexShader = /* glsl */ `
|
|||||||
uniform float uMaxBladeHeight;
|
uniform float uMaxBladeHeight;
|
||||||
uniform float uRandomHeightAmount;
|
uniform float uRandomHeightAmount;
|
||||||
uniform float uSurfaceOffset;
|
uniform float uSurfaceOffset;
|
||||||
|
uniform vec2 uLaFabrikCenter;
|
||||||
|
uniform vec2 uLaFabrikHalfExtents;
|
||||||
|
uniform float uLaFabrikRotation;
|
||||||
|
uniform float uLaFabrikNoGrassFeather;
|
||||||
|
|
||||||
float random(vec2 st) {
|
float random(vec2 st) {
|
||||||
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
|
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
|
||||||
@@ -132,6 +136,18 @@ export const grassVertexShader = /* glsl */ `
|
|||||||
smoothstep(uBoundingBoxMax.z, uBoundingBoxMax.z - 2.0, worldPos.z);
|
smoothstep(uBoundingBoxMax.z, uBoundingBoxMax.z - 2.0, worldPos.z);
|
||||||
heightModifier *= edgeFade * mix(0.45, 1.0, clumpMask);
|
heightModifier *= edgeFade * mix(0.45, 1.0, clumpMask);
|
||||||
|
|
||||||
|
vec2 laFabrikDelta = worldPos.xz - uLaFabrikCenter;
|
||||||
|
float laFabrikCos = cos(-uLaFabrikRotation);
|
||||||
|
float laFabrikSin = sin(-uLaFabrikRotation);
|
||||||
|
vec2 laFabrikLocal = vec2(
|
||||||
|
laFabrikDelta.x * laFabrikCos - laFabrikDelta.y * laFabrikSin,
|
||||||
|
laFabrikDelta.x * laFabrikSin + laFabrikDelta.y * laFabrikCos
|
||||||
|
);
|
||||||
|
vec2 laFabrikDistance = abs(laFabrikLocal) - uLaFabrikHalfExtents;
|
||||||
|
float laFabrikOutsideDistance = max(laFabrikDistance.x, laFabrikDistance.y);
|
||||||
|
float laFabrikGrassMask = smoothstep(0.0, uLaFabrikNoGrassFeather, laFabrikOutsideDistance);
|
||||||
|
heightModifier *= laFabrikGrassMask;
|
||||||
|
|
||||||
float sideFactor = (color.r == 0.1) ? 1.0 : (color.b == 0.1) ? -1.0 : 0.0;
|
float sideFactor = (color.r == 0.1) ? 1.0 : (color.b == 0.1) ? -1.0 : 0.0;
|
||||||
float tipFactor = color.g;
|
float tipFactor = color.g;
|
||||||
float width = smoothstep(0.02, uMaxBladeHeight * 0.85, heightModifier) * uBladeWidth * bladeVisibility;
|
float width = smoothstep(0.02, uMaxBladeHeight * 0.85, heightModifier) * uBladeWidth * bladeVisibility;
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ export function GeneratedMapNodeInstance({
|
|||||||
node,
|
node,
|
||||||
onLoaded,
|
onLoaded,
|
||||||
}: GeneratedMapNodeInstanceProps): React.JSX.Element | null {
|
}: GeneratedMapNodeInstanceProps): React.JSX.Element | null {
|
||||||
const position = useTerrainSnappedPosition(node.position);
|
const snappedPosition = 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") {
|
||||||
|
|||||||
Reference in New Issue
Block a user