Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Planes & Layers part 2: Plane Masters #18749

Merged
merged 12 commits into from
Apr 13, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion aurorastation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
#include "code\__DEFINES\_compile_helpers.dm"
#include "code\__DEFINES\_flags.dm"
#include "code\__DEFINES\_helpers.dm"
#include "code\__DEFINES\_layers.dm"
#include "code\__DEFINES\_macros.dm"
#include "code\__DEFINES\_protect.dm"
#include "code\__DEFINES\_tick.dm"
Expand Down Expand Up @@ -73,6 +72,7 @@
#include "code\__DEFINES\important_recursive_contents.dm"
#include "code\__DEFINES\items_clothing.dm"
#include "code\__DEFINES\jobs.dm"
#include "code\__DEFINES\layers.dm"
#include "code\__DEFINES\lighting.dm"
#include "code\__DEFINES\logging.dm"
#include "code\__DEFINES\machinery.dm"
Expand Down Expand Up @@ -267,6 +267,7 @@
#include "code\_onclick\hud\screen_objects.dm"
#include "code\_onclick\hud\skybox.dm"
#include "code\_onclick\hud\spell_screen_objects.dm"
#include "code\_onclick\hud\rendering\_renderer.dm"
#include "code\_onclick\hud\screen_object_types\ai_screen_objs.dm"
#include "code\_onclick\hud\screen_object_types\borer.dm"
#include "code\_onclick\hud\screen_object_types\internals.dm"
Expand Down
10 changes: 8 additions & 2 deletions code/__DEFINES/flags.dm
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ var/global/list/bitflags = list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
#define ZM_ALLOW_ATMOS 4 // If this turf permits passage of air.
#define ZM_MIMIC_NO_AO 8 // If the turf shouldn't apply regular turf AO and only do Z-mimic AO.
#define ZM_NO_OCCLUDE 16 // Don't occlude below atoms if we're a non-mimic z-turf.
#define ZM_MIMIC_BASETURF 32 // We dont want to show space, we want to show the base turf of the area (for stuff like glass floors)

// Movable flags.
#define ZMM_IGNORE 1 //! Do not copy this movable.
#define ZMM_MANGLE_PLANES 2 //! Check this movable's overlays/underlays for explicit plane use and mangle for compatibility with Z-Mimic. If you're using emissive overlays, you probably should be using this flag. Expensive, only use if necessary.

// Convenience flag.
#define ZM_MIMIC_DEFAULTS (ZM_MIMIC_BELOW)
Expand All @@ -18,10 +23,11 @@ var/global/list/bitflags = list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
var/list/mimic_defines = list(
"ZM_MIMIC_BELOW",
"ZM_MIMIC_OVERWRITE",
"ZM_ALLOW_LIGHTING",
// "ZM_ALLOW_LIGHTING", //Exists on Nebula, but not Aurora?
"ZM_ALLOW_ATMOS",
"ZM_MIMIC_NO_AO",
"ZM_NO_OCCLUDE"
"ZM_NO_OCCLUDE",
"ZM_MIMIC_BASETURF"
)

//EMP protection
Expand Down
118 changes: 79 additions & 39 deletions code/__DEFINES/_layers.dm → code/__DEFINES/layers.dm
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Planes, layers, and defaults for _renderer.dm
/*
from stddef.dm, planes & layers built into byond.

Expand All @@ -15,15 +16,31 @@
FLOAT_PLANE = -32767
*/

#define LOWEST_PLANE -200

#define CLICKCATCHER_PLANE -100

#define PLANE_SPACE_BACKGROUND -99
#define PLANE_SPACE_PARALLAX (PLANE_SPACE_BACKGROUND + 1) // -98
#define PLANE_SKYBOX (PLANE_SPACE_PARALLAX + 1) // -97
#define PLANE_SPACE_DUST (PLANE_SPACE_PARALLAX + 1) // -96
#define PLANE_ABOVE_PARALLAX (PLANE_SPACE_DUST + 1) // -95
#define SPACE_PLANE -99
#define SPACE_LAYER 1

#define SKYBOX_PLANE -98
#define SKYBOX_LAYER 1

#define DUST_PLANE -97
#define DEBRIS_LAYER 1
#define DUST_LAYER 2

// Openspace uses planes -80 through -70.
#define OPENTURF_MAX_PLANE -70
#define OPENTURF_MAX_DEPTH 10 // The maxiumum number of planes deep we'll go before we just dump everything on the same plane.

#define OVER_OPENSPACE_PLANE -4

#define DEFAULT_PLANE 0
#define WARP_EFFECT_PLANE -3

