Improve movement flow layout/animation and fix Byzantine hall doors, styles, and preload.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Danila Khodjaef
2026-07-28 20:36:55 +03:00
co-authored by Cursor
parent 23e1210e86
commit 8111cd0163
13 changed files with 632 additions and 166 deletions
+55 -19
View File
@@ -1418,6 +1418,7 @@ function ArtistHall({
nearPassage?: boolean;
}) {
const { width, depth, segments } = layout;
const endWallHasDoor = movementMode && 'endWallHasDoor' in layout ? layout.endWallHasDoor : false;
const halfW = width / 2;
const halfD = depth / 2;
const walls = useMemo(
@@ -1500,7 +1501,7 @@ function ArtistHall({
</>
)}
{/* Front wall — passage to next wing, or solid (artist exit / movement entrance) */}
{/* Front wall — next-wing passage, entrance exit (single-wing), or artist exit */}
{movementMode && hasNextHall ? (
<>
<GalleryWall
@@ -1527,10 +1528,8 @@ function ArtistHall({
trimColor={walls.trim}
/>
</>
) : movementMode ? (
<>
<GalleryWall position={[0, WALL_HEIGHT / 2, halfD]} size={[width, WALL_HEIGHT]} color={walls.main} />
</>
) : movementMode && endWallHasDoor ? (
<GalleryWall position={[0, WALL_HEIGHT / 2, halfD]} size={[width, WALL_HEIGHT]} color={walls.main} />
) : (
<>
<GalleryWall position={[-(DOOR_WIDTH / 2 + (width - DOOR_WIDTH) / 4), WALL_HEIGHT / 2, halfD]} size={[(width - DOOR_WIDTH) / 2, WALL_HEIGHT]} color={walls.main} />
@@ -1547,8 +1546,8 @@ function ArtistHall({
</>
)}
{/* Back wall — movement exit / navigation, or solid with title */}
{movementMode ? (
{/* Back wall — multi-wing exit / navigator, or solid end wall for paintings */}
{movementMode && endWallHasDoor ? (
<>
<GalleryWall
position={[-(DOOR_WIDTH / 2 + (width - DOOR_WIDTH) / 4), WALL_HEIGHT / 2, -halfD]}
@@ -1576,6 +1575,17 @@ function ArtistHall({
/>
</group>
</>
) : movementMode ? (
interiorStyle ? (
<TexturedWall
kind={interiorStyle.surfaces.wall}
tint={interiorStyle.tints.wall}
position={[0, WALL_HEIGHT / 2, -halfD]}
size={[width, WALL_HEIGHT, WALL_THICKNESS]}
/>
) : (
<GalleryWall position={[0, WALL_HEIGHT / 2, -halfD]} size={[width, WALL_HEIGHT]} color={walls.main} />
)
) : null}
{interiorStyle && computedWindows && computedWindows.length > 0 && (
@@ -2108,11 +2118,22 @@ export default function VirtualGallery(props: Props) {
for (const seg of layout.segments) {
for (const slot of seg.slots) boxes.push(paintingKeepOutBounds(slot));
}
// Door jambs — keep 0.5 m from the opening posts; opening corridor stays walkable.
boxes.push(...doorJambKeepOuts(wallInnerHalfD, true));
if (isWingedHall) boxes.push(...doorJambKeepOuts(wallInnerHalfD, false));
const endWallHasDoor =
isWingedHall && 'endWallHasDoor' in layout ? layout.endWallHasDoor : false;
// Front door/passage jambs when the entrance wall has an opening.
if (!isWingedHall || hasNextHall || !endWallHasDoor) {
boxes.push(...doorJambKeepOuts(wallInnerHalfD, true));
}
// Back exit jambs for multi-wing halls.
if (isWingedHall && endWallHasDoor) {
boxes.push(...doorJambKeepOuts(wallInnerHalfD, false));
}
return boxes;
}, [layout.segments, wallInnerHalfD, isWingedHall]);
}, [layout, wallInnerHalfD, isWingedHall, hasNextHall]);
const endWallHasDoor =
isWingedHall && 'endWallHasDoor' in layout ? layout.endWallHasDoor : false;
const exitOnFront = !isWingedHall || !endWallHasDoor;
const resolvePlayerPosition = useCallback(
(pos: { x: number; z: number }, allowFrontDoorApproach: boolean) => {
@@ -2123,8 +2144,10 @@ export default function VirtualGallery(props: Props) {
let minZ = -playHalfD;
let maxZ = playHalfD;
if (inDoorBand) {
minZ = -wallInnerHalfD + 0.08;
if (hasNextHall || allowFrontDoorApproach) maxZ = wallInnerHalfD - 0.08;
if (endWallHasDoor) minZ = -wallInnerHalfD + 0.08;
if (hasNextHall || allowFrontDoorApproach || exitOnFront) {
maxZ = wallInnerHalfD - 0.08;
}
}
pos.z = Math.max(minZ, Math.min(maxZ, pos.z));
} else {
@@ -2144,6 +2167,8 @@ export default function VirtualGallery(props: Props) {
exitZ,
isWingedHall,
hasNextHall,
endWallHasDoor,
exitOnFront,
wallInnerHalfD,
collisionObstacles,
]
@@ -2218,23 +2243,30 @@ export default function VirtualGallery(props: Props) {
}
}, [isWingedHall, onBack, !isWingedHall ? props.data.artist.id : undefined]);
const backExitZ = isWingedHall ? -playHalfD : exitZ;
const backExitZ = isWingedHall && endWallHasDoor ? -playHalfD : exitZ;
const frontPassageZ = playHalfD;
const updateProximityFlags = useCallback(
(pos: { x: number; z: number }) => {
if (isWingedHall) {
const atBackExit = pos.z < backExitZ + 0.8 && Math.abs(pos.x) < DOOR_WIDTH / 2 + 0.6;
const atBackExit =
endWallHasDoor &&
pos.z < backExitZ + 0.8 &&
Math.abs(pos.x) < DOOR_WIDTH / 2 + 0.6;
const atFrontExit =
!endWallHasDoor &&
pos.z > frontPassageZ - 0.8 &&
Math.abs(pos.x) < DOOR_WIDTH / 2 + 0.6;
const atFrontPassage =
hasNextHall && pos.z > frontPassageZ - 0.8 && Math.abs(pos.x) < DOOR_WIDTH / 2 + 0.6;
setNearExit(atBackExit);
setNearExit(!!(atBackExit || atFrontExit));
setNearPassage(!!atFrontPassage);
} else {
const atExit = pos.z > exitZ - 0.8 && Math.abs(pos.x) < DOOR_WIDTH / 2 + 0.6;
setNearExit(atExit);
}
},
[isWingedHall, backExitZ, frontPassageZ, hasNextHall, exitZ]
[isWingedHall, endWallHasDoor, backExitZ, frontPassageZ, hasNextHall, exitZ]
);
const moveCamera = useCallback(
@@ -2548,8 +2580,12 @@ export default function VirtualGallery(props: Props) {
{isWingedHall ? (
<>
<li>Date and artist labels appear below each frame</li>
<li>Works hang on left &amp; right walls up to ~55 per wing</li>
<li>Back door: wing navigator &amp; exit to timeline</li>
<li>Works hang on left, end, and right walls up to ~55 per wing</li>
<li>
{movementHalls.length > 1
? 'Back door: wing navigator & exit to timeline · Front: next wing'
: 'Entrance door: exit to timeline'}
</li>
{movementHalls.length > 1 && (
<li>Front archway: walk to the next chronological wing</li>
)}