@quenty/depthoffield 11.0.0 → 11.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [11.1.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/depthoffield@11.0.0...@quenty/depthoffield@11.1.0) (2024-03-09)
7
+
8
+
9
+ ### Features
10
+
11
+ * Refactor DepthOfFieldService (Technically a breaking change, but I don't think anyone is using this package) ([b43d54e](https://github.com/Quenty/NevermoreEngine/commit/b43d54ea4edccf33def7e8d60c97d9dbec694379))
12
+
13
+
14
+
15
+
16
+
6
17
  # [11.0.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/depthoffield@10.0.0...@quenty/depthoffield@11.0.0) (2024-02-14)
7
18
 
8
19
  **Note:** Version bump only for package @quenty/depthoffield
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quenty/depthoffield",
3
- "version": "11.0.0",
3
+ "version": "11.1.0",
4
4
  "description": "Depth of field service to allow multiple systems to write depth of field",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -26,15 +26,22 @@
26
26
  ],
27
27
  "dependencies": {
28
28
  "@quenty/acceltween": "^2.3.0",
29
- "@quenty/baseobject": "^10.0.0",
30
- "@quenty/loader": "^10.0.0",
31
- "@quenty/maid": "^3.0.0",
32
- "@quenty/signal": "^7.0.0",
29
+ "@quenty/baseobject": "^10.1.0",
30
+ "@quenty/basicpane": "^13.1.0",
31
+ "@quenty/blend": "^12.1.0",
32
+ "@quenty/brio": "^14.1.0",
33
+ "@quenty/instanceutils": "^13.1.0",
34
+ "@quenty/loader": "^10.1.0",
35
+ "@quenty/maid": "^3.1.0",
36
+ "@quenty/math": "^2.6.0",
37
+ "@quenty/rx": "^13.1.0",
38
+ "@quenty/signal": "^7.1.0",
33
39
  "@quenty/steputils": "^3.3.0",
34
- "@quenty/valueobject": "^13.0.0"
40
+ "@quenty/transitionmodel": "^7.1.0",
41
+ "@quenty/valueobject": "^13.1.0"
35
42
  },
36
43
  "publishConfig": {
37
44
  "access": "public"
38
45
  },
39
- "gitHead": "63f949a67b77d3edc98b358cace163da8c789e9f"
46
+ "gitHead": "e0148dde5ca3864389a0faa2da66153a776acc1e"
40
47
  }