#define BLACKNESS_PLANE 0 //Blackness plane as per DM documentation.

#define DEFAULT_PLANE 1
#define PLATING_LAYER 1
//ABOVE PLATING
#define HOLOMAP_LAYER 1.01
Expand Down Expand Up @@ -109,45 +126,68 @@
#define OBSERVER_LAYER 5.1
#define OBFUSCATION_LAYER 5.2

#define OVERMAP_SECTOR_LAYER 60
#define OVERMAP_IMPORTANT_SECTOR_LAYER 61
#define OVERMAP_SHIP_LAYER 62
#define OVERMAP_SHUTTLE_LAYER 63

#define AREA_LAYER 999

//FUTURE OVERMAP_PLANE
#define OVERMAP_SECTOR_LAYER 3.15
#define OVERMAP_IMPORTANT_SECTOR_LAYER 3.16
#define OVERMAP_SHIP_LAYER 3.17
#define OVERMAP_SHUTTLE_LAYER 3.18

//FUTURE LIGHTING PLANE
#define LIGHTBULB_LAYER 10
#define LIGHTING_LAYER 11
#define ABOVE_LIGHTING_LAYER 12

//FUTURE EFFECTS_ABOVE_LIGHTING_PLANE
#define EFFECTS_ABOVE_LIGHTING_LAYER 13 // For overlays you want to be above light.
#define EYEGLOW_LAYER 14
#define BEAM_PROJECTILE_LAYER 15
#define SUPERMATTER_WALL_LAYER 16
#define SPEECH_INDICATOR_LAYER 17

//FUTURE FULLSCREEN_PLANE
#define FULLSCREEN_LAYER 18
#define DAMAGE_LAYER 19
#define IMPAIRED_LAYER 20
#define BLIND_LAYER 21
#define CRIT_LAYER 22

//FUTURE HUD_PLANE
#define UNDER_HUD_LAYER 23
#define HUD_BASE_LAYER 24
#define HUD_ITEM_LAYER 25
#define HUD_ABOVE_ITEM_LAYER 26
#define RADIAL_BACKGROUND_LAYER 26.5
#define RADIAL_BASE_LAYER 27
#define RADIAL_CONTENT_LAYER 28
#define OBSERVER_PLANE 3

#define LIGHTING_PLANE 4
#define LIGHTING_LAYER 1

#define EFFECTS_ABOVE_LIGHTING_PLANE 5
#define EYEGLOW_LAYER 1
#define BEAM_PROJECTILE_LAYER 2
#define SUPERMATTER_WALL_LAYER 3
#define LIGHTNING_LAYER 4

#define FULLSCREEN_PLANE 6
#define FULLSCREEN_LAYER 1
#define DAMAGE_LAYER 2
#define IMPAIRED_LAYER 3
#define BLIND_LAYER 4
#define CRIT_LAYER 5

#define HUD_PLANE 7
#define UNDER_HUD_LAYER 1
#define HUD_BASE_LAYER 2
#define HUD_ITEM_LAYER 3
#define HUD_ABOVE_ITEM_LAYER 4
#define RADIAL_BACKGROUND_LAYER 5
#define RADIAL_BASE_LAYER 6
#define RADIAL_CONTENT_LAYER 7

//-------------------- Rendering ---------------------

/// Semantics - The final compositor or a filter effect renderer
#define RENDER_GROUP_NONE null

/// Things to be drawn within the game context
#define RENDER_GROUP_SCENE 990

/// Things to be drawn within the screen context
#define RENDER_GROUP_SCREEN 995

/// The final render group, for compositing
#define RENDER_GROUP_FINAL 999

/// Integer (One of `*_PLANE`). The atom's rendering plane. See `code\__defines\__renderer.dm` for a list of valid planes. Also see the DM Reference for `plane var (atom)`.
/atom/plane = DEFAULT_PLANE

#define DEFAULT_APPEARANCE_FLAGS (PIXEL_SCALE)

#define DEFAULT_RENDERER_APPEARANCE_FLAGS (PLANE_MASTER | NO_CLIENT_COLOR)

/atom/appearance_flags = DEFAULT_APPEARANCE_FLAGS
/atom/movable/appearance_flags = DEFAULT_APPEARANCE_FLAGS | TILE_BOUND // Most AMs are not visibly bigger than a tile.
/image/appearance_flags = DEFAULT_APPEARANCE_FLAGS
/mutable_appearance/appearance_flags = DEFAULT_APPEARANCE_FLAGS // Inherits /image but re docs, subject to change

/atom/proc/hud_layerise()
plane = HUD_PLANE
layer = HUD_ITEM_LAYER

