@quenty/sunpositionutils 2.4.2 → 2.4.3-canary.684.24818543952.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 +17 -0
- package/package.json +10 -2
- package/src/Shared/GlobalLightSourceType.lua +15 -0
- package/src/Shared/SunPositionUtils.lua +381 -10
- package/src/node_modules.project.json +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
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
|
+
## [2.4.3-canary.684.24818543952.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/sunpositionutils@2.4.2...@quenty/sunpositionutils@2.4.3-canary.684.24818543952.0) (2026-04-23)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* Fix dependencies list ([776b270](https://github.com/Quenty/NevermoreEngine/commit/776b2700b97d877b70c487d2f47092bacc373e7a))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* Additional improvments ([44896ef](https://github.com/Quenty/NevermoreEngine/commit/44896efe8dd9506ad6002bc41f816b9b2b482ebc))
|
|
17
|
+
* Upgrade dependent libraries with new functionality ([22e8c9e](https://github.com/Quenty/NevermoreEngine/commit/22e8c9e3c3dd7288dd264f00c98852653b40a402))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
6
23
|
## [2.4.2](https://github.com/Quenty/NevermoreEngine/compare/@quenty/sunpositionutils@2.4.1...@quenty/sunpositionutils@2.4.2) (2026-01-12)
|
|
7
24
|
|
|
8
25
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/sunpositionutils",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.3-canary.684.24818543952.0",
|
|
4
4
|
"description": "Utility to position the sun and to retrieve sun information specific to Roblox.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Roblox",
|
|
@@ -30,5 +30,13 @@
|
|
|
30
30
|
"publishConfig": {
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
|
-
"
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@quenty/color3utils": "11.33.1-canary.684.24818543952.0",
|
|
35
|
+
"@quenty/colorsequenceutils": "7.10.1-canary.684.24818543952.0",
|
|
36
|
+
"@quenty/enums": "1.2.1-canary.684.24818543952.0",
|
|
37
|
+
"@quenty/instanceutils": "13.28.1-canary.684.24818543952.0",
|
|
38
|
+
"@quenty/loader": "10.10.1-canary.684.24818543952.0",
|
|
39
|
+
"@quenty/rx": "13.27.1-canary.684.24818543952.0"
|
|
40
|
+
},
|
|
41
|
+
"gitHead": "67b6385a1ead1ab1b137158b3e0c179e5f5b19ac"
|
|
34
42
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[=[
|
|
3
|
+
@class GlobalLightSourceType
|
|
4
|
+
]=]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local SimpleEnum = require("SimpleEnum")
|
|
9
|
+
|
|
10
|
+
export type GlobalLightSourceType = "sun" | "moon"
|
|
11
|
+
|
|
12
|
+
return SimpleEnum.new({
|
|
13
|
+
SUN = "sun" :: "sun",
|
|
14
|
+
MOON = "moon" :: "moon",
|
|
15
|
+
})
|
|
@@ -6,8 +6,22 @@
|
|
|
6
6
|
the computation to compute where Roblox is rendering the sun given the lighting
|
|
7
7
|
properties set.
|
|
8
8
|
|
|
9
|
+
https://raw.githubusercontent.com/iryl1/RBX_DOCUMENTATIONS/c0db57fedf0540db2cd9eb65404ad258c3e21295/lighting.md
|
|
10
|
+
|
|
9
11
|
@class SunPositionUtils
|
|
10
12
|
]=]
|
|
13
|
+
|
|
14
|
+
local require = require(script.Parent.loader).load(script)
|
|
15
|
+
|
|
16
|
+
local Lighting = game:GetService("Lighting")
|
|
17
|
+
|
|
18
|
+
local Color3Utils = require("Color3Utils")
|
|
19
|
+
local ColorSequenceUtils = require("ColorSequenceUtils")
|
|
20
|
+
local GlobalLightSourceType = require("GlobalLightSourceType")
|
|
21
|
+
local Observable = require("Observable")
|
|
22
|
+
local Rx = require("Rx")
|
|
23
|
+
local RxInstanceUtils = require("RxInstanceUtils")
|
|
24
|
+
|
|
11
25
|
local SunPositionUtils = {}
|
|
12
26
|
|
|
13
27
|
local EARTH_TILT = 23.5
|
|
@@ -66,15 +80,14 @@ function SunPositionUtils.getDirection(azimuthRad: number, altitudeRad: number,
|
|
|
66
80
|
return cframe:VectorToWorldSpace(north or NORTH)
|
|
67
81
|
end
|
|
68
82
|
|
|
69
|
-
|
|
70
|
-
|
|
83
|
+
export type SunPositionData = {
|
|
84
|
+
sunPosition: Vector3,
|
|
85
|
+
moonPosition: Vector3,
|
|
86
|
+
clockTime: number,
|
|
87
|
+
geoLatitude: number,
|
|
88
|
+
}
|
|
71
89
|
|
|
72
|
-
|
|
73
|
-
@param geoLatitude number
|
|
74
|
-
@return Vector3 -- Sun position
|
|
75
|
-
@return Vector3 -- Moon position
|
|
76
|
-
]=]
|
|
77
|
-
function SunPositionUtils.getSunPosition(clockTime: number, geoLatitude: number): (Vector3, Vector3)
|
|
90
|
+
function SunPositionUtils.getSunPositionData(clockTime: number, geoLatitude: number): SunPositionData
|
|
78
91
|
local seconds = clockTime * 60 * 60
|
|
79
92
|
local DAY = 24 * 60 * 60
|
|
80
93
|
local YEAR = 365.2564 * DAY
|
|
@@ -91,9 +104,367 @@ function SunPositionUtils.getSunPosition(clockTime: number, geoLatitude: number)
|
|
|
91
104
|
local sunOffset = -EARTHTILT * math.cos(math.pi * (dayOfYearOffset - HALFYEAR) / HALFYEAR) - latRad
|
|
92
105
|
|
|
93
106
|
local trueSunPosition = CFrame.fromAxisAngle(ZAXIS:Cross(sunPosition), sunOffset) * sunPosition
|
|
94
|
-
local trueMoonPosition = CFrame.fromAxisAngle(ZAXIS:Cross(moonPosition), sunOffset)
|
|
107
|
+
local trueMoonPosition = CFrame.fromAxisAngle(ZAXIS:Cross(moonPosition), sunOffset)
|
|
108
|
+
* moonPosition
|
|
109
|
+
* Vector3.new(1, -1, 1)
|
|
110
|
+
return {
|
|
111
|
+
sunPosition = trueSunPosition,
|
|
112
|
+
moonPosition = trueMoonPosition,
|
|
113
|
+
clockTime = clockTime,
|
|
114
|
+
geoLatitude = geoLatitude,
|
|
115
|
+
}
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
--[=[
|
|
119
|
+
Estimates the sun position given the clockTime and geographical latitude.
|
|
120
|
+
|
|
121
|
+
@param clockTime number
|
|
122
|
+
@param geoLatitude number
|
|
123
|
+
@return Vector3 -- Sun position
|
|
124
|
+
@return Vector3 -- Moon position
|
|
125
|
+
]=]
|
|
126
|
+
function SunPositionUtils.getSunPosition(clockTime: number, geoLatitude: number): (Vector3, Vector3)
|
|
127
|
+
local data = SunPositionUtils.getSunPositionData(clockTime, geoLatitude)
|
|
128
|
+
return data.sunPosition, data.moonPosition
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
--[=[
|
|
132
|
+
A single float in `[0.1, 1.0]` that scales overall scene brightness. peaks at noon (`1.0`), bottoms out at midnight (`0.1`). derived directly from the sun's y position, which is itself just a function of clock time.
|
|
133
|
+
]=]
|
|
134
|
+
function SunPositionUtils.getLightSourceBrightness(sunPositionData: SunPositionData): number
|
|
135
|
+
local lightSourceType = SunPositionUtils.getLightSourceType(sunPositionData)
|
|
136
|
+
if lightSourceType == GlobalLightSourceType.SUN then
|
|
137
|
+
return math.clamp(math.map(sunPositionData.sunPosition.Y, -1, 1, 0.1, 1), 0.1, 1)
|
|
138
|
+
elseif lightSourceType == GlobalLightSourceType.MOON then
|
|
139
|
+
return math.clamp(math.map(sunPositionData.moonPosition.Y, -1, 1, 0.1, 0.5), 0.1, 1)
|
|
140
|
+
else
|
|
141
|
+
error("Unknown light source type: " .. tostring(lightSourceType))
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
--[=[
|
|
146
|
+
Whether the sun or moon is the active light source. This is what actually tints shadows and direct illumination
|
|
147
|
+
|
|
148
|
+
@param sunPositionData SunPositionData
|
|
149
|
+
@return GlobalLightSourceType
|
|
150
|
+
]=]
|
|
151
|
+
function SunPositionUtils.getLightSourceType(
|
|
152
|
+
sunPositionData: SunPositionData
|
|
153
|
+
): GlobalLightSourceType.GlobalLightSourceType
|
|
154
|
+
if sunPositionData.sunPosition.Y > -0.3 then
|
|
155
|
+
return GlobalLightSourceType.SUN
|
|
156
|
+
else
|
|
157
|
+
return GlobalLightSourceType.MOON
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
--[=[
|
|
162
|
+
Gets the direction of the active light source (sun or moon). this is what actually tints shadows and direct illumination
|
|
163
|
+
|
|
164
|
+
@param sunPositionData SunPositionData
|
|
165
|
+
@return Vector3
|
|
166
|
+
]=]
|
|
167
|
+
function SunPositionUtils.getLightSourceDirection(sunPositionData: SunPositionData): Vector3
|
|
168
|
+
local lightSourceType = SunPositionUtils.getLightSourceType(sunPositionData)
|
|
169
|
+
if lightSourceType == GlobalLightSourceType.SUN then
|
|
170
|
+
return sunPositionData.sunPosition
|
|
171
|
+
elseif lightSourceType == GlobalLightSourceType.MOON then
|
|
172
|
+
return sunPositionData.moonPosition
|
|
173
|
+
else
|
|
174
|
+
error("Unknown light source type: " .. tostring(lightSourceType))
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
local MIDNIGHT = 0
|
|
179
|
+
local SUNRISE = 6
|
|
180
|
+
local SUNSET = 18
|
|
181
|
+
local HOUR = 1
|
|
182
|
+
local DAY = 24
|
|
183
|
+
local SUN_RISE_AND_SET_TIME = HOUR / 2
|
|
184
|
+
|
|
185
|
+
local LIGHT_COLOR_SEQUENCE = ColorSequenceUtils.fromUnscaledTimesAndColors({
|
|
186
|
+
MIDNIGHT,
|
|
187
|
+
SUNRISE - HOUR,
|
|
188
|
+
SUNRISE,
|
|
189
|
+
SUNRISE + SUN_RISE_AND_SET_TIME / 4,
|
|
190
|
+
SUNRISE + SUN_RISE_AND_SET_TIME,
|
|
191
|
+
SUNSET - SUN_RISE_AND_SET_TIME,
|
|
192
|
+
SUNSET - SUN_RISE_AND_SET_TIME / 2,
|
|
193
|
+
SUNSET,
|
|
194
|
+
SUNSET + HOUR / 2,
|
|
195
|
+
DAY,
|
|
196
|
+
}, {
|
|
197
|
+
Color3.new(0, 0, 0), -- midnight — black
|
|
198
|
+
Color3.new(0, 0, 0), -- pre-dawn — black
|
|
199
|
+
Color3.new(0.07, 0.07, 0.1), -- late pre-dawn — very dim blue
|
|
200
|
+
Color3.new(0.2, 0.15, 0.01), -- near sunrise — warm amber
|
|
201
|
+
Color3.new(0.2, 0.15, 0.01), -- sunrise — warm amber
|
|
202
|
+
Color3.new(1, 1, 1), -- daytime — full white
|
|
203
|
+
Color3.new(1, 1, 1), -- daytime
|
|
204
|
+
Color3.new(0.4, 0.2, 0.05), -- sunset — orange-brown
|
|
205
|
+
Color3.new(0, 0, 0), -- post-sunset — black
|
|
206
|
+
Color3.new(0, 0, 0), -- midnight
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
local MIN_LIGHT_COLOR = Color3.new(0.35, 0.35, 0.35)
|
|
210
|
+
|
|
211
|
+
function SunPositionUtils._toScaledClockTime(unscaledTime: number): number
|
|
212
|
+
return math.map(unscaledTime, 0, 24, 0, 1) % 1
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
--[=[
|
|
216
|
+
The color of the active light source (sun or moon). this is what actually tints shadows and direct illumination
|
|
217
|
+
]=]
|
|
218
|
+
function SunPositionUtils.getLightColor(clockTime: number): Color3
|
|
219
|
+
local color = ColorSequenceUtils.getColor(LIGHT_COLOR_SEQUENCE, SunPositionUtils._toScaledClockTime(clockTime))
|
|
220
|
+
|
|
221
|
+
return Color3Utils.max(color, MIN_LIGHT_COLOR)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
local DIFFUSE_AMBIENT_SEQUENCE = ColorSequenceUtils.fromUnscaledTimesAndColors({
|
|
225
|
+
MIDNIGHT,
|
|
226
|
+
SUNRISE - HOUR,
|
|
227
|
+
SUNRISE,
|
|
228
|
+
SUNRISE + SUN_RISE_AND_SET_TIME / 2,
|
|
229
|
+
SUNRISE + SUN_RISE_AND_SET_TIME,
|
|
230
|
+
SUNSET - SUN_RISE_AND_SET_TIME,
|
|
231
|
+
SUNSET - SUN_RISE_AND_SET_TIME / 2,
|
|
232
|
+
SUNSET,
|
|
233
|
+
SUNSET + HOUR / 2,
|
|
234
|
+
DAY,
|
|
235
|
+
}, {
|
|
236
|
+
Color3.new(0.1, 0.1, 0.17), -- midnight — dim blue-grey
|
|
237
|
+
Color3.new(0.05, 0.06, 0.07), -- pre-dawn — near black
|
|
238
|
+
Color3.new(0.08, 0.08, 0.01), -- sunrise start — slight warm tint
|
|
239
|
+
Color3.new(0.75, 0.75, 0.75), -- mid-sunrise — bright (white * 0.75)
|
|
240
|
+
Color3.new(0.75, 0.75, 0.75), -- daytime — full bright
|
|
241
|
+
Color3.new(0.35, 0.35, 0.35), -- late afternoon — dimming
|
|
242
|
+
Color3.new(0.5, 0.2, 0.2), -- pre-sunset — warm pink
|
|
243
|
+
Color3.new(0.05, 0.05, 0.1), -- sunset — dark blue
|
|
244
|
+
Color3.new(0.06, 0.06, 0.07), -- post-sunset — near black
|
|
245
|
+
Color3.new(0.1, 0.1, 0.17), -- back to midnight
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
--[=[
|
|
249
|
+
The diffuse component of ambient — softer than direct light but still contributes to scene brightness. peaks at full white during the day.
|
|
250
|
+
]=]
|
|
251
|
+
function SunPositionUtils.getDiffuseAmbient(clockTime: number): Color3
|
|
252
|
+
return ColorSequenceUtils.getColor(DIFFUSE_AMBIENT_SEQUENCE, SunPositionUtils._toScaledClockTime(clockTime))
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
--[=[
|
|
256
|
+
The sky's contribution to ambient — this is what gives the sky its color cast on surfaces. ramps up around sunrise, stays on through sunset, then cuts out.
|
|
257
|
+
|
|
258
|
+
This is the bottom color
|
|
259
|
+
]=]
|
|
260
|
+
function SunPositionUtils.getSkyAmbientBottom(clockTime: number, environmentDiffuseScale: number): Color3
|
|
261
|
+
local midnight = 0.2 * environmentDiffuseScale
|
|
262
|
+
local dark = midnight / 2
|
|
263
|
+
|
|
264
|
+
local SKY_AMBIENT_SEQUENCE = ColorSequenceUtils.fromUnscaledTimesAndColors({
|
|
265
|
+
MIDNIGHT,
|
|
266
|
+
SUNRISE - 2 * HOUR,
|
|
267
|
+
SUNRISE - HOUR,
|
|
268
|
+
SUNRISE - HOUR / 2,
|
|
269
|
+
SUNRISE,
|
|
270
|
+
SUNRISE + SUN_RISE_AND_SET_TIME,
|
|
271
|
+
SUNSET - SUN_RISE_AND_SET_TIME,
|
|
272
|
+
SUNSET,
|
|
273
|
+
SUNSET + HOUR / 3,
|
|
274
|
+
DAY,
|
|
275
|
+
}, {
|
|
276
|
+
Color3.new(midnight, midnight, midnight), -- midnight — black
|
|
277
|
+
Color3.new(dark, dark, dark), -- pre-dawn — black
|
|
278
|
+
Color3.new(0.07 + dark, 0.07 + dark, 0.1 + dark), -- late pre-dawn — very dim blue
|
|
279
|
+
Color3.new(0.2 + dark, 0.15 + dark, 0.01 + dark), -- near sunrise — warm amber
|
|
280
|
+
Color3.new(0.2 + dark, 0.15 + dark, 0.01 + dark), -- sunrise — warm amber
|
|
281
|
+
Color3.new(1, 1, 1), -- daytime — full white
|
|
282
|
+
Color3.new(1, 1, 1), -- daytime
|
|
283
|
+
Color3.new(0.4 + dark, 0.2 + dark, 0.05 + dark), -- sunset — orange-brown
|
|
284
|
+
Color3.new(dark, dark, dark), -- post-sunset — black
|
|
285
|
+
Color3.new(midnight, midnight, midnight), -- midnight
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
return ColorSequenceUtils.getColor(SKY_AMBIENT_SEQUENCE, SunPositionUtils._toScaledClockTime(clockTime))
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
--[=[
|
|
292
|
+
a second sky ambient spline — this one has a wider ramp and contributes additional sky color. the difference between `skyAmbientBottom` and `skyAmbientTop` gives you the gradient from horizon to zenith.
|
|
293
|
+
|
|
294
|
+
This is the top color
|
|
295
|
+
]=]
|
|
296
|
+
function SunPositionUtils.getSkyAmbientTop(clockTime: number, environmentDiffuseScale: number): Color3
|
|
297
|
+
local moon = 0.2 * environmentDiffuseScale
|
|
298
|
+
local dark = moon / 2
|
|
299
|
+
|
|
300
|
+
local SKY_AMBIENT_2_SEQUENCE = ColorSequenceUtils.fromUnscaledTimesAndColors({
|
|
301
|
+
MIDNIGHT,
|
|
302
|
+
SUNRISE - 3 * HOUR,
|
|
303
|
+
SUNRISE - 2 * HOUR,
|
|
304
|
+
SUNRISE - HOUR / 2,
|
|
305
|
+
SUNRISE,
|
|
306
|
+
SUNRISE + SUN_RISE_AND_SET_TIME,
|
|
307
|
+
SUNSET - SUN_RISE_AND_SET_TIME,
|
|
308
|
+
SUNSET,
|
|
309
|
+
SUNSET + HOUR / 3,
|
|
310
|
+
SUNSET + 2 * HOUR,
|
|
311
|
+
SUNSET + 3 * HOUR,
|
|
312
|
+
DAY,
|
|
313
|
+
}, {
|
|
314
|
+
Color3.new(dark, dark, dark), -- midnight
|
|
315
|
+
Color3.new(moon, moon, moon), -- early pre-dawn — near black
|
|
316
|
+
Color3.new(0.3 * 0.7, 0.3 * 0.7, 0.4 * 0.7), -- pre-dawn — dim blue-grey
|
|
317
|
+
Color3.new(0.4, 0.3, 0.3), -- near sunrise — muted warm
|
|
318
|
+
Color3.new(0.3, 0.2, 0.3), -- sunrise — muted purple-pink
|
|
319
|
+
Color3.new(1, 1, 1), -- daytime
|
|
320
|
+
Color3.new(1, 1, 1), -- daytime
|
|
321
|
+
Color3.new(0.4, 0.3, 0.2), -- sunset — warm brown
|
|
322
|
+
Color3.new(0.3, 0.2, 0.3), -- post-sunset — muted purple
|
|
323
|
+
Color3.new(0.3, 0.2, 0.3), -- late post-sunset
|
|
324
|
+
Color3.new(moon, moon, moon), -- night fade
|
|
325
|
+
Color3.new(dark, dark, dark), -- midnight
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
return ColorSequenceUtils.getColor(SKY_AMBIENT_2_SEQUENCE, SunPositionUtils._toScaledClockTime(clockTime))
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
--[=[
|
|
332
|
+
Gets the sun image brightness
|
|
333
|
+
]=]
|
|
334
|
+
function SunPositionUtils.getSunImageBrightness(sunPositionData: SunPositionData): number
|
|
335
|
+
local brightness = 0.8 * math.clamp((sunPositionData.sunPosition.Y + 0.1) * 10, 0, 1)
|
|
336
|
+
|
|
337
|
+
return brightness
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
--[=[
|
|
341
|
+
Gets the moon image brightness
|
|
342
|
+
]=]
|
|
343
|
+
function SunPositionUtils.getMoonImageBrightness(
|
|
344
|
+
sunPositionData: SunPositionData,
|
|
345
|
+
environmentDiffuseScale: number
|
|
346
|
+
): number
|
|
347
|
+
local skyAmbientBottom = SunPositionUtils.getSkyAmbientBottom(sunPositionData.clockTime, environmentDiffuseScale)
|
|
348
|
+
local brightness = math.min((1 - skyAmbientBottom.R) + 0.1, 1)
|
|
349
|
+
|
|
350
|
+
return brightness
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
--[=[
|
|
354
|
+
Gets the sky ambient color sequence, which is a sunset glow during certain times of the day.
|
|
355
|
+
]=]
|
|
356
|
+
function SunPositionUtils.getSkyboxGradient(clockTime: number, environmentDiffuseScale: number): ColorSequence
|
|
357
|
+
local bottom = SunPositionUtils.getSkyAmbientBottom(clockTime, environmentDiffuseScale)
|
|
358
|
+
local top = SunPositionUtils.getSkyAmbientTop(clockTime, environmentDiffuseScale)
|
|
359
|
+
|
|
360
|
+
return ColorSequence.new(bottom, top)
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
export type ViewportLighting = {
|
|
364
|
+
brightness: number, -- Should be applied to a surface gui
|
|
365
|
+
ambient: Color3,
|
|
366
|
+
lightColor: Color3,
|
|
367
|
+
lightDirection: Vector3,
|
|
368
|
+
backgroundColor3: Color3, -- Approximated sky color
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export type ViewportLightingArgs = {
|
|
372
|
+
ambient: Color3,
|
|
373
|
+
outdoorAmbient: Color3,
|
|
374
|
+
clockTime: number,
|
|
375
|
+
brightness: number,
|
|
376
|
+
geoLatitude: number,
|
|
377
|
+
environmentDiffuseScale: number,
|
|
378
|
+
colorShiftTop: Color3,
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
--[=[
|
|
382
|
+
Compute viewport lighting to match Roblox lighting
|
|
383
|
+
]=]
|
|
384
|
+
function SunPositionUtils.computeViewportLighting(args: ViewportLightingArgs): ViewportLighting
|
|
385
|
+
local sunPositionData = SunPositionUtils.getSunPositionData(args.clockTime, args.geoLatitude)
|
|
386
|
+
local diffuseAmbient = SunPositionUtils.getDiffuseAmbient(sunPositionData.clockTime)
|
|
387
|
+
|
|
388
|
+
local ambient = SunPositionUtils._computeViewportAmbient(args, sunPositionData)
|
|
389
|
+
local lightColor = SunPositionUtils._computeViewportLightColor(args, ambient, sunPositionData)
|
|
390
|
+
|
|
391
|
+
return {
|
|
392
|
+
backgroundColor3 = diffuseAmbient,
|
|
393
|
+
brightness = args.brightness,
|
|
394
|
+
ambient = ambient,
|
|
395
|
+
lightColor = lightColor,
|
|
396
|
+
lightDirection = -SunPositionUtils.getLightSourceDirection(sunPositionData),
|
|
397
|
+
}
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
function SunPositionUtils._computeViewportLightColor(
|
|
401
|
+
args: ViewportLightingArgs,
|
|
402
|
+
computedAmbient: Color3,
|
|
403
|
+
sunPositionData: SunPositionData
|
|
404
|
+
): Color3
|
|
405
|
+
local lightColor = SunPositionUtils.getLightColor(sunPositionData.clockTime)
|
|
406
|
+
local lightSourceBrightness = SunPositionUtils.getLightSourceBrightness(sunPositionData)
|
|
407
|
+
local ambientStrength = Color3Utils.length(computedAmbient)
|
|
408
|
+
local scaledLightColor = Color3Utils.multiplyScalar(lightColor, 0.9 * 0.5 * (args.brightness + ambientStrength))
|
|
409
|
+
|
|
410
|
+
local shiftedLight = SunPositionUtils._computeLightShift(
|
|
411
|
+
scaledLightColor,
|
|
412
|
+
Color3Utils.multiplyScalar(args.colorShiftTop, lightSourceBrightness),
|
|
413
|
+
args.environmentDiffuseScale
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
return Color3Utils.multiplyScalar(shiftedLight, lightSourceBrightness)
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
function SunPositionUtils._computeLightShift(lightColor: Color3, colorShiftTop: Color3, diffuseScale: number): Color3
|
|
420
|
+
local lightStrength = Color3Utils.length(lightColor)
|
|
421
|
+
|
|
422
|
+
local shiftBase = (colorShiftTop.R + colorShiftTop.G + colorShiftTop.B) / 3
|
|
423
|
+
local shift = Color3.new(colorShiftTop.R - shiftBase, colorShiftTop.G - shiftBase, colorShiftTop.B - shiftBase)
|
|
424
|
+
local baseline = Color3Utils.add(lightColor, Color3Utils.multiplyScalar(shift, lightStrength))
|
|
425
|
+
|
|
426
|
+
local shiftStrength = Color3Utils.length(shift)
|
|
427
|
+
local diffuseLight = lightColor:Lerp(Color3Utils.multiplyScalar(colorShiftTop, lightStrength), shiftStrength)
|
|
428
|
+
|
|
429
|
+
return baseline:Lerp(diffuseLight, diffuseScale)
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
function SunPositionUtils._computeViewportAmbient(args: ViewportLightingArgs, sunPositionData: SunPositionData): Color3
|
|
433
|
+
local diffuseAmbient = SunPositionUtils.getDiffuseAmbient(sunPositionData.clockTime)
|
|
434
|
+
local diffuseTotal = Color3Utils.multiplyScalar(diffuseAmbient, args.environmentDiffuseScale)
|
|
435
|
+
|
|
436
|
+
local blendedAmbient = Color3.new(
|
|
437
|
+
math.max(args.ambient.R, args.outdoorAmbient.R),
|
|
438
|
+
math.max(args.ambient.G, args.outdoorAmbient.G),
|
|
439
|
+
math.max(args.ambient.B, args.outdoorAmbient.B)
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
local ambient = Color3Utils.add(blendedAmbient, diffuseTotal)
|
|
443
|
+
return ambient
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
function SunPositionUtils.captureViewportLightingArgs(): ViewportLightingArgs
|
|
447
|
+
return {
|
|
448
|
+
ambient = Lighting.Ambient,
|
|
449
|
+
outdoorAmbient = Lighting.OutdoorAmbient,
|
|
450
|
+
clockTime = Lighting.ClockTime,
|
|
451
|
+
geoLatitude = Lighting.GeographicLatitude,
|
|
452
|
+
brightness = Lighting.Brightness,
|
|
453
|
+
environmentDiffuseScale = Lighting.EnvironmentDiffuseScale,
|
|
454
|
+
colorShiftTop = Lighting.ColorShift_Top,
|
|
455
|
+
}
|
|
456
|
+
end
|
|
95
457
|
|
|
96
|
-
|
|
458
|
+
function SunPositionUtils.observeViewportLightingArgs(): Observable.Observable<ViewportLightingArgs>
|
|
459
|
+
return Rx.combineLatest({
|
|
460
|
+
ambient = RxInstanceUtils.observeProperty(Lighting, "Ambient"),
|
|
461
|
+
outdoorAmbient = RxInstanceUtils.observeProperty(Lighting, "OutdoorAmbient"),
|
|
462
|
+
clockTime = RxInstanceUtils.observeProperty(Lighting, "ClockTime"),
|
|
463
|
+
geoLatitude = RxInstanceUtils.observeProperty(Lighting, "GeographicLatitude"),
|
|
464
|
+
brightness = RxInstanceUtils.observeProperty(Lighting, "Brightness"),
|
|
465
|
+
environmentDiffuseScale = RxInstanceUtils.observeProperty(Lighting, "EnvironmentDiffuseScale"),
|
|
466
|
+
colorShiftTop = RxInstanceUtils.observeProperty(Lighting, "ColorShift_Top"),
|
|
467
|
+
}) :: any
|
|
97
468
|
end
|
|
98
469
|
|
|
99
470
|
return SunPositionUtils
|