Add can_see argument to whitelist visible rooms

Useful for room shapes which cannot be handled by standard visibility
checks.

Normally this can be addressed with additional rooms and doorways, but
sometimes a room is required to be a complex shape (for example, to
accomodate animated static geometry).

These instances should be few and far between, so leaving this as an
opt-in feature that can be used during level authoring.
This commit is contained in:
Matt Penny 2024-08-20 19:52:50 -04:00
parent d5a1c302b1
commit ab56ffb380
9 changed files with 50 additions and 13 deletions

View file

@ -18,8 +18,9 @@ Static level collision geometry. Must be a quad.
## Notes
Game objects in the same [room](./room.md) and on the same collision layer can
collide. Some layers are also used for certain checks by the game.
Game objects can collide if they share a collision layer. Some layers are also
used for certain checks by the game. Static collision is only collidable if it
is in the same [room](./room.md)
If no collision layers are specified, the defaults are `STATIC`, `TANGIBLE`, and
`BLOCK_BALL`. The possible layers and their uses are as follows.

View file

@ -17,6 +17,13 @@ associated with a [door](./door.md)). If a doorway is closed, its rooms are not
considered connected at that point (other open doorways count). Only the
player's current room and visible connected rooms are considered for rendering.
Doorway visibility checks do not consider objects which may be covering a
doorway - just that it is within the bounds of the previous doorway from the
camera's point of view (or within the bounds of the camera itself, if there is
no previous doorway). To block visibility in these cases, either use an
additional room such that looking through all necessary doorways at once is
impossible or set the room's `can_see` argument.
Passing through an open doorway updates an object's room index, allowing it to
collide with other objects in the entered room while ignoring those in the
exited room.

View file

@ -6,14 +6,15 @@ collision.
## Name structure
```
@room INDEX
@room INDEX [can_see R1,R2,...,RN]
```
## Arguments
| Name | Description |
| ------- | -------------------------------------------- |
| `INDEX` | The index number of the room the box defines |
| Name | Description |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `INDEX` | The index number of the room the box defines |
| `can_see R1,R2,...,RN` (optional) | If specified, rooms outside the comma-separated list will not be considered visible from the current room. This is useful for room shapes which cannot be handled by standard visibility checks. |
## Notes
@ -23,7 +24,7 @@ know their room index, which is updated when passing through
[doorways](./doorway.md).
For performance reasons, only the player's current room and those visible
through open doorways are considered for rendering, and only objects in the same
room can collide with each other.
through open doorways are considered for rendering, and objects can only collide
with static [collision](./collision.md) in their current room.
Levels are limited to 64 rooms.

View file