/image/proc/turf_decal_layerise()
Expand Down
3 changes: 0 additions & 3 deletions code/__DEFINES/zcopy.dm
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
#define OPENTURF_MAX_PLANE -71
#define OPENTURF_CAP_PLANE -70 // The multiplier goes here so it'll be on top of every other overlay.
#define OPENTURF_MAX_DEPTH 10 // The maxiumum number of planes deep we'll go before we just dump everything on the same plane.
#define SHADOWER_DARKENING_FACTOR 0.7 // The multiplication factor for openturf shadower darkness. Lighting will be multiplied by this.
#define SHADOWER_DARKENING_COLOR "#999999" // The above, but as an RGB string for lighting-less turfs.
4 changes: 2 additions & 2 deletions code/__HELPERS/icon_smoothing.dm
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,10 @@
var/istate = "[((x + y) ^ ~(x * y) + z) % 25]"
underlay_appearance.icon = 'icons/turf/space.dmi'
underlay_appearance.icon_state = istate
underlay_appearance.plane = PLANE_SPACE_BACKGROUND
underlay_appearance.plane = SPACE_PLANE

var/image/dust = image('icons/turf/space_parallax1.dmi', istate)
dust.plane = PLANE_SPACE_DUST
dust.plane = DUST_PLANE
dust.alpha = 80
dust.blend_mode = BLEND_ADD
U += dust
Expand Down
2 changes: 1 addition & 1 deletion code/__HELPERS/overlay.dm
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

/proc/make_screen_overlay(icon, icon_state, brightness_factor = null, glow_radius = 1)
var/image/overlay = image(icon, icon_state)
overlay.layer = ABOVE_LIGHTING_LAYER
overlay.plane = EFFECTS_ABOVE_LIGHTING_PLANE
var/image/underlay = image(overlay)
underlay.alpha = 128
underlay.filters = filter(type="bloom", offset=glow_radius, size=glow_radius*2, threshold="#000")
Expand Down
1 change: 1 addition & 0 deletions code/_onclick/hud/fullscreen.dm
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
icon_state = "default"
screen_loc = "CENTER-7,CENTER-7"
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
plane = FULLSCREEN_PLANE
layer = FULLSCREEN_LAYER
var/severity = 0
var/allstate = 0 //shows if it should show up for dead people too
Expand Down
62 changes: 62 additions & 0 deletions code/_onclick/hud/rendering/_render_readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# The Render Readme

