-
Notifications
You must be signed in to change notification settings - Fork 113
Implementation Details
There are two ways of rendering portal within the architecture of rasterization:
- Use stencil buffer to limit rendering area and render portals from outer to inner
- Render portal content to another frame buffer first and then draw to the main frame buffer. Render inner portal first.
This mod adopts the first method. (It also has the "compatibility" rendering mode which uses the second method but does not support portal-in-portal rendering)
Portal rendering takes place after all solid triangle rendering and before translucent triangle rendering. It will first render the portals nearby camera by the order of distance to the camera. When rendering a portal, if the portal passes rough frustum culling, the view area will be rendered under an occlusion query to determine whether a portal is visible. If the portal is visible, clear the depth of portal view area, and then switch the rendering context and render the world again. And it will render inner portals recursively.
The outmost part of the framebuffer has stencil value 0. Rendering the portal area will increase stencil value by 1. So the stencil value corresponds to the portal layer.
When rendering the portal view area, if the camera is very close to the portal,
the pixels that are too close to the camera are culled during rasterization.
So the view area will be rendered with GL_DEPTH_CLAMP
enabled.
Because the portal rendering is recursive, all rendering related context should be switched upon rendering, stored on the stack, and then recover after the portal rendering finishes.
When rendering portals, it will do advanced frustum culling to improve performance.
For example, when rendering this scene
The sections behind the portal will be culled
The sections out of portal view will be culled
Without advanced frustum culling:
When rendering portal content, everything in front of the portal plane will be culled. This mod uses glClipPlane
to cull it (which normally uses internal geometry shader to cull).
These pixels will block the portal view if not being culled
There is another culling method using oblique projection. http://www.terathon.com/lengyel/Lengyel-Oblique.pdf But oblique projection won't work when the angle between the culling plane normal with view vector is not bigger than 90 degrees.
(Rendering portals and mirrors with ray tracing is much simpler than in rasterization.)
After applying one mirror transformation, all counter-clockwise triangles will become clockwise. So the face culling should be inverted. Applying two mirror transformations cancels that. The face culling will be inverted when rendering an odd number of layers of mirrors.
If an entity is intersecting with a portal, to render this entity correctly, it will render the entity twice. The entity will be rendered both outside of the portal and inside portal with plane culling. Minecraft uses deferred entity rendering which firstly collects all triangles and then render all of them. This cannot handle the entity rendering that has a special culling plane. So it will first render all collected triangles and then use a separate draw call to render the culled entity.
Vanilla assumes that only the chunks near player will be loaded so ClientChunkManager use a fixed size 2d array to store chunks. I change it into a map so the chunk storage is not limited to the area near player. Similar things also apply to BuiltChunkStorage.
Vanilla only allows one client world to be present at a time but this mod breaks this assumption. This mod will create a faked client world when it's firstly used. When ticking remote world or processing remote world packet, it will switch the world field of MinecraftClient and then switch back.
In server side, it will send a redirected packet to players to synchronize world information. If the packet is not redirected, a chunk data packet of nether may be recognized as overworld chunk data in client.
This mod will delay the unloading of chunks to avoid frequently load/unload chunks. So it will consume more memory.
Teleportation on client side happens before rendering (not during ticking). Teleportation happens when the camera crosses the portal (not after the player entity crossing the portal).
Teleportation is iterative. Normally the player at most teleports one time in a frame. But when the player crosses the world wrapping corner the teleportation may happen twice.
The client will teleport first and then the server receives the teleport request and do teleportation on the server. But when the player is not teleporting frequently, the client will accept a sync message from the server.
When an entity is halfway in the portal then its collision will be specially treated. It will cut the entity's collision into two pieces, one outside portal and one inside portal. It firstly does collision test for the outer part and then switch the entity to the portal destination position and do collision test for the inner part.
The plane for cutting the collision box is not the portal plane. It is the portal plane moved by the reverse of the moving attempt vector.
World wrapping portals and vertical dimension connecting portals are very big. They are not entities in the world like small portals. They are stored in per-dimension global portal storage.