@ -117,7 +117,7 @@ void staticRenderPopulateRooms(struct FrustumCullingInformation* cullingInfo, Mt
#define FORCE_RENDER_DOORWAY_DISTANCE 0.1f
void staticRenderDetermineVisibleRooms(struct FrustumCullingInformation* cullingInfo, u16 currentRoom, u64* visitedRooms) {
void staticRenderDetermineVisibleRooms(struct FrustumCullingInformation* cullingInfo, u16 currentRoom, u64* visitedRooms, u64 nonVisibleRooms) {
if (currentRoom == RIGID_BODY_NO_ROOM) {
return;
}
@ -125,6 +125,8 @@ void staticRenderDetermineVisibleRooms(struct FrustumCullingInformation* culling
u64 roomMask = 1LL << currentRoom;
*visitedRooms |= roomMask;
nonVisibleRooms |= gCurrentLevel->world.rooms[currentRoom].nonVisibleRooms;
for (int i = 0; i < gCurrentLevel->world.rooms[currentRoom].doorwayCount; ++i) {
struct Doorway* doorway = &gCurrentLevel->world.doorways[gCurrentLevel->world.rooms[currentRoom].doorwayIndices[i]];
@ -133,7 +135,8 @@ void staticRenderDetermineVisibleRooms(struct FrustumCullingInformation* culling
}
int newRoom = currentRoom == doorway->roomA ? doorway->roomB : doorway->roomA;
if (*visitedRooms & (1LL << newRoom)) {
u64 newRoomMask = 1LL << newRoom;
if ((*visitedRooms & newRoomMask) || (nonVisibleRooms & newRoomMask)) {
continue;
}
@ -154,7 +157,7 @@ void staticRenderDetermineVisibleRooms(struct FrustumCullingInformation* culling
struct FrustumCullingInformation doorwayFrustum;
frustumFromQuad(&cullingInfo->cameraPos, &doorway->quad, &doorwayFrustum);
staticRenderDetermineVisibleRooms(&doorwayFrustum, newRoom, visitedRooms);
staticRenderDetermineVisibleRooms(&doorwayFrustum, newRoom, visitedRooms, nonVisibleRooms);
};
}

View file

@ -6,7 +6,7 @@
#include "scene/camera.h"
#include "../scene/dynamic_render_list.h"
void staticRenderDetermineVisibleRooms(struct FrustumCullingInformation* cullingInfo, u16 currentRoom, u64* visitedRooms);
void staticRenderDetermineVisibleRooms(struct FrustumCullingInformation* cullingInfo, u16 currentRoom, u64* visitedRooms, u64 nonVisibleRooms);
int staticRenderIsRoomVisible(u64 visibleRooms, u16 roomIndex);
void staticRender(struct Transform* cameraTransform, struct FrustumCullingInformation* cullingInfo, u64 visibleRooms, struct DynamicRenderDataList* dynamicList, int stageIndex, Mtx* staticMatrices, struct Transform* staticTransforms, struct RenderState* renderState);

View file

@ -29,6 +29,8 @@ struct Room {
short* doorwayIndices;
short doorwayCount;
u64 nonVisibleRooms;
};
struct World {

View file

@ -370,7 +370,7 @@ int renderShouldRenderOtherPortal(struct Scene* scene, int visiblePortal, struct
}
void renderPlanFinishView(struct RenderPlan* renderPlan, struct Scene* scene, struct RenderProps* properties, struct RenderState* renderState) {
staticRenderDetermineVisibleRooms(&properties->cameraMatrixInfo.cullingInformation, properties->fromRoom, &properties->visiblerooms);
staticRenderDetermineVisibleRooms(&properties->cameraMatrixInfo.cullingInformation, properties->fromRoom, &properties->visiblerooms, 0);
struct Ray cameraRay;
quatMultVector(&properties->camera.transform.rotation, &gForward, &cameraRay.dir);

View file

@ -1,14 +1,29 @@
local sk_scene = require('sk_scene')
local sk_math = require('sk_math')
local util = require('tools.level_scripts.util')
local room_blocks = {}
local room_count = 0
local room_bb = {}
local room_non_visibility = {}
for _, room in pairs(sk_scene.nodes_for_type("@room")) do
local firstMesh = room.node.meshes[1]:transform(room.node.full_transformation)
local room_index = tonumber(room.arguments[1])
local non_visible_rooms = 0
local can_see = sk_scene.find_named_argument(room.arguments, "can_see")
if can_see then
local visible_rooms = 1 << room_index
local can_see_rooms = util.string_split(util.trim(can_see), ',')
for _, can_see_index in pairs(can_see_rooms) do
visible_rooms = visible_rooms | (1 << tonumber(can_see_index))
end
non_visible_rooms = ~visible_rooms
end
room_count = math.max(room_count, room_index + 1)
@ -18,6 +33,12 @@ for _, room in pairs(sk_scene.nodes_for_type("@room")) do
room_bb[room_index + 1] = firstMesh.bb
end
if room_non_visibility[room_index + 1] then
room_non_visibility[room_index + 1] = room_non_visibility[room_index + 1] | non_visible_rooms
else
room_non_visibility[room_index + 1] = non_visible_rooms
end
table.insert(room_blocks, {
bb = firstMesh.bb,
room_index = room_index,
@ -63,4 +84,5 @@ return {
node_nearest_room_index = node_nearest_room_index,
room_count = room_count,
room_bb = room_bb,
room_non_visibility = room_non_visibility,
}

View file

@ -128,6 +128,7 @@ local function generate_room(room_index)
room_export.room_bb[room_index] or sk_math.box3(),
sk_definition_writer.reference_to(room_doorways[room_index], 1),
#room_doorways[room_index],
room_export.room_non_visibility[room_index]
}
end