1. [Byond internal functionality](#byond-internal-functionality)
2. [Known internal snowflake](#known-internal-snowflake)
3. [The rendering solution](#the-rendering-solution)
4. [Render plates](#render-plates)

## Byond internal functionality
This part of the guide will assume that you have read the byond reference entry for rendering at www.byond.com/docs/ref//#/{notes}/renderer

When you create an atom, this will always create an internal byond structure called an "appearance". This appearance you will likely be familiar with, as it is exposed through the /atom/var/appearance var. This appearance var holds data on how to render the object, ie what icon/icon_state/color etc it is using. Note that appearance vars will always copy, and do not hold a reference. When you update a var, for example lets pretend we add a filter, the appearance will be updated to include the filter. Note that, however, vis_contents objets are uniquely excluded from appearances. Then, when the filter is updated, the appearance will be recreated, and the atom marked as "dirty". After it has been updated, the SendMaps() function (sometimes also called maptick), which is a internal byond function that iterates over all objects in a clients view and in the clients.mob.contents, checks for "dirty" atoms, then resends any "dirty" appearances to clients as needed and unmarks them as dirty. This function is notoriosly slow, but we can see it's tick usage through the world.map_cpu var. We can also avoid more complex checks checking whether an object is visible on a clients screen by using the TILE_BOUND appearance flag.

Finally, we arrive at clientside behavior, where we have two main clientside functions: GetMapIcons, and Render. GetMapIcons is repsonsible for actual rendering calculations on the clientside, such as "Group Icons and Set bounds", which performs clientside calculations for transform matrixes. Note that particles here are handled in a seperate thread and are not diplayed in the clientside profiler. Render handles the actual drawing of the screen.

## Known internal snowflake
The following is an incomplete list of pitfalls that come from byond snowflake that are known, this list is obviously incomplete.

1. Transforms are very slow on clientside. This is not usually noticable, but if you start using large amounts of them it will grind you to a halt quickly, regardless of whether its on overlays or objs
2. The darkness plane. The darkness plane has specific variables it needs to render correctly, and these can be found in the plane masters file. it is composed internally of two parts, a black mask over the clients screen, and a non rendering mask that blocks all luminosity=0 turfs and their contents from rendering if the SEE_BLACKNESS flag is set properly. It behaves very oddly, such as forcing itself to ALWAYS render or pre-render on blend_multiply blend mode or refusing to render the black mask properly otherwise. The blocker will always block rendering but the mask can be layered under other objects.
3. render_target/source. Render_target/source will only copy certain rendering instructions, and these are only defined as "etc." in the byond reference. Known non copied appearance vars include: blend_mode, plane, layer, vis_contents, mouse_opacity...
4. Large icons on the screen that peek over the edge will instead of only rendering partly like you would expect will instead stretch the screen while not adgusting the render buffer, which means that you can actively see as tiles and map objects are rendered. You can use this for an easy "offscreen" UI.
5. Numerically large filters on objects of any size will torpedo performance, even though large objects with small filters will perform massively better. (ie blur(size=20) BAD)
6. Texture Atlas: the texture atlas byond uses to render icons is very susceptible to corruption and can regularily replace icons with other icons or just not render at all. This can be exasperated by alt tabbing or pausing the dreamseeker process.
7. The renderer is awful code and lummox said he will try changing a large part of it for 515 so keep an eye on that
8. Byond uses DirectX 9 (Lummox said he wants to update to DirectX 11)
9. Particles are just fancy overlays and are not independent of their owner
10. Maptick items inside mob.contents are cheaper compared to most other movables

## The rendering solution
One of the main issues with making pretty effects is how objects can only render to one plane, and how filters can only be applied to single objects. Quite simply it means we cant apply effects to multiple planes at once, and an effect to one plane only by treating it as a single unit:

![](https://raw.githubusercontent.com/tgstation/documentation-assets/main/rendering/renderpipe_old.png)

A semi-fix to stop from having to apply effects to every single plane is to use the render controllers, to automatically apply filters and colors automatically onto their controlled planes.

The solution is thus instead we replace plane masters rendering directly to client with planes that render multiple planes onto them as objects in order to be able to affect multiple planes while treating them as a single object. This is done by relaying the plane using a "render relay" onto a "render plate" which acts as a plane master of plane masters of sorts, and since planes are rendered onto it as single objects any filters we apply to them will render over the planes, treating them as a single unit.

![](https://raw.githubusercontent.com/tgstation/documentation-assets/main/rendering/renderpipe_refactored.png)

We can also choose to render these by decreasing the scaling all applied effects (effect_size/number_of_plates_rendered_to) then rendering it onto multiple planes:

![](https://raw.githubusercontent.com/tgstation/documentation-assets/main/rendering/renderpipe_refactored_multiple.png)

Through these this allows us to treat planes as single objects, and lets us distort them as a single unit, most notably works wonders with the displacement filter. Specifically, here you can displacement_filter a plane onto a plate, which then will treat all the other planes rendered on that plate as a single unit.

## Render plates

The rendering system uses two objects to unify planes: render_relay and render_plates. Render relays use render_target/source and the relay_render_to_plane proc to replicate the plane master on the render relay. This render relay is then rendered onto a render_plate, which is a plane master that renders the render_relays onto itself. This plate can then be hierachically rendered with the same process until it reaches the master render_plate, which is the plate that will actually render to the player. These plates naturally in the byond style have quirks. For example, rendering to two plates will double any effects such as color or filters, and as such you need to carefully manage how you render them. Keep in mind as well that when sorting the layers for rendering on a plane that they should not be negative, this is handled automatically in relay_render_to_plane. When debugging note that mouse_opacity can act bizzarly with this method, such as only allowing you to click things that are layered over objects on a certain plane but auomatically setting the mouse_opacity should be handling this. Note that if you decide to manipulate a plane with internal byond objects that you will have to manually extrapolate the vars that are set if you want to render them to another plane (See blackness plane for example), and that this is not documented anywhere.


Goodluck and godspeed with coding
- Just another contributor

## Baystation/Aurorastation Differences

The bay implementation differs in some points but you can treat the prior sections as broadly accurate.

- Locally we have refactored and simplified the above concepts down to `/atom/movable/renderer` and undifferentiated relay movables.
- There is no type destinction between renderers intended to draw normal entities and renderers intended to draw other renderers.
- We instead refer to these as "Plane Renderers" and "Group Renderers", and note that Group Renderers may render *other* Group Renderers in turn.
- We additionally add the extra "Effect Renderer" as a distinct concept. Effect renderers are more virtual, and drawn to other renderers by filters instead.
- See the comments in _renderer.dm for detail next to code.
Loading
Loading