Fixed light bleeding throw scene switching doors
This commit is contained in:
@@ -642,7 +642,15 @@ door builder also adds a black occluder covering the doorway, childed to
|
||||
the grid node (it does NOT swing with the hinge) and tracked via
|
||||
`DoorComponent::occluder`; `DoorSystem` hides it while the door is fully
|
||||
closed so the player never sees the missing room interior through the
|
||||
opened doorway before the switch fires. Every scene-switch door gets the
|
||||
opened doorway before the switch fires. A second flat black **gap shield**
|
||||
quad (`CellGridDoorOccluderShield_*`, tracked via `DoorComponent::gapShield`)
|
||||
sits 1 cm behind the closed leaf's back face on the same void side and is
|
||||
visible exactly while the door is fully closed: spanning the leaf box plus
|
||||
a 6 cm margin, it blacks out the leaf/frame clearance slits (0.02 m per
|
||||
side, 0.05 m at the top) that would otherwise show the ungenerated void
|
||||
(sky/terrain) around the closed door; its margin edges end up inside the
|
||||
jamb boxes and wall thickness, so it never pokes out of the frame. Every
|
||||
scene-switch door gets the
|
||||
same open-front tunnel mesh (`CellGridDoorOccluderTunnel_*`, shared per
|
||||
size and side) on the VOID side of the doorway: door-local +Z points
|
||||
outward from the owning cell, where normal grids hide the missing room,
|
||||
@@ -651,7 +659,9 @@ while exteriorOnly grids hide their missing interior inward, so
|
||||
builder default is +1; standalone doors keep it). The tunnel depth covers
|
||||
the fully-open leaf sweep, so the leaf stays visible whether it swings
|
||||
towards or away from the player, and the side/top/bottom walls flared
|
||||
1 cm into the frame keep the darkness hole-free. The shared
|
||||
6 cm into the frame — past the 0.02 m side / 0.05 m top leaf clearances,
|
||||
so their edges sit inside the jamb boxes and no sight line slips between
|
||||
the frame and the tunnel into the void — keep the darkness hole-free. The shared
|
||||
`CellGridDoorOccluderBlack` material keeps lighting ENABLED with
|
||||
all-black colours (diffuse/ambient/specular/emissive) — with lighting
|
||||
disabled RTSS ignores material colours and renders the pass white.
|
||||
|
||||
@@ -779,8 +779,18 @@ side of the doorway — door-local +Z points outward from the owning cell
|
||||
`occluderSide = -1` for exteriorOnly grids, whose missing interior is
|
||||
inward. The back panel sits behind the fully-open leaf sweep so the
|
||||
swinging leaf stays visible for both swing directions, and
|
||||
side/top/bottom walls flared 1 cm into the frame keep the darkness
|
||||
hole-free; `DoorSystem` hides the occluder while fully closed. The
|
||||
side/top/bottom walls flared 6 cm into the frame — past the 0.02 m side /
|
||||
0.05 m top leaf clearances, so their edges sit inside the jamb boxes and
|
||||
no sight line slips between the frame and the tunnel into the void — keep the darkness
|
||||
hole-free; `DoorSystem` hides the occluder while fully closed. A second
|
||||
flat black gap-shield quad (`CellGridDoorOccluderShield_*`, tracked via
|
||||
`DoorComponent::gapShield`) sits 1 cm behind the closed leaf's back face
|
||||
on the same void side and is visible exactly while the door is fully
|
||||
closed: spanning the leaf box plus a 6 cm margin, it blacks out the
|
||||
leaf/frame clearance slits (0.02 m per side, 0.05 m at the top) that
|
||||
otherwise show the ungenerated void (sky/terrain) around the closed
|
||||
door; its margin edges end up inside the jamb boxes and wall thickness,
|
||||
so it never pokes out of the frame. The
|
||||
occluder material keeps lighting enabled with all-black
|
||||
colours — an unlit pass renders white under RTSS (FFPColour defaults the
|
||||
output to white when no lighting SRS runs). Scene-switch doors never
|
||||
@@ -822,7 +832,11 @@ sub-millisecond while swings are real-time).
|
||||
Its visibility follows the door state: hidden while fully closed, visible
|
||||
while open/swinging (visible flag toggled by `DoorSystem` from
|
||||
`currentAngle`), so the player never sees the missing room interior
|
||||
behind the opened doorway before the switch fires. The material is lit
|
||||
behind the opened doorway before the switch fires. A complementary flat
|
||||
gap-shield quad just behind the closed leaf (leaf box + 6 cm margin,
|
||||
covering the 0.02 m side / 0.05 m top clearances) is visible exactly
|
||||
while the door is fully closed, blacking out the clearance slits. The
|
||||
material is lit
|
||||
with all-black colours (unlit passes render white under RTSS).
|
||||
- The prompt stays "E Open" (never "E Close") for scene-switch doors, as
|
||||
today.
|
||||
|
||||
@@ -78,6 +78,13 @@ struct DoorComponent {
|
||||
// so it does NOT swing with the hinge. Hidden while the door is
|
||||
// fully closed. Runtime only.
|
||||
Ogre::Entity *occluder = nullptr;
|
||||
|
||||
// F1: flat black gap-shield quad just behind the closed leaf of a
|
||||
// scene-switch door (same void side as the tunnel), visible only
|
||||
// while the door is fully closed - it blacks out the leaf/frame
|
||||
// clearance slits (0.02 m sides, 0.05 m top) the tunnel, hidden
|
||||
// while closed, does not cover. Runtime only.
|
||||
Ogre::Entity *gapShield = nullptr;
|
||||
};
|
||||
|
||||
#endif // EDITSCENE_DOOR_HPP
|
||||
|
||||
@@ -484,7 +484,9 @@ struct SceneSwitchTestListener : public Ogre::FrameListener {
|
||||
}
|
||||
/* F1: every scene-switch door gets the void-side
|
||||
* tunnel occluder (depth covers the leaf sweep,
|
||||
* so both swing directions stay visible). */
|
||||
* so both swing directions stay visible) plus the
|
||||
* gap shield backing the leaf/frame clearance
|
||||
* slits while the door is closed. */
|
||||
{
|
||||
const DoorComponent &d =
|
||||
findDoorById(exitDoorId)
|
||||
@@ -499,6 +501,49 @@ struct SceneSwitchTestListener : public Ogre::FrameListener {
|
||||
"F1 exit door has no tunnel occluder";
|
||||
break;
|
||||
}
|
||||
if (!d.gapShield ||
|
||||
d.gapShield->getMesh()
|
||||
->getName()
|
||||
.find("CellGridDoorOccluderShield") !=
|
||||
0) {
|
||||
failed = true;
|
||||
failReason =
|
||||
"F1 exit door has no gap shield";
|
||||
break;
|
||||
}
|
||||
/* The door is still closed here: the shield
|
||||
* backs the slits, the tunnel stays hidden. */
|
||||
if (!d.gapShield->isVisible() ||
|
||||
d.occluder->isVisible()) {
|
||||
failed = true;
|
||||
failReason =
|
||||
"F1 closed-door occluder visibility wrong";
|
||||
break;
|
||||
}
|
||||
/* The tunnel walls must reach past the
|
||||
* leaf/frame clearances (0.02 m sides,
|
||||
* 0.05 m top) or the void shows between the
|
||||
* frame and the black corridor while the
|
||||
* door is open; both meshes are hinge-local,
|
||||
* so the tunnel footprint must cover at
|
||||
* least the gap shield's (6 cm margin). */
|
||||
{
|
||||
Ogre::Vector3 th =
|
||||
d.occluder->getMesh()
|
||||
->getBounds()
|
||||
.getHalfSize();
|
||||
Ogre::Vector3 sh =
|
||||
d.gapShield->getMesh()
|
||||
->getBounds()
|
||||
.getHalfSize();
|
||||
if (th.x < sh.x - 0.001f ||
|
||||
th.y < sh.y - 0.001f) {
|
||||
failed = true;
|
||||
failReason =
|
||||
"F1 tunnel walls inside the leaf/frame clearances";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!requestDoorSwitch(exitDoorId))
|
||||
break;
|
||||
@@ -730,7 +775,10 @@ struct SceneSwitchTestListener : public Ogre::FrameListener {
|
||||
* Z:0:0:0 is the way back. E on such a door swings the leaf open first;
|
||||
* the scene switch fires only when the leaf reaches the open angle, and
|
||||
* a black occluder box behind the doorway hides the missing half of the
|
||||
* building while it swings. Both scenes carry their own player
|
||||
* building while it swings; while the door is closed a flat black gap
|
||||
* shield behind the leaf blacks out the leaf/frame clearance slits, so
|
||||
* no sky/terrain bleeds through the closed door. Both scenes carry
|
||||
* their own player
|
||||
* controller, so each switch is a clean takeover (see
|
||||
* EditorApp::performSceneSwitch); travel back and forth is endless.
|
||||
*
|
||||
@@ -752,7 +800,9 @@ struct SceneSwitchTestListener : public Ogre::FrameListener {
|
||||
* persisted and restored across the scene switches) and
|
||||
* both F1 scene-switch doors (interior -> exterior and
|
||||
* back each fire only when the leaf is fully open,
|
||||
* black occluder present), verifying the round-trip
|
||||
* black occluder tunnel + closed-door gap shield
|
||||
* present with the right closed-state visibility),
|
||||
* verifying the round-trip
|
||||
* arrival teleports (the exterior arrival check reads
|
||||
* the expected Y from TerrainSystem::getHeightAt and
|
||||
* polls while the streaming window loads and the
|
||||
|
||||
@@ -64,9 +64,16 @@ occluderTunnelMesh(const Ogre::Vector3 ¢er,
|
||||
const float y0 = center.y - hy, y1 = center.y + hy;
|
||||
const float z0 = center.z + side * (hz + 0.02f);
|
||||
const float z1 = z0 + side * (x1 + hz * 2.0f + 0.05f);
|
||||
/* Flare the walls 1 cm INTO the frame/jamb: no gap at the
|
||||
* opening edges and no z-fighting with the jamb faces. */
|
||||
const float f = 0.01f;
|
||||
/* Flare the walls INTO the frame/jamb past the leaf/frame
|
||||
* clearances (0.02 m per side, 0.05 m at the top): with a smaller
|
||||
* flare, sight lines through the open doorway slip over the top
|
||||
* wall / past the side walls into the void between the jamb inner
|
||||
* face and the wall, showing sky/terrain around the black
|
||||
* corridor. 6 cm puts the wall edges inside the jamb boxes (0.1 m
|
||||
* thick, inner faces at the clearances), so any ray past a wall
|
||||
* hits the frame instead, with no z-fighting against the jamb
|
||||
* faces. */
|
||||
const float f = 0.06f;
|
||||
|
||||
char name[160];
|
||||
snprintf(name, sizeof(name),
|
||||
@@ -123,6 +130,67 @@ occluderTunnelMesh(const Ogre::Vector3 ¢er,
|
||||
return name;
|
||||
}
|
||||
|
||||
/* F1: flat black gap shield for scene-switch doors, shown while the
|
||||
* door is fully closed (the tunnel covers the swinging/open states):
|
||||
* the leaf is sized with clearances against the frame opening (0.02 m
|
||||
* per side, 0.05 m at the top), and without a backing those slits show
|
||||
* the ungenerated void (sky/terrain) around the closed door. The
|
||||
* shield is a single quad 1 cm behind the closed leaf's back face on
|
||||
* the void side (`side` * Z), spanning the leaf box plus a margin that
|
||||
* exceeds the clearances; the margin edges end up inside the jamb
|
||||
* boxes and the wall thickness, so the quad never pokes out of the
|
||||
* frame. Like the tunnel, the coordinates are baked into the mesh in
|
||||
* hinge-local space and meshes are shared between doors of equal size
|
||||
* and side. */
|
||||
static Ogre::String
|
||||
occluderShieldMesh(const Ogre::Vector3 ¢er,
|
||||
const Ogre::Vector3 &halfExtents, float side,
|
||||
const Ogre::String &group)
|
||||
{
|
||||
const float hx = halfExtents.x;
|
||||
const float hy = halfExtents.y;
|
||||
const float hz = halfExtents.z;
|
||||
const float x0 = center.x - hx, x1 = center.x + hx;
|
||||
const float y0 = center.y - hy, y1 = center.y + hy;
|
||||
const float z = center.z + side * (hz + 0.01f);
|
||||
/* Margin beyond the leaf box: must exceed the leaf/frame
|
||||
* clearances (0.02 m sides, 0.05 m top). */
|
||||
const float m = 0.06f;
|
||||
|
||||
char name[160];
|
||||
snprintf(name, sizeof(name),
|
||||
"CellGridDoorOccluderShield_%d_%d_%d_%d_%d_%d_%d",
|
||||
(int)lroundf(center.x * 1000.0f),
|
||||
(int)lroundf(center.y * 1000.0f),
|
||||
(int)lroundf(center.z * 1000.0f),
|
||||
(int)lroundf(hx * 1000.0f), (int)lroundf(hy * 1000.0f),
|
||||
(int)lroundf(hz * 1000.0f), (int)side);
|
||||
if (!Ogre::MeshManager::getSingleton().getByName(name, group).isNull())
|
||||
return name;
|
||||
|
||||
const float x0m = x0 - m, x1m = x1 + m;
|
||||
const float y0m = y0 - m, y1m = y1 + m;
|
||||
|
||||
Ogre::ManualObject mo(name);
|
||||
mo.begin("CellGridDoorOccluderBlack",
|
||||
Ogre::RenderOperation::OT_TRIANGLE_LIST);
|
||||
Ogre::Vector3 normal(0, 0, -side);
|
||||
Ogre::uint32 base = mo.getCurrentVertexCount();
|
||||
mo.position(x0m, y0m, z);
|
||||
mo.normal(normal);
|
||||
mo.position(x1m, y0m, z);
|
||||
mo.normal(normal);
|
||||
mo.position(x1m, y1m, z);
|
||||
mo.normal(normal);
|
||||
mo.position(x0m, y1m, z);
|
||||
mo.normal(normal);
|
||||
mo.triangle(base, base + 1, base + 2);
|
||||
mo.triangle(base, base + 2, base + 3);
|
||||
mo.end();
|
||||
mo.convertToMesh(name, group);
|
||||
return name;
|
||||
}
|
||||
|
||||
flecs::entity DoorBuilder::build(flecs::world &world,
|
||||
Ogre::SceneManager *sceneMgr,
|
||||
flecs::entity parentEntity,
|
||||
@@ -286,7 +354,10 @@ flecs::entity DoorBuilder::build(flecs::world &world,
|
||||
* the frame. The geometry is baked into the mesh in hinge-local
|
||||
* space and the node hangs off the PARENT node (not the hinge),
|
||||
* so it does not swing with the leaf; DoorSystem toggles its
|
||||
* visibility from the swing angle. */
|
||||
* visibility from the swing angle. A second flat gap-shield quad
|
||||
* sits just behind the closed leaf and is visible only while the
|
||||
* door is fully closed, blacking out the leaf/frame clearance
|
||||
* slits the tunnel (hidden while closed) does not cover. */
|
||||
if (!door.sceneSwitchPath.empty()) {
|
||||
const Ogre::String group =
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME;
|
||||
@@ -328,6 +399,42 @@ flecs::entity DoorBuilder::build(flecs::world &world,
|
||||
|
||||
doorEntity.get_mut<DoorComponent>().occluder = occEnt;
|
||||
}
|
||||
|
||||
Ogre::String shieldMeshName = occluderShieldMesh(
|
||||
colliderCenter, colliderHalfExtents,
|
||||
params.occluderSide, group);
|
||||
Ogre::Entity *shieldEnt = nullptr;
|
||||
try {
|
||||
shieldEnt = sceneMgr->createEntity(shieldMeshName);
|
||||
} catch (const std::exception &e) {
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"DoorBuilder: Error creating door gap shield: " +
|
||||
std::string(e.what()));
|
||||
}
|
||||
if (shieldEnt) {
|
||||
shieldEnt->setMaterialName("CellGridDoorOccluderBlack");
|
||||
shieldEnt->setVisible(!wasOpen);
|
||||
|
||||
Ogre::SceneNode *shieldNode =
|
||||
parentNode->createChildSceneNode();
|
||||
shieldNode->setPosition(occPos);
|
||||
shieldNode->setOrientation(doorwayRot);
|
||||
shieldNode->setScale(occScale);
|
||||
shieldNode->attachObject(shieldEnt);
|
||||
|
||||
flecs::entity shieldEntity = world.entity();
|
||||
shieldEntity.child_of(doorEntity);
|
||||
shieldEntity.set<TransformComponent>(
|
||||
{shieldNode, occPos, doorwayRot, occScale});
|
||||
RenderableComponent shieldRenderable;
|
||||
shieldRenderable.entity = shieldEnt;
|
||||
shieldRenderable.meshName = shieldMeshName;
|
||||
shieldEntity.set<RenderableComponent>(
|
||||
shieldRenderable);
|
||||
|
||||
doorEntity.get_mut<DoorComponent>().gapShield =
|
||||
shieldEnt;
|
||||
}
|
||||
}
|
||||
|
||||
return doorEntity;
|
||||
|
||||
@@ -18,11 +18,15 @@
|
||||
* leaf, DoorComponent, RigidBodyComponent static, ActuatorComponent)
|
||||
* ├── collider child (PhysicsColliderComponent box of the closed
|
||||
* │ leaf; disabled by DoorSystem while not fully closed)
|
||||
* └── occluder child (F1, scene-switch doors only: open-front
|
||||
* black tunnel on the void side of the doorway - door-local
|
||||
* +Z normally, -Z for exteriorOnly grids -, node on the
|
||||
* PARENT node so it does not swing; DoorSystem toggles its
|
||||
* visibility)
|
||||
* ├── occluder child (F1, scene-switch doors only: open-front
|
||||
* │ black tunnel on the void side of the doorway - door-local
|
||||
* │ +Z normally, -Z for exteriorOnly grids -, node on the
|
||||
* │ PARENT node so it does not swing; DoorSystem toggles its
|
||||
* │ visibility, hidden while the door is fully closed)
|
||||
* └── gap-shield child (F1, scene-switch doors only: flat black
|
||||
* quad just behind the closed leaf, visible only while the
|
||||
* door is fully closed; blacks out the leaf/frame clearance
|
||||
* slits the hidden tunnel does not cover)
|
||||
*
|
||||
* The builder also declares the F6 store defaults for persistent doors
|
||||
* (DoorSystem::declareDoorDefaults) and snaps a door that was left open
|
||||
|
||||
@@ -175,10 +175,13 @@ void DoorSystem::update(float deltaTime)
|
||||
}
|
||||
}
|
||||
|
||||
// F1: the black occluder is visible unless the door is
|
||||
// fully closed
|
||||
// F1: the black occluder tunnel is visible unless the door
|
||||
// is fully closed; the gap shield backs the leaf/frame
|
||||
// clearance slits exactly while it is
|
||||
if (door.occluder)
|
||||
door.occluder->setVisible(door.currentAngle != 0.0f);
|
||||
if (door.gapShield)
|
||||
door.gapShield->setVisible(door.currentAngle == 0.0f);
|
||||
});
|
||||
|
||||
// Refresh the per-door "door_unlock_<doorId>" subscriptions
|
||||
|
||||
Reference in New Issue
Block a user