/
_renderer.dm
353 lines (287 loc) · 10.9 KB
/
_renderer.dm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/* *
* Renderers
* Renderers are virtual objects that act as draw groups of things, including
* other Renderers. Renderers are similar to older uses of PLANE_MASTER but
* employ render targets using the "*" slate prefix to draw off-screen, to be
* composited in a controlled and flexible order and in some cases, reuse for
* visual effects.
*/
/// The base /renderer definition and defaults.
/atom/movable/renderer
abstract_type = /atom/movable/renderer
appearance_flags = DEFAULT_RENDERER_APPEARANCE_FLAGS
screen_loc = "CENTER"
plane = LOWEST_PLANE
blend_mode = BLEND_OVERLAY
/// The compositing renderer this renderer belongs to.
var/group = RENDER_GROUP_FINAL
/// The relay movable used to composite this renderer to its group.
var/atom/movable/relay // Also see https://secure.byond.com/forum/?post=2141928 maybe.
/// Optional blend mode override for the renderer's composition relay.
var/relay_blend_mode
/// If text, uses the text or, if TRUE, uses "*AUTO-[name]"
var/render_target_name = TRUE
var/mob/owner = null
/atom/movable/renderer/Destroy()
owner = null
QDEL_NULL(relay)
return ..()
INITIALIZE_IMMEDIATE(/atom/movable/renderer)
/atom/movable/renderer/Initialize(mapload, mob/owner)
. = ..()
if (. == INITIALIZE_HINT_QDEL)
return
src.owner = owner
if (isnull(group))
if (istext(render_target_name))
render_target = render_target_name
return
if (istext(render_target_name))
render_target = render_target_name
else if (render_target_name)
render_target = "*[ckey(name)]"
relay = new
relay.screen_loc = "CENTER"
relay.appearance_flags = PASS_MOUSE | NO_CLIENT_COLOR | KEEP_TOGETHER
relay.name = "[render_target] relay"
relay.mouse_opacity = mouse_opacity
relay.render_source = render_target
relay.layer = (plane + abs(LOWEST_PLANE)) * 0.5
relay.plane = group
if (isnull(relay_blend_mode))
relay.blend_mode = blend_mode
else
relay.blend_mode = relay_blend_mode
/**
* Graphic preferences
*
* Some renderers may be able to use a graphic preference to determine how to display effects. For example reduce particle counts or filter variables.
*/
/atom/movable/renderer/proc/GraphicsUpdate()
return
/**
* Renderers on /mob
* We attach renderers to mobs for their lifespan. Only mobs with clients get
* renderers, and they are removed again when the mob loses its client. Mobs
* get their own unique renderer instances but it would not be inconceivable
* to share them globally.
*/
/// The list of renderers associated with this mob.
/mob/var/list/atom/movable/renderer/renderers
/// Creates the mob's renderers on /Login()
/mob/proc/CreateRenderers()
if (!renderers)
renderers = list()
for (var/atom/movable/renderer/renderer as anything in subtypesof(/atom/movable/renderer))
if(ispath(renderer, /atom/movable/renderer/shared))
continue
renderer = new renderer (null, src)
renderers[renderer] = renderer.plane // (renderer = plane) format for visual debugging
if (renderer.relay)
my_client.screen += renderer.relay
my_client.screen += renderer
for (var/atom/movable/renderer/zrenderer as anything in GLOB.zmimic_renderers)
if (zrenderer.relay)
my_client.screen += zrenderer.relay
my_client.screen += zrenderer
/// Removes the mob's renderers on /Logout()
/mob/proc/RemoveRenderers()
if(my_client)
for(var/atom/movable/renderer/renderer as anything in renderers)
my_client.screen -= renderer
if (renderer.relay)
my_client.screen -= renderer.relay
qdel(renderer)
for (var/atom/movable/renderer/renderer as anything in GLOB.zmimic_renderers)
my_client.screen -= renderer
if (renderers)
renderers.Cut()
/* *
* Plane Renderers
* We treat some renderers as planes with layers. When some atom has the same plane
* as a Plane Renderer, it is drawn by that renderer. The layer of the atom determines
* its draw order within the scope of the renderer. The draw order of same-layered things
* is probably by atom contents order, but can be assumed not to matter - if it's out of
* order, it should have a more appropriate layer value.
* Higher plane values are composited over lower. Here, they are ordered from under to over.
*/
/// Handles byond internal letterboxing. Avoid touching.
/atom/movable/renderer/letterbox
name = "Letterbox"
group = RENDER_GROUP_SCENE
plane = BLACKNESS_PLANE
blend_mode = BLEND_MULTIPLY
mouse_opacity = MOUSE_OPACITY_UNCLICKABLE
/atom/movable/renderer/space
name = "Space"
group = RENDER_GROUP_SCENE
plane = SPACE_PLANE
/atom/movable/renderer/skybox
name = "Skybox"
group = RENDER_GROUP_SCENE
plane = SKYBOX_PLANE
relay_blend_mode = BLEND_MULTIPLY
//Z Mimic planemasters -> Could apply scaling for parallax though that requires copying appearances from adjacent turfs
GLOBAL_LIST_EMPTY(zmimic_renderers)
/hook/startup/proc/create_global_renderers() //Some (most) renderers probably do not need to be instantiated per mob. So may as well make them global and just add to screen
//Zmimic planemasters
for(var/i = 0 to OPENTURF_MAX_DEPTH)
GLOB.zmimic_renderers += new /atom/movable/renderer/shared/zmimic(null, null, OPENTURF_MAX_PLANE - i)
return TRUE
/atom/movable/renderer/shared/zmimic
name = "Zrenderer"
group = RENDER_GROUP_SCENE
/atom/movable/renderer/shared/zmimic/Initialize(mapload, _owner, _plane)
plane = _plane
name = "Zrenderer [plane]"
. = ..()
/atom/movable/renderer/shared/zmimic/Destroy()
GLOB.zmimic_renderers -= src
return ..()
// Draws the game world; live mobs, items, turfs, etc.
/atom/movable/renderer/game
name = "Game"
group = RENDER_GROUP_SCENE
plane = DEFAULT_PLANE
/// Draws observers; ghosts, camera eyes, etc.
/atom/movable/renderer/observers
name = "Observers"
group = RENDER_GROUP_SCENE
plane = OBSERVER_PLANE
/// Draws darkness effects.
/atom/movable/renderer/lighting
name = "Lighting"
group = RENDER_GROUP_SCENE
plane = LIGHTING_PLANE
relay_blend_mode = BLEND_MULTIPLY
mouse_opacity = MOUSE_OPACITY_UNCLICKABLE
/atom/movable/renderer/lighting/Initialize()
. = ..()
filters += filter(
type = "alpha",
render_source = EMISSIVE_TARGET,
flags = MASK_INVERSE
)
/// Draws visuals that should not be affected by darkness.
/atom/movable/renderer/above_lighting
name = "Above Lighting"
group = RENDER_GROUP_SCENE
plane = EFFECTS_ABOVE_LIGHTING_PLANE
/// Draws full screen visual effects, like pain and bluespace.
/atom/movable/renderer/screen_effects
name = "Screen Effects"
group = RENDER_GROUP_SCENE
plane = FULLSCREEN_PLANE
mouse_opacity = MOUSE_OPACITY_UNCLICKABLE
/// Draws user interface elements.
/atom/movable/renderer/interface
name = "Interface"
group = RENDER_GROUP_SCREEN
plane = HUD_PLANE
/* *
* Group renderers
* We treat some renderers as render groups that other renderers subscribe to. Renderers
* subscribe themselves to groups by setting a group value equal to the plane of a Group
* Renderer. This value is used for the Renderer's relay to include it into the Group, and
* the Renderer's plane is used as the relay's layer.
* Group renderers can subscribe themselves to other Group Renderers. This allows for more
* granular manipulation of how the final scene is composed.
*/
/// Render group for stuff INSIDE the typical game context - people, items, lighting, etc.
/atom/movable/renderer/scene_group
name = "Scene Group"
group = RENDER_GROUP_FINAL
plane = RENDER_GROUP_SCENE
/// Render group for stuff OUTSIDE the typical game context - UI, full screen effects, etc.
/atom/movable/renderer/screen_group
name = "Screen Group"
group = RENDER_GROUP_FINAL
plane = RENDER_GROUP_SCREEN
/// Render group for final compositing before user display.
/atom/movable/renderer/final_group
name = "Final Group"
group = RENDER_GROUP_NONE
plane = RENDER_GROUP_FINAL
/* *
* Effect Renderers
* Some renderers are used to produce complex screen effects. These are drawn using filters
* rather than composition groups, and may be added to another renderer in the following
* fashion. Setting a render_target_name with no group is the expected patter for Effect
* Renderers as it allows them to draw to a slate that will be empty unless a relevant
* behavior, such as the effect atom below, causes them to be noticeable.
*/
/// Renders the /obj/effect/warp example effect as well as gravity catapult effects
/atom/movable/renderer/warp
name = "Warp Effect"
group = RENDER_GROUP_NONE
plane = WARP_EFFECT_PLANE
render_target_name = "*warp"
mouse_opacity = MOUSE_OPACITY_UNCLICKABLE
//Similar to warp but not as strong
/atom/movable/renderer/heat
name = "Heat Effect"
group = RENDER_GROUP_NONE
plane = HEAT_EFFECT_PLANE
render_target_name = HEAT_COMPOSITE_TARGET
mouse_opacity = MOUSE_OPACITY_UNCLICKABLE
var/obj/gas_heat_object = null
var/obj/gas_cold_object = null // Not strictly a heat effect but similar setup so may as well
/atom/movable/renderer/heat/proc/Setup()
var/mob/M = owner
if(istype(M))
var/quality = M.get_preference_value(/datum/client_preference/graphics_quality)
if(gas_heat_object)
vis_contents -= gas_heat_object
if(gas_cold_object)
vis_contents -= gas_cold_object
if (quality == GLOB.PREF_LOW)
QDEL_NULL(gas_heat_object)
gas_heat_object = new /obj/heat(null)
QDEL_NULL(gas_cold_object)
gas_cold_object = new /obj/effect/cold_mist_gas(null)
else
QDEL_NULL(gas_heat_object)
QDEL_NULL(gas_cold_object)
gas_cold_object = new /obj/particle_emitter/mist/gas(null)
if (quality == GLOB.PREF_MED)
gas_heat_object = new /obj/particle_emitter/heat(null)
else if (quality == GLOB.PREF_HIGH)
gas_heat_object = new /obj/particle_emitter/heat/high(null)
vis_contents += gas_heat_object
if(config.enable_cold_mist)
vis_contents += gas_cold_object
/atom/movable/renderer/heat/Initialize()
. = ..()
Setup()
/atom/movable/renderer/heat/GraphicsUpdate()
. = ..()
Setup()
/atom/movable/renderer/scene_group/Initialize()
. = ..()
filters += filter(type = "displace", render_source = "*warp", size = 5)
filters += filter(type = "displace", render_source = HEAT_COMPOSITE_TARGET, size = 2.5)
/*!
* This system works by exploiting BYONDs color matrix filter to use layers to handle emissive blockers.
*
* Emissive overlays are pasted with an atom color that converts them to be entirely some specific color.
* Emissive blockers are pasted with an atom color that converts them to be entirely some different color.
* Emissive overlays and emissive blockers are put onto the same plane.
* The layers for the emissive overlays and emissive blockers cause them to mask eachother similar to normal BYOND objects.
* A color matrix filter is applied to the emissive plane to mask out anything that isn't whatever the emissive color is.
* This is then used to alpha mask the lighting plane.
*
* This works best if emissive overlays applied only to objects that emit light,
* since luminosity=0 turfs may not be rendered.
*/
/atom/movable/renderer/emissive
name = "Emissive"
group = RENDER_GROUP_NONE
plane = EMISSIVE_PLANE
mouse_opacity = MOUSE_OPACITY_UNCLICKABLE
render_target_name = EMISSIVE_TARGET
/atom/movable/renderer/emissive/Initialize()
. = ..()
filters += filter(
type = "color",
color = GLOB.em_mask_matrix
)