Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 153833deec | |||
| b617885aa2 | |||
| 5d2e7e2aab | |||
| de77f76d48 |
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
+3
-5
@@ -942,11 +942,9 @@ canvas {
|
||||
.scene-loading-overlay__logo {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: clamp(180px, 28vw, 320px);
|
||||
max-height: min(38vh, 320px);
|
||||
border-radius: 16px;
|
||||
object-fit: cover;
|
||||
box-shadow: 0 28px 80px rgba(0, 0, 0, 0.28);
|
||||
width: clamp(207px, 32.2vw, 368px);
|
||||
max-height: min(43.7vh, 368px);
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.scene-loading-overlay__footer {
|
||||
|
||||
+2
-2
@@ -130,7 +130,7 @@ export function HomePage(): React.JSX.Element | null {
|
||||
|
||||
gl.shadowMap.enabled = true;
|
||||
gl.shadowMap.type = THREE.PCFShadowMap;
|
||||
gl.shadowMap.autoUpdate = false;
|
||||
gl.shadowMap.autoUpdate = true;
|
||||
gl.shadowMap.needsUpdate = true;
|
||||
|
||||
// The browser hands us a WEBGL_lose_context extension we can use to
|
||||
@@ -148,7 +148,7 @@ export function HomePage(): React.JSX.Element | null {
|
||||
const handleContextRestored = () => {
|
||||
gl.shadowMap.enabled = true;
|
||||
gl.shadowMap.type = THREE.PCFShadowMap;
|
||||
gl.shadowMap.autoUpdate = false;
|
||||
gl.shadowMap.autoUpdate = true;
|
||||
gl.shadowMap.needsUpdate = true;
|
||||
logger.info("WebGL", "Context restored");
|
||||
};
|
||||
|
||||
@@ -223,6 +223,22 @@ function CollisionModelInstance({
|
||||
scale: normalizedScale,
|
||||
});
|
||||
const sceneInstance = useClonedObject(scene);
|
||||
useEffect(() => {
|
||||
// Strip the door slab from the la fabrik collision octree so the player
|
||||
// can walk through the doorway. The visual model is rendered separately
|
||||
// by MergedStaticMapModel and is unaffected.
|
||||
if (node.name !== "lafabrik") return;
|
||||
|
||||
const removed: THREE.Object3D[] = [];
|
||||
sceneInstance.traverse((child) => {
|
||||
if (child.name === "porte") {
|
||||
removed.push(child);
|
||||
}
|
||||
});
|
||||
for (const child of removed) {
|
||||
child.removeFromParent();
|
||||
}
|
||||
}, [node.name, sceneInstance]);
|
||||
const collisionPosition = useMemo(() => {
|
||||
if (node.name === "terrain") return position;
|
||||
|
||||
|
||||
+6
-59
@@ -1,11 +1,8 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import type { MutableRefObject } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import {
|
||||
PCFShadowMap,
|
||||
Vector3,
|
||||
type AmbientLight,
|
||||
type Camera,
|
||||
type DirectionalLight,
|
||||
type Object3D,
|
||||
type WebGLRenderer,
|
||||
@@ -35,21 +32,17 @@ const SHADOW_MAP_SIZE = 2048;
|
||||
const SHADOW_CAMERA_SIZE = 95;
|
||||
const SHADOW_CAMERA_NEAR = 0.5;
|
||||
const SHADOW_CAMERA_FAR = 300;
|
||||
const SHADOW_REFRESH_INTERVAL_MS = 180;
|
||||
const SHADOW_REFRESH_DISTANCE = 0.75;
|
||||
const SHADOW_REFRESH_DISTANCE_SQUARED =
|
||||
SHADOW_REFRESH_DISTANCE * SHADOW_REFRESH_DISTANCE;
|
||||
|
||||
function configureManualRendererShadows(gl: WebGLRenderer): void {
|
||||
function configureRendererShadows(gl: WebGLRenderer): void {
|
||||
gl.shadowMap.enabled = true;
|
||||
gl.shadowMap.type = PCFShadowMap;
|
||||
gl.shadowMap.autoUpdate = false;
|
||||
gl.shadowMap.autoUpdate = true;
|
||||
gl.shadowMap.needsUpdate = true;
|
||||
}
|
||||
|
||||
function configureSunShadow(sun: DirectionalLight, sunTarget: Object3D): void {
|
||||
sun.target = sunTarget;
|
||||
sun.shadow.autoUpdate = false;
|
||||
sun.shadow.autoUpdate = true;
|
||||
sun.shadow.needsUpdate = true;
|
||||
sun.shadow.mapSize.width = SHADOW_MAP_SIZE;
|
||||
sun.shadow.mapSize.height = SHADOW_MAP_SIZE;
|
||||
@@ -62,56 +55,18 @@ function configureSunShadow(sun: DirectionalLight, sunTarget: Object3D): void {
|
||||
sun.shadow.camera.updateProjectionMatrix();
|
||||
}
|
||||
|
||||
function requestSunShadowRefresh({
|
||||
camera,
|
||||
elapsedMs,
|
||||
gl,
|
||||
lastCameraPosition,
|
||||
lastRefreshMs,
|
||||
shadowHasInitialPosition,
|
||||
sun,
|
||||
}: {
|
||||
camera: Camera;
|
||||
elapsedMs: number;
|
||||
gl: WebGLRenderer;
|
||||
lastCameraPosition: Vector3;
|
||||
lastRefreshMs: MutableRefObject<number>;
|
||||
shadowHasInitialPosition: MutableRefObject<boolean>;
|
||||
sun: DirectionalLight;
|
||||
}): void {
|
||||
if (elapsedMs - lastRefreshMs.current < SHADOW_REFRESH_INTERVAL_MS) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cameraMovedEnough =
|
||||
!shadowHasInitialPosition.current ||
|
||||
lastCameraPosition.distanceToSquared(camera.position) >=
|
||||
SHADOW_REFRESH_DISTANCE_SQUARED;
|
||||
|
||||
if (!cameraMovedEnough) return;
|
||||
|
||||
configureManualRendererShadows(gl);
|
||||
sun.shadow.needsUpdate = true;
|
||||
lastCameraPosition.copy(camera.position);
|
||||
lastRefreshMs.current = elapsedMs;
|
||||
shadowHasInitialPosition.current = true;
|
||||
}
|
||||
|
||||
export function Lighting(): React.JSX.Element {
|
||||
const camera = useThree((state) => state.camera);
|
||||
const gl = useThree((state) => state.gl);
|
||||
const ambient = useRef<AmbientLight>(null);
|
||||
const sun = useRef<DirectionalLight>(null);
|
||||
const sunTarget = useRef<Object3D>(null);
|
||||
const lastShadowRefreshMs = useRef(-SHADOW_REFRESH_INTERVAL_MS);
|
||||
const lastShadowCameraPosition = useRef(new Vector3());
|
||||
const shadowHasInitialPosition = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!sun.current || !sunTarget.current) return;
|
||||
|
||||
configureSunShadow(sun.current, sunTarget.current);
|
||||
configureManualRendererShadows(gl);
|
||||
configureRendererShadows(gl);
|
||||
}, [gl]);
|
||||
|
||||
useDebugFolder("Lighting", (folder) => {
|
||||
@@ -146,7 +101,7 @@ export function Lighting(): React.JSX.Element {
|
||||
.name("Sun Z");
|
||||
});
|
||||
|
||||
useFrame(({ clock }) => {
|
||||
useFrame(() => {
|
||||
if (ambient.current) {
|
||||
ambient.current.color.set(LIGHTING_STATE.ambientColor);
|
||||
ambient.current.intensity = LIGHTING_STATE.ambientIntensity;
|
||||
@@ -163,15 +118,7 @@ export function Lighting(): React.JSX.Element {
|
||||
sun.current.color.set(LIGHTING_STATE.sunColor);
|
||||
sun.current.intensity = LIGHTING_STATE.sunIntensity;
|
||||
sun.current.updateMatrixWorld();
|
||||
requestSunShadowRefresh({
|
||||
camera,
|
||||
elapsedMs: clock.elapsedTime * 1000,
|
||||
gl,
|
||||
lastCameraPosition: lastShadowCameraPosition.current,
|
||||
lastRefreshMs: lastShadowRefreshMs,
|
||||
shadowHasInitialPosition,
|
||||
sun: sun.current,
|
||||
});
|
||||
sun.current.shadow.needsUpdate = true;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -45,11 +45,6 @@ function forceSceneShadowPass(
|
||||
});
|
||||
}
|
||||
|
||||
function restoreManualShadowUpdates(gl: THREE.WebGLRenderer): void {
|
||||
gl.shadowMap.autoUpdate = false;
|
||||
gl.shadowMap.needsUpdate = true;
|
||||
}
|
||||
|
||||
export function SceneShadowWarmup({
|
||||
active,
|
||||
onReady,
|
||||
@@ -82,7 +77,6 @@ export function SceneShadowWarmup({
|
||||
|
||||
secondFrame = window.requestAnimationFrame(() => {
|
||||
forceSceneShadowPass(gl, scene);
|
||||
restoreManualShadowUpdates(gl);
|
||||
invalidate();
|
||||
onReady();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user