@@ -0,0 +1,314 @@
1
+ --[=[
2
+ Handles interpolation of depth of field, which is tricky due to how Roblox implemented the shader
3
+ and how it interacts with other depth of field effects.
4
+
5
+ @class DepthOfFieldEffect
6
+ ]=]
7
+
8
+ local require = require(script.Parent.loader).load(script)
9
+
10
+ local Lighting = game:GetService("Lighting")
11
+ local Workspace = game:GetService("Workspace")
12
+
13
+ local Blend = require("Blend")
14
+ local Maid = require("Maid")
15
+ local Math = require("Math")
16
+ local Observable = require("Observable")
17
+ local Rx = require("Rx")
18
+ local RxBrioUtils = require("RxBrioUtils")
19
+ local RxInstanceUtils = require("RxInstanceUtils")
20
+ local SpringObject = require("SpringObject")
21
+ local SpringTransitionModel = require("SpringTransitionModel")
22
+ local TransitionModel = require("TransitionModel")
23
+ local ValueObject = require("ValueObject")
24
+ local RxAttributeUtils = require("RxAttributeUtils")
25
+
26
+ local DepthOfFieldEffect = setmetatable({}, TransitionModel)
27
+ DepthOfFieldEffect.ClassName = "DepthOfFieldEffect"
28
+ DepthOfFieldEffect.__index = DepthOfFieldEffect
29
+
30
+ function DepthOfFieldEffect.new()
31
+ local self = setmetatable(TransitionModel.new(), DepthOfFieldEffect)
32
+
33
+ self._focusDistanceSpring = self._maid:Add(SpringObject.new(40, 30))
34
+ self._inFocusRadiusSpring = self._maid:Add(SpringObject.new(35, 30))
35
+ self._nearIntensitySpring = self._maid:Add(SpringObject.new(1, 30))
36
+ self._farIntensitySpring = self._maid:Add(SpringObject.new(1, 30))
37
+
38
+ self._focusDistanceSpring.Epsilon = 1e-3
39
+ self._focusDistanceSpring.Epsilon = 1e-3
40
+ self._nearIntensitySpring.Epsilon = 1e-4
41
+ self._farIntensitySpring.Epsilon = 1e-4
42
+
43
+ self._percentVisibleModel = self._maid:Add(SpringTransitionModel.new())
44
+ self._percentVisibleModel:SetSpeed(10)
45
+ self._percentVisibleModel:BindToPaneVisbility(self)
46
+
47
+ self:SetPromiseShow(function(_, doNotAnimate)
48
+ return self._percentVisibleModel:PromiseShow(doNotAnimate)
49
+ end)
50
+ self:SetPromiseHide(function(_, doNotAnimate)
51
+ return self._percentVisibleModel:PromiseHide(doNotAnimate)
52
+ end)
53
+
54
+ self._maid:GiveTask(self:_render():Subscribe(function(gui)
55
+ self.Gui = gui
56
+ end))
57
+
58
+ return self
59
+ end
60
+
61
+ function DepthOfFieldEffect:SetShowSpeed(speed)
62
+ self._percentVisibleModel:SetSpeed(speed)
63
+ end
64
+
65
+ --[=[
66
+ Sets the target depth of field distance
67
+ @param focusDistanceTarget number
68
+ @param doNotAnimate boolean
69
+ ]=]
70
+ function DepthOfFieldEffect:SetFocusDistanceTarget(focusDistanceTarget, doNotAnimate)
71
+ assert(type(focusDistanceTarget) == "number", "Bad focusDistanceTarget")
72
+
73
+ self._focusDistanceSpring:SetTarget(focusDistanceTarget, doNotAnimate)
74
+ end
75
+
76
+ --[=[
77
+ Sets the target depth of field radius
78
+ @param inFocusRadiusTarget number
79
+ @param doNotAnimate boolean
80
+ ]=]
81
+ function DepthOfFieldEffect:SetInFocusRadiusTarget(inFocusRadiusTarget, doNotAnimate)
82
+ assert(type(inFocusRadiusTarget) == "number", "Bad inFocusRadiusTarget")
83
+
84
+ self._inFocusRadiusSpring:SetTarget(inFocusRadiusTarget, doNotAnimate)
85
+ end
86
+
87
+ --[=[
88
+ Sets the near intensity target
89
+ @param nearIntensityTarget number
90
+ @param doNotAnimate boolean
91
+ ]=]
92
+ function DepthOfFieldEffect:SetNearIntensityTarget(nearIntensityTarget, doNotAnimate)
93
+ assert(type(nearIntensityTarget) == "number", "Bad nearIntensityTarget")
94
+
95
+ self._nearIntensitySpring:SetTarget(nearIntensityTarget, doNotAnimate)
96
+ end
97
+
98
+ --[=[
99
+ Sets the far intensity target
100
+ @param farIntensityTarget number
101
+ @param doNotAnimate boolean
102
+ ]=]
103
+ function DepthOfFieldEffect:SetFarIntensityTarget(farIntensityTarget, doNotAnimate)
104
+ assert(type(farIntensityTarget) == "number", "Bad farIntensityTarget")
105
+
106
+ self._farIntensitySpring:SetTarget(farIntensityTarget, doNotAnimate)
107
+ end
108
+
109
+ --[=[
110
+ Retrieves the distance target
111
+ @return number
112
+ ]=]
113
+ function DepthOfFieldEffect:GetFocusDistanceTarget()
114
+ return self._focusDistanceSpring.Target
115
+ end
116
+
117
+ --[=[
118
+ Retrieves the radius target
119
+ @return number
120
+ ]=]
121
+ function DepthOfFieldEffect:GetInFocusRadiusTarget()
122
+ return self._inFocusRadiusSpring.Target
123
+ end
124
+
125
+ --[=[
126
+ Retrieve the near intensity target
127
+ @return number
128
+ ]=]
129
+ function DepthOfFieldEffect:GetNearIntensityTarget()
130
+ return self._nearIntensitySpring.Target
131
+ end
132
+
133
+ --[=[
134
+ Retrieve the far intensity target
135
+ @return number
136
+ ]=]
137
+ function DepthOfFieldEffect:GetFarIntensityTarget()
138
+ return self._farIntensitySpring.Target
139
+ end
140
+
141
+ function DepthOfFieldEffect:_render()
142
+ -- Note: Roblox blends DepthOfField by picking highest value in each category, so we always drive the "hidden"
143
+ -- state towards zero. The only issue is `InFocusRadius` must be rendered at target of 500 to fade "out" the effect
144
+ -- if other
145
+
146
+ return Blend.New "DepthOfFieldEffect" {
147
+ Name = "DepthOfField";
148
+ Enabled = Blend.Computed(self._percentVisibleModel, function(visible)
149
+ return visible > 0
150
+ end);
151
+ FocusDistance = Blend.Computed(
152
+ self._percentVisibleModel,
153
+ self._focusDistanceSpring,
154
+ self:_observeRenderedDepthOfFieldState(),
155
+ function(percentVisible, focusDistance, externalRenderedState)
156
+ -- This help smooth out interpolation
157
+
158
+ local externalFocusDistance = focusDistance
159
+ if externalRenderedState and externalRenderedState.focusDistance then
160
+ externalFocusDistance = externalRenderedState.focusDistance
161
+ end
162
+
163
+ return Math.map(percentVisible, 0, 1, externalFocusDistance, focusDistance)
164
+ end);
165
+ InFocusRadius = Blend.Computed(
166
+ self._percentVisibleModel,
167
+ self._inFocusRadiusSpring,
168
+ self:_observeRenderedDepthOfFieldState(),
169
+ function(percentVisible, inFocusRadius, externalRenderedState)
170
+ -- If we tween this to 0 then we create lots of blur as we do so
171
+ -- However, if we tween out to 500 then we get picked, blocking in-focus blur...
172
+ -- So tween to the minimum of other enabled depth of fields
173
+
174
+ -- Tweening just intensity doesn't work because of the way Roblox shaders work.
175
+
176
+ local externalInFocusRadius = 500
177
+ if externalRenderedState and externalRenderedState.inFocusRadius then
178
+ externalInFocusRadius = externalRenderedState.inFocusRadius
179
+ end
180
+
181
+ return Math.map(percentVisible, 0, 1, externalInFocusRadius, inFocusRadius)
182
+ end);
183
+ NearIntensity = Blend.Computed(self._percentVisibleModel, self._nearIntensitySpring, function(percentVisible, intensity)
184
+ return Math.map(percentVisible, 0, 1, 0, intensity)
185
+ end);
186
+ FarIntensity = Blend.Computed(self._percentVisibleModel, self._farIntensitySpring, function(percentVisible, intensity)
187
+ return Math.map(percentVisible, 0, 1, 0, intensity)
188
+ end);
189
+ [Blend.Instance] = function(gui)
190
+ self._depthOfField = gui
191
+
192
+ -- Setup attributes so multiple tweening depth off fields with this system isn't sad
193
+ self._maid:GiveTask(Blend.Computed(self._percentVisibleModel, self._inFocusRadiusSpring, function(percentVisible, inFocusRadius)
194
+ return Math.map(percentVisible, 0, 1, 0, inFocusRadius)
195
+ end):Subscribe(function(targetDepthOfFieldRadius)
196
+ self._depthOfField:SetAttribute("DepthOfFieldEffect_TargetInFocusRadius", targetDepthOfFieldRadius)
197
+ end))
198
+
199
+ self._maid:GiveTask(Blend.Computed(self._percentVisibleModel, self._focusDistanceSpring, function(percentVisible, focusDistance)
200
+ return Math.map(percentVisible, 0, 1, 0, focusDistance)
201
+ end):Subscribe(function(targetDepthOfFieldRadius)
202
+ self._depthOfField:SetAttribute("DepthOfFieldEffect_TargetFocusDistance", targetDepthOfFieldRadius)
203
+ end))
204
+ end;
205
+ }
206
+ end
207
+
208
+ function DepthOfFieldEffect:_observeRenderedDepthOfFieldState()
209
+ if self._observeOtherStates then
210
+ return self._observeOtherStates
211
+ end
212
+
213
+ self._observeOtherStates = Observable.new(function(sub)
214
+ local topMaid = Maid.new()
215
+
216
+ local result = topMaid:Add(ValueObject.new(nil))
217
+
218
+ local latestStates = {}
219
+
220
+ local function update()
221
+ local output = {
222
+ inFocusRadius = 0;
223
+ focusDistance = 0;
224
+ externalCount = 0;
225
+ }
226
+
227
+ for _, state in pairs(latestStates) do
228
+ if state.depthOfField == self._depthOfField then
229
+ continue
230
+ end
231
+
232
+ if state.enabled then
233
+ output.externalCount += 1
234
+ local inFocusRadius
235
+ if state.targetInFocusRadius then
236
+ inFocusRadius = state.targetInFocusRadius
237
+ else
238
+ inFocusRadius = state.inFocusRadius
239
+ end
240
+
241
+ output.inFocusRadius = math.max(output.inFocusRadius, inFocusRadius)
242
+
243
+ local focusDistance
244
+ if state.targetFocusDistance then
245
+ focusDistance = state.targetFocusDistance
246
+ else
247
+ focusDistance = state.focusDistance
248
+ end
249
+ output.focusDistance = math.max(output.focusDistance, focusDistance)
250
+ end
251
+ end
252
+
253
+ if output.externalCount == 0 then
254
+ result.Value = nil
255
+ else
256
+ result.Value = output
257
+ end
258
+ end
259
+
260
+ topMaid:GiveTask(self:_observeAllDepthOfFieldBrio():Subscribe(function(brio)
261
+ if brio:IsDead() then
262
+ return
263
+ end
264
+
265
+ local maid, depthOfField = brio:ToMaidAndValue()
266
+ maid:GiveTask(Rx.combineLatest({
267
+ depthOfField = depthOfField;
268
+
269
+ inFocusRadius = RxInstanceUtils.observeProperty(depthOfField, "InFocusRadius");
270
+ targetInFocusRadius = RxAttributeUtils.observeAttribute(depthOfField, "DepthOfFieldEffect_TargetInFocusRadius");
271
+
272
+ focusDistance = RxInstanceUtils.observeProperty(depthOfField, "FocusDistance");
273
+ targetFocusDistance = RxAttributeUtils.observeAttribute(depthOfField, "DepthOfFieldEffect_TargetFocusDistance");
274
+
275
+ enabled = RxInstanceUtils.observeProperty(depthOfField, "Enabled");
276
+ }):Subscribe(function(state)
277
+ if state.depthOfField == self._depthOfField then
278
+ latestStates[maid] = nil
279
+ else
280
+ latestStates[maid] = state
281
+ update()
282
+ end
283
+ end))
284
+
285
+ maid:GiveTask(function()
286
+ latestStates[maid] = nil
287
+ update()
288
+ end)
289
+ end))
290
+
291
+ topMaid:GiveTask(result:Observe():Subscribe(sub:GetFireFailComplete()))
292
+
293
+ return topMaid
294
+ end):Pipe({
295
+ Rx.cache()
296
+ })
297
+
298
+ return self._observeOtherStates
299
+ end
300
+
301
+ function DepthOfFieldEffect:_observeAllDepthOfFieldBrio()
302
+ return Rx.merge({
303
+ RxInstanceUtils.observeChildrenOfClassBrio(Lighting, "DepthOfFieldEffect");
304
+ RxInstanceUtils.observePropertyBrio(Workspace, "CurrentCamera", function(camera)
305
+ return camera ~= nil
306
+ end):Pipe({
307
+ RxBrioUtils.flatMapBrio(function(currentCamera)
308
+ return RxInstanceUtils.observeChildrenOfClassBrio(currentCamera, "DepthOfFieldEffect")
309
+ end)
310
+ })
311
+ })
312
+ end
313
+
314
+ return DepthOfFieldEffect
@@ -0,0 +1,79 @@
1
+ --[[
2
+ @class DepthOfFieldEffect.story
3
+ ]]
4
+
5
+ local require = require(game:GetService("ServerScriptService"):FindFirstChild("LoaderUtils", true).Parent).load(script)
6
+
7
+ local Workspace = game:GetService("Workspace")
8
+
9
+ local Maid = require("Maid")
10
+ local DepthOfFieldEffect = require("DepthOfFieldEffect")
11
+ local Blend = require("Blend")
12
+
13
+ return function(target)
14
+ local maid = Maid.new()
15
+
16
+ local depthOfFieldEffect = maid:Add(DepthOfFieldEffect.new())
17
+ depthOfFieldEffect:SetShowSpeed(10)
18
+ depthOfFieldEffect.Gui.Parent = Workspace.CurrentCamera
19
+ depthOfFieldEffect:Show()
20
+
21
+ local depthOfFieldEffect2 = maid:Add(DepthOfFieldEffect.new())
22
+ depthOfFieldEffect2:SetFocusDistanceTarget(100, true)
23
+ depthOfFieldEffect2:SetInFocusRadiusTarget(50, true)
24
+ depthOfFieldEffect2:SetShowSpeed(10)
25
+ depthOfFieldEffect2.Gui.Parent = Workspace.CurrentCamera
26
+ -- depthOfFieldEffect2:Show()
27
+
28
+
29
+ maid:Add(Blend.mount(target, {
30
+ Blend.New "Frame" {
31
+ Size = UDim2.new(1, 0, 1, 0);
32
+ BackgroundTransparency = 1;
33
+
34
+ Blend.New "UIListLayout" {
35
+ Padding = UDim.new(0, 5);
36
+ };
37
+
38
+ Blend.New "TextButton" {
39
+ Text = Blend.Computed(depthOfFieldEffect:ObserveVisible(), function(visible)
40
+ return string.format("Toggle 1 (%s)", visible and "on" or "off")
41
+ end);
42
+ BackgroundColor3 = Blend.Computed(depthOfFieldEffect:ObserveVisible(), function(visible)
43
+ return visible and Color3.new(0.5, 1, 0.5) or Color3.new(1, 0.5, 0.5)
44
+ end);
45
+ AutoButtonColor = true;
46
+ Size = UDim2.new(0, 100, 0, 30);
47
+ [Blend.OnEvent "Activated"] = function()
48
+ depthOfFieldEffect:Toggle()
49
+ end;
50
+
51
+ Blend.New "UICorner" {
52
+ CornerRadius = UDim.new(0, 5);
53
+ };
54
+ };
55
+
56
+ Blend.New "TextButton" {
57
+ Text = Blend.Computed(depthOfFieldEffect2:ObserveVisible(), function(visible)
58
+ return string.format("Toggle 2 (%s)", visible and "on" or "off")
59
+ end);
60
+ BackgroundColor3 = Blend.Computed(depthOfFieldEffect2:ObserveVisible(), function(visible)
61
+ return visible and Color3.new(0.5, 1, 0.5) or Color3.new(1, 0.5, 0.5)
62
+ end);
63
+ AutoButtonColor = true;
64
+ Size = UDim2.new(0, 100, 0, 30);
65
+ [Blend.OnEvent "Activated"] = function()
66
+ depthOfFieldEffect2:Toggle()
67
+ end;
68
+
69
+ Blend.New "UICorner" {
70
+ CornerRadius = UDim.new(0, 5);
71
+ };
72
+ };
73
+ };
74
+ }))
75
+
76
+ return function()
77
+ maid:DoCleaning()
78
+ end
79
+ end
@@ -1,164 +0,0 @@
1
- --[=[
2
- @class DepthOfFieldModifier
3
- ]=]
4
-
5
- local require = require(script.Parent.loader).load(script)
6
-
7
- local BaseObject = require("BaseObject")
8
- local Signal = require("Signal")
9
-
10
- local DepthOfFieldModifier = setmetatable({}, BaseObject)
11
- DepthOfFieldModifier.ClassName = "DepthOfFieldModifier"
12
- DepthOfFieldModifier.__index = DepthOfFieldModifier
13
-
14
- function DepthOfFieldModifier.new(distance, radius, nearIntensity, farIntensity)
15
- local self = setmetatable(BaseObject.new(), DepthOfFieldModifier)
16
-
17
- assert(type(distance) == "number", "Bad distance")
18
- assert(type(radius) == "number", "Bad radius")
19
- assert(type(nearIntensity) == "number", "Bad nearIntensity")
20
- assert(type(farIntensity) == "number", "Bad farIntensity")
21
-
22
- self._originalDistance = distance
23
- self._originalRadius = radius
24
- self._originalNearIntensity = nearIntensity
25
- self._originalFarIntensity = farIntensity
26
-
27
- self._distance = distance
28
- self._radius = radius
29
- self._nearIntensity = nearIntensity
30
- self._farIntensity = farIntensity
31
-
32
- --[=[
33
- Fires when the modifier is removing.
34
- @prop Removing Signal
35
- @within DepthOfFieldModifier
36
- ]=]
37
- self.Removing = Signal.new()
38
- self._maid:GiveTask(function()
39
- self.Removing:Fire()
40
- self.Removing:Destroy()
41
- end)
42
-
43
- --[=[
44
- Fires when the distance changes.
45
- @prop DistanceChanged Signal
46
- @within DepthOfFieldModifier
47
- ]=]
48
- self.DistanceChanged = Signal.new()
49
- self._maid:GiveTask(self.DistanceChanged)
50
-
51
- --[=[
52
- Fires when the radius changes.
53
- @prop RadiusChanged Signal
54
- @within DepthOfFieldModifier
55
- ]=]
56
- self.RadiusChanged = Signal.new()
57
- self._maid:GiveTask(self.RadiusChanged)
58
-
59
- self.NearIntensityChanged = Signal.new()
60
- self._maid:GiveTask(self.NearIntensityChanged)
61
-
62
- self.FarIntensityChanged = Signal.new()
63
- self._maid:GiveTask(self.FarIntensityChanged)
64
-
65
- return self
66
- end
67
-
68
- --[=[
69
- Sets the target depth of field distance
70
- @param distance number
71
- @param doNotAnimate boolean
72
- ]=]
73
- function DepthOfFieldModifier:SetDistance(distance, doNotAnimate)
74
- assert(type(distance) == "number", "Bad distance")
75
-
76
- if self._distance == distance then
77
- return
78
- end
79
-
80
- self._distance = distance
81
- self.DistanceChanged:Fire(distance, doNotAnimate)
82
- end
83
-
84
- function DepthOfFieldModifier:GetOriginalDistance()
85
- return self._originalDistance
86
- end
87
-
88
- function DepthOfFieldModifier:GetOriginalRadius()
89
- return self._originalRadius
90
- end
91
- --[=[
92
- Sets the target depth of field distance
93
- @param radius number
94
- @param doNotAnimate boolean
95
- ]=]
96
- function DepthOfFieldModifier:SetRadius(radius, doNotAnimate)
97
- assert(type(radius) == "number", "Bad radius")
98
-
99
- if self._radius == radius then
100
- return
101
- end
102
-
103
- self._radius = radius
104
- self.RadiusChanged:Fire(radius, doNotAnimate)
105
- end
106
-
107
- function DepthOfFieldModifier:SetNearIntensity(nearIntensity, doNotAnimate)
108
- assert(type(nearIntensity) == "number", "Bad nearIntensity")
109
-
110
- if self._nearIntensity == nearIntensity then
111
- return
112
- end
113
-
114
- self._nearIntensity = nearIntensity
115
- self.NearIntensityChanged:Fire(nearIntensity, doNotAnimate)
116
- end
117
-
118
- function DepthOfFieldModifier:SetFarIntensity(farIntensity, doNotAnimate)
119
- assert(type(farIntensity) == "number", "Bad farIntensity")
120
-
121
- if self._farIntensity == farIntensity then
122
- return
123
- end
124
-
125
- self._farIntensity = farIntensity
126
- self.FarIntensityChanged:Fire(farIntensity, doNotAnimate)
127
- end
128
-
129
- --[=[
130
- Retrieves the distance
131
- @return number
132
- ]=]
133
- function DepthOfFieldModifier:GetDistance()
134
- return self._distance
135
- end
136
-
137
- --[=[
138
- Retrieves the radius
139
- @return number
140
- ]=]
141
- function DepthOfFieldModifier:GetRadius()
142
- return self._radius
143
- end
144
-
145
- function DepthOfFieldModifier:GetNearIntensity()
146
- return self._nearIntensity
147
- end
148
-
149
- function DepthOfFieldModifier:GetFarIntensity()
150
- return self._farIntensity
151
- end
152
-
153
- --[=[
154
- Resets the radius
155
- @param doNotAnimate boolean
156
- ]=]
157
- function DepthOfFieldModifier:Reset(doNotAnimate)
158
- self:SetDistance(self._originalDistance, doNotAnimate)
159
- self:SetRadius(self._originalRadius, doNotAnimate)
160
- self:SetNearIntensity(self._originalNearIntensity, doNotAnimate)
161
- self:SetFarIntensity(self._originalFarIntensity, doNotAnimate)
162
- end
163
-
164
- return DepthOfFieldModifier
@@ -1,135 +0,0 @@
1
- --[=[
2
- @class DepthOfFieldService
3
- ]=]
4
-
5
- local require = require(script.Parent.loader).load(script)
6
-
7
- local Lighting = game:GetService("Lighting")
8
-
9
- local DepthOfFieldTweener = require("DepthOfFieldTweener")
10
- local DepthOfFieldModifier = require("DepthOfFieldModifier")
11
- local Maid = require("Maid")
12
- local ValueObject = require("ValueObject")
13
-
14
- local DepthOfFieldService = {}
15
- DepthOfFieldService.ServiceName = "DepthOfFieldService"
16
-
17
- --[=[
18
- Initializes the DepthOfFieldService. Should be done via [ServiceBag].
19
- @param _serviceBag ServiceBag
20
- ]=]
21
- function DepthOfFieldService:Init(_serviceBag)
22
- self._maid = Maid.new()
23
-
24
- self._topOfStack = self._maid:Add(ValueObject.new())
25
-
26
- self._depthOfField = self:_createDepthOfFIeld()
27
-
28
- self._tweener = self._maid:Add(DepthOfFieldTweener.new(self._depthOfField))
29
-
30
- -- TODO: Reprogram completely to be independent...
31
-
32
- -- Assume we can enable now that we've recorded values
33
- -- self._depthOfField.InFocusRadius = self._tweener:GetOriginalRadius()
34
- -- self._depthOfField.FocusDistance = self._tweener:GetOriginalDistance()
35
- -- self._depthOfField.NearIntensity = self._tweener:GetOriginalNearIntensity()
36
- -- self._depthOfField.FarIntensity = self._tweener:GetOriginalFarIntensity()
37
- -- self._depthOfField.Enabled = true
38
-
39
- self._maid:GiveTask(self._topOfStack.Changed:Connect(function(new)
40
- local maid = Maid.new()
41
-
42
- if new then
43
- self._tweener:SetDistance(new:GetDistance(), false)
44
- self._tweener:SetRadius(new:GetRadius(), false)
45
- self._tweener:SetNearIntensity(new:GetNearIntensity(), false)
46
- self._tweener:SetFarIntensity(new:GetFarIntensity(), false)
47
-
48
- maid:GiveTask(new.DistanceChanged:Connect(function(distance, doNotAnimate)
49
- self._tweener:SetDistance(distance, doNotAnimate)
50
- end))
51
- maid:GiveTask(new.RadiusChanged:Connect(function(radius, doNotAnimate)
52
- self._tweener:SetRadius(radius, doNotAnimate)
53
- end))
54
- maid:GiveTask(new.NearIntensityChanged:Connect(function(nearIntensity, doNotAnimate)
55
- self._tweener:SetNearIntensity(nearIntensity, doNotAnimate)
56
- end))
57
- maid:GiveTask(new.FarIntensityChanged:Connect(function(farIntensity, doNotAnimate)
58
- self._tweener:SetFarIntensity(farIntensity, doNotAnimate)
59
- end))
60
- else
61
- self._tweener:Reset()
62
- end
63
-
64
- self._maid._currentVisible = maid
65
- end))
66
-
67
- self._modifierStack = {}
68
- end
69
-
70
- --[=[
71
- Creates a new depth of field modifier
72
- @return DepthOfFieldModifier
73
- ]=]
74
- function DepthOfFieldService:CreateModifier()
75
- local maid = Maid.new()
76
-
77
- local modifier = DepthOfFieldModifier.new(
78
- 500,
79
- 500,
80
- 1,
81
- 1)
82
- maid:GiveTask(modifier)
83
-
84
- maid:GiveTask(function()
85
- local index = table.find(self._modifierStack, modifier)
86
- if index then
87
- table.remove(self._modifierStack, index)
88
- else
89
- warn("[DepthOfFieldService] - Somehow modifier not in stack")
90
- end
91
-
92
- self:_updateTopOfStack()
93
- end)
94
-
95
- maid:GiveTask(modifier.Removing:Connect(function()
96
- self:_removeModifier(modifier)
97
- end))
98
-
99
- self._maid[modifier] = maid
100
-
101
-
102
- table.insert(self._modifierStack, modifier)
103
- self:_updateTopOfStack()
104
-
105
- if #self._modifierStack >= 10 then
106
- warn("[DepthOfFieldService.PushEffect] - Memory leak possible in stack")
107
- end
108
-
109
- return modifier
110
- end
111
-
112
- function DepthOfFieldService:_updateTopOfStack()
113
- self._topOfStack.Value = self._modifierStack[#self._modifierStack]
114
- end
115
-
116
- function DepthOfFieldService:_removeModifier(modifier)
117
- self._maid[modifier] = nil
118
- self:_updateTopOfStack()
119
- end
120
-
121
- function DepthOfFieldService:_createDepthOfFIeld()
122
- local depthOfField = Instance.new("DepthOfFieldEffect")
123
- depthOfField.Name = "DepthOfFieldService_DepthOfField"
124
- depthOfField.FarIntensity = 0
125
- depthOfField.FocusDistance = 500
126
- depthOfField.InFocusRadius = 500
127
- depthOfField.NearIntensity = 0
128
- depthOfField.Enabled = true
129
- depthOfField.Parent = Lighting
130
- self._maid:GiveTask(depthOfField)
131
-
132
- return depthOfField
133
- end
134
-
135
- return DepthOfFieldService
@@ -1,203 +0,0 @@
1
- --[=[
2
- Tweens DepthOfField. Prefer to use [DepthOfFieldService].
3
- @class DepthOfFieldTweener
4
- ]=]
5
-
6
- local require = require(script.Parent.loader).load(script)
7
-
8
- local AccelTween = require("AccelTween")
9
- local BaseObject = require("BaseObject")
10
- local StepUtils = require("StepUtils")
11
-
12
- local DepthOfFieldTweener = setmetatable({}, BaseObject)
13
- DepthOfFieldTweener.ClassName = "DepthOfFieldTweener"
14
- DepthOfFieldTweener.__index = DepthOfFieldTweener
15
-
16
- --[=[
17
- Create a new DepthOfFieldTweener.
18
- @param depthOfField number
19
- @return DepthOfFieldTweener
20
- ]=]
21
- function DepthOfFieldTweener.new(depthOfField)
22
- local self = setmetatable(BaseObject.new(), DepthOfFieldTweener)
23
-
24
- self._depthOfField = assert(depthOfField, "No depthOfField")
25
-
26
- -- If we aren't enabled we make sure to set distance to be far
27
- self._originalDistance = self._depthOfField.Enabled and self._depthOfField.FocusDistance or 500
28
- self._originalRadius = self._depthOfField.Enabled and self._depthOfField.InFocusRadius or 500
29
- self._originalNearIntensity = self._depthOfField.NearIntensity
30
- self._originalFarIntensity = self._depthOfField.FarIntensity
31
-
32
- self._distance = AccelTween.new(10000)
33
- self._distance.t = self._originalDistance
34
- self._distance.p = self._originalDistance
35
-
36
- self._radius = AccelTween.new(10000)
37
- self._radius.t = self._originalRadius
38
- self._radius.p = self._originalRadius
39
-
40
- self._nearIntensity = AccelTween.new(30)
41
- self._nearIntensity.t = self._originalNearIntensity
42
- self._nearIntensity.p = self._originalNearIntensity
43
-
44
- self._farIntensity = AccelTween.new(30)
45
- self._farIntensity.t = self._originalFarIntensity
46
- self._farIntensity.p = self._originalFarIntensity
47
-
48
- self._maid:GiveTask(function()
49
- self._depthOfField.FocusDistance = self._originalDistance
50
- self._depthOfField.InFocusRadius = self._originalRadius
51
- self._depthOfField.NearIntensity = self._originalNearIntensity
52
- self._depthOfField.FarIntensity = self._originalFarIntensity
53
- end)
54
-
55
- self._startAnimation, self._maid._stop = StepUtils.bindToRenderStep(self._update)
56
- self:_startAnimation()
57
-
58
- return self
59
- end
60
-
61
- function DepthOfFieldTweener:SetNearIntensity(nearIntensity, doNotAnimate)
62
- assert(type(nearIntensity) == "number", "Bad nearIntensity")
63
-
64
- local target = math.clamp(nearIntensity, 0, 1)
65
- self._nearIntensity.t = target
66
- if doNotAnimate then
67
- self._nearIntensity.p = target
68
- self._nearIntensity.v = 0
69
- end
70
-
71
- self:_startAnimation()
72
- end
73
-
74
- function DepthOfFieldTweener:SetFarIntensity(farIntensity, doNotAnimate)
75
- assert(type(farIntensity) == "number", "Bad farIntensity")
76
-
77
- local target = math.clamp(farIntensity, 0, 1)
78
- self._farIntensity.t = target
79
- if doNotAnimate then
80
- self._farIntensity.p = target
81
- self._farIntensity.v = 0
82
- end
83
-
84
- self:_startAnimation()
85
- end
86
-
87
-
88
- --[=[
89
- Sets the radius and starts any animation
90
- @param radius number
91
- @param doNotAnimate boolean
92
- ]=]
93
- function DepthOfFieldTweener:SetRadius(radius, doNotAnimate)
94
- assert(type(radius) == "number", "Bad radius")
95
-
96
- local target = math.clamp(radius, 0, 500)
97
- self._radius.t = target
98
- if doNotAnimate then
99
- self._radius.p = target
100
- self._radius.v = 0
101
- end
102
-
103
- self:_startAnimation()
104
- end
105
-
106
- --[=[
107
- Gets the current radius being rendered
108
- @return number
109
- ]=]
110
- function DepthOfFieldTweener:GetRadius()
111
- return self._radius.p
112
- end
113
-
114
- --[=[
115
- Gets the current distance being set
116
- @return number
117
- ]=]
118
- function DepthOfFieldTweener:GetDistance()
119
- return self._distance.p
120
- end
121
-
122
- --[=[
123
- Sets the distance to render
124
- @param distance number
125
- @param doNotAnimate boolean
126
- ]=]
127
- function DepthOfFieldTweener:SetDistance(distance, doNotAnimate)
128
- assert(type(distance) == "number", "Bad distance")
129
-
130
- local target = math.clamp(distance, 0, 500)
131
- self._distance.t = target
132
- if doNotAnimate then
133
- self._distance.p = target
134
- self._distance.v = 0
135
- end
136
-
137
- self:_startAnimation()
138
- end
139
-
140
- --[=[
141
- Resets the depth of field to the original distance
142
- @param doNotAnimate boolean
143
- ]=]
144
- function DepthOfFieldTweener:Reset(doNotAnimate)
145
- self:ResetRadius(doNotAnimate)
146
- self:ResetDistance(doNotAnimate)
147
- self:ResetNearIntensity(doNotAnimate)
148
- self:ResetFarIntensity(doNotAnimate)
149
- end
150
-
151
- --[=[
152
- Resets the radius
153
- @param doNotAnimate boolean
154
- ]=]
155
- function DepthOfFieldTweener:ResetRadius(doNotAnimate)
156
- self:SetRadius(self._originalRadius, doNotAnimate)
157
- end
158
-
159
- --[=[
160
- Resets the distance
161
- @param doNotAnimate boolean
162
- ]=]
163
- function DepthOfFieldTweener:ResetDistance(doNotAnimate)
164
- self:SetDistance(self._originalDistance, doNotAnimate)
165
- end
166
-
167
- function DepthOfFieldTweener:ResetNearIntensity(doNotAnimate)
168
- self:SetNearIntensity(self._originalNearIntensity, doNotAnimate)
169
- end
170
-
171
- function DepthOfFieldTweener:ResetFarIntensity(doNotAnimate)
172
- self:SetFarIntensity(self._originalFarIntensity, doNotAnimate)
173
- end
174
-
175
- function DepthOfFieldTweener:GetOriginalRadius()
176
- return self._originalRadius
177
- end
178
-
179
- function DepthOfFieldTweener:GetOriginalDistance()
180
- return self._originalDistance
181
- end
182
-
183
- function DepthOfFieldTweener:GetOriginalNearIntensity()
184
- return self._originalNearIntensity
185
- end
186
-
187
- function DepthOfFieldTweener:GetOriginalFarIntensity()
188
- return self._originalFarIntensity
189
- end
190
-
191
- function DepthOfFieldTweener:_update()
192
- self._depthOfField.FocusDistance = self._distance.p
193
- self._depthOfField.InFocusRadius = self._radius.p
194
- self._depthOfField.NearIntensity = self._nearIntensity.p
195
- self._depthOfField.FarIntensity = self._farIntensity.p
196
-
197
- return self._radius.rtime > 0
198
- or self._distance.rtime > 0
199
- or self._nearIntensity.rtime > 0
200
- or self._farIntensity.rtime > 0
201
- end
202
-
203
- return DepthOfFieldTweener