@3driseai/3drise-core 1.0.7 → 1.0.9
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/dist/services/SkySystemManager.d.ts +64 -0
- package/dist/services/SkySystemManager.d.ts.map +1 -1
- package/dist/services/SkySystemManager.js +134 -16
- package/dist/three/SkySystemV.d.ts.map +1 -1
- package/dist/three/SkySystemV.js +10 -8
- package/dist/utils/galleriesUtils.js +1 -1
- package/dist/utils/materialApplicationUtils.d.ts +1 -0
- package/dist/utils/materialApplicationUtils.d.ts.map +1 -1
- package/dist/utils/materialApplicationUtils.js +2 -1
- package/package.json +1 -1
|
@@ -12,6 +12,10 @@ export interface SkyTimeState {
|
|
|
12
12
|
delta: number;
|
|
13
13
|
elapsedTime: number;
|
|
14
14
|
autoAnimate: boolean;
|
|
15
|
+
/** Time is moving on its own — autoAnimate OR real-time sync. Consumers that
|
|
16
|
+
* schedule expensive work off a sun change (a cube-camera capture) use this
|
|
17
|
+
* to stand down and let the environment probe's own timer do it once. */
|
|
18
|
+
advancing: boolean;
|
|
15
19
|
}
|
|
16
20
|
export declare class SkySystemManager {
|
|
17
21
|
private static instance;
|
|
@@ -30,12 +34,35 @@ export declare class SkySystemManager {
|
|
|
30
34
|
private syncWithRealTime;
|
|
31
35
|
private lastSyncedMinute;
|
|
32
36
|
private onTimeSync;
|
|
37
|
+
/** The last timeOfDay this manager and its host agree on. A host pushing the
|
|
38
|
+
* same value back (its own echo of `onTimeSync`) must NOT rewind the clock;
|
|
39
|
+
* only a genuinely different value is an intentional seek. */
|
|
40
|
+
private lastAppliedTimeOfDay;
|
|
41
|
+
/** Real seconds since the last write-back, so playback dispatches a few times
|
|
42
|
+
* a second instead of once per frame. */
|
|
43
|
+
private syncAccumulator;
|
|
44
|
+
/** Whoever is allowed to advance the clock. The manager is a singleton, so a
|
|
45
|
+
* second mounted SkySystem (a market preview, a second canvas) would
|
|
46
|
+
* otherwise double-tick it and stomp its settings. */
|
|
47
|
+
private driver;
|
|
33
48
|
constructor();
|
|
34
49
|
static getInstance(): SkySystemManager;
|
|
35
50
|
static resetInstance(): void;
|
|
51
|
+
/** Claim the right to drive the clock. Returns true if `token` owns it —
|
|
52
|
+
* the first claimant wins and keeps it until it releases. Everyone else
|
|
53
|
+
* reads. */
|
|
54
|
+
claimDriver(token: object): boolean;
|
|
55
|
+
releaseDriver(token: object): void;
|
|
56
|
+
hasDriver(): boolean;
|
|
36
57
|
setTimeSettings(settings: TimeSettings): void;
|
|
37
58
|
setOnTimeSync(callback: ((timeOfDay: number) => void) | null): void;
|
|
38
59
|
setTime(hours: number, minutes?: number): void;
|
|
60
|
+
/**
|
|
61
|
+
* One frame of clock. Safe to call every frame whenever time is enabled —
|
|
62
|
+
* it advances `timeInHours` only in an advancing mode, but `elapsedTime` and
|
|
63
|
+
* `delta` are what every consumer's smoothing and twinkle read, so they must
|
|
64
|
+
* keep running in manual mode too.
|
|
65
|
+
*/
|
|
39
66
|
tick(delta: number): void;
|
|
40
67
|
getState(): SkyTimeState;
|
|
41
68
|
getStateByTime(timeOfDay: number): SkyTimeState;
|
|
@@ -45,10 +72,47 @@ export declare class SkySystemManager {
|
|
|
45
72
|
getTimeInHours(): number;
|
|
46
73
|
isEnabled(): boolean;
|
|
47
74
|
isAutoAnimating(): boolean;
|
|
75
|
+
isAdvancing(): boolean;
|
|
48
76
|
private advanceTime;
|
|
77
|
+
/**
|
|
78
|
+
* Push the animating time back to the host a few times a second.
|
|
79
|
+
*
|
|
80
|
+
* Without this the manager was the only thing that knew what time it was:
|
|
81
|
+
* the slider sat frozen at the last value the user set, and that stale value
|
|
82
|
+
* was what got saved with the project. Throttled in REAL seconds, not
|
|
83
|
+
* simulated ones, so the dispatch rate does not scale with timescale.
|
|
84
|
+
*
|
|
85
|
+
* `lastAppliedTimeOfDay` is set to exactly what is published, so the value
|
|
86
|
+
* coming back through the host's state is recognised as our own echo by
|
|
87
|
+
* `setTimeSettings` and does not rewind the clock.
|
|
88
|
+
*/
|
|
89
|
+
private publishTime;
|
|
49
90
|
private syncToRealTime;
|
|
50
91
|
private recalculateState;
|
|
92
|
+
/**
|
|
93
|
+
* Sun elevation and azimuth for a given hour.
|
|
94
|
+
*
|
|
95
|
+
* Both curves are continuous across the whole 24 h, which they were not:
|
|
96
|
+
*
|
|
97
|
+
* - Azimuth used to be `180 + 180 * normalizedTime` with `normalizedTime`
|
|
98
|
+
* restarting at 0 at both SUNRISE and SUNSET, so the sun teleported to
|
|
99
|
+
* the opposite horizon twice a cycle. The night arc now CONTINUES the
|
|
100
|
+
* day arc (360 -> 540, which is 180 again mod 360), so the sun keeps
|
|
101
|
+
* travelling the same way round and meets the next dawn exactly where
|
|
102
|
+
* the day arc starts.
|
|
103
|
+
*
|
|
104
|
+
* - Night elevation used to be the day curve halved, and the day curve is
|
|
105
|
+
* positive in the middle — so the sun rose again after sunset and stood
|
|
106
|
+
* +18 degrees up at 02:00. Anything reading the direction rather than
|
|
107
|
+
* `isDaytime` (the clouds and the ocean, through useCloudSun) was lit as
|
|
108
|
+
* if it were afternoon in the middle of the night. The night arc is now
|
|
109
|
+
* its own curve, below the horizon for all of it, meeting the day curve
|
|
110
|
+
* at -5 degrees at both ends.
|
|
111
|
+
*/
|
|
51
112
|
private calculateTimeState;
|
|
113
|
+
/** Scene azimuth in [-180, 180). Normalised into range rather than left to
|
|
114
|
+
* JS's signed `%`, so the night arc's 360-540 degrees maps onto the same
|
|
115
|
+
* directions the day arc's 180-360 does instead of running off the end. */
|
|
52
116
|
private transformAzimuth;
|
|
53
117
|
private updateSunDirection;
|
|
54
118
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SkySystemManager.d.ts","sourceRoot":"","sources":["../../src/services/SkySystemManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE3E,MAAM,WAAW,YAAY;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,cAAc,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,KAAK,CAAC,OAAO,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"SkySystemManager.d.ts","sourceRoot":"","sources":["../../src/services/SkySystemManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE3E,MAAM,WAAW,YAAY;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,cAAc,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,KAAK,CAAC,OAAO,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB;;8EAE0E;IAC1E,SAAS,EAAE,OAAO,CAAC;CACtB;AAaD,qBAAa,gBAAgB;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAQ;IAC/B,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,cAAc,CAAO;IAC7B,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,UAAU,CAAO;IACzB,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,gBAAgB,CAAM;IAC9B,OAAO,CAAC,UAAU,CAAQ;IAC1B;;mEAE+D;IAC/D,OAAO,CAAC,oBAAoB,CAAuB;IACnD;8CAC0C;IAC1C,OAAO,CAAC,eAAe,CAAK;IAC5B;;2DAEuD;IACvD,OAAO,CAAC,MAAM,CAAuB;;IAIrC,MAAM,CAAC,WAAW,IAAI,gBAAgB;IAMtC,MAAM,CAAC,aAAa,IAAI,IAAI;IAG5B;;iBAEa;IACb,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAMnC,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKlC,SAAS,IAAI,OAAO;IAGpB,eAAe,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI;IA6B7C,aAAa,CAAC,QAAQ,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI;IAGnE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,MAAU,GAAG,IAAI;IAQjD;;;;;OAKG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAczB,QAAQ,IAAI,YAAY;IAexB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY;IAoB/C,OAAO,IAAI,IAAI;IAKf,YAAY,IAAI,cAAc;IAG9B,eAAe,IAAI,KAAK,CAAC,OAAO;IAGhC,cAAc,IAAI,MAAM;IAGxB,SAAS,IAAI,OAAO;IAGpB,eAAe,IAAI,OAAO;IAG1B,WAAW,IAAI,OAAO;IAGtB,OAAO,CAAC,WAAW;IAWnB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,gBAAgB;IASxB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,kBAAkB;IA2B1B;;gFAE4E;IAC5E,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,kBAAkB;CAQ7B;AACD,eAAO,MAAM,gBAAgB,EAAE,gBAAiD,CAAC;AACjF,eAAe,gBAAgB,CAAC"}
|
|
@@ -2,6 +2,13 @@ import * as THREE from 'three';
|
|
|
2
2
|
const SUNRISE = 6;
|
|
3
3
|
const SUNSET = 21;
|
|
4
4
|
const MAX_ELEVATION = 42;
|
|
5
|
+
/** Where the day arc meets the horizon at SUNRISE and SUNSET, and where the
|
|
6
|
+
* night arc has to meet it back for the curve to be continuous. */
|
|
7
|
+
const HORIZON_OFFSET = 5;
|
|
8
|
+
/** How far below the horizon the sun gets at the middle of the night. */
|
|
9
|
+
const MAX_NIGHT_DEPTH = 21;
|
|
10
|
+
/** Real seconds between playback write-backs to the host. */
|
|
11
|
+
const SYNC_INTERVAL_SECONDS = 0.25;
|
|
5
12
|
export class SkySystemManager {
|
|
6
13
|
static instance = null;
|
|
7
14
|
timeInHours = 12;
|
|
@@ -19,6 +26,17 @@ export class SkySystemManager {
|
|
|
19
26
|
syncWithRealTime = false;
|
|
20
27
|
lastSyncedMinute = -1;
|
|
21
28
|
onTimeSync = null;
|
|
29
|
+
/** The last timeOfDay this manager and its host agree on. A host pushing the
|
|
30
|
+
* same value back (its own echo of `onTimeSync`) must NOT rewind the clock;
|
|
31
|
+
* only a genuinely different value is an intentional seek. */
|
|
32
|
+
lastAppliedTimeOfDay = null;
|
|
33
|
+
/** Real seconds since the last write-back, so playback dispatches a few times
|
|
34
|
+
* a second instead of once per frame. */
|
|
35
|
+
syncAccumulator = 0;
|
|
36
|
+
/** Whoever is allowed to advance the clock. The manager is a singleton, so a
|
|
37
|
+
* second mounted SkySystem (a market preview, a second canvas) would
|
|
38
|
+
* otherwise double-tick it and stomp its settings. */
|
|
39
|
+
driver = null;
|
|
22
40
|
constructor() {
|
|
23
41
|
this.updateSunDirection();
|
|
24
42
|
}
|
|
@@ -31,16 +49,49 @@ export class SkySystemManager {
|
|
|
31
49
|
static resetInstance() {
|
|
32
50
|
SkySystemManager.instance = null;
|
|
33
51
|
}
|
|
52
|
+
/** Claim the right to drive the clock. Returns true if `token` owns it —
|
|
53
|
+
* the first claimant wins and keeps it until it releases. Everyone else
|
|
54
|
+
* reads. */
|
|
55
|
+
claimDriver(token) {
|
|
56
|
+
if (this.driver === null) {
|
|
57
|
+
this.driver = token;
|
|
58
|
+
}
|
|
59
|
+
return this.driver === token;
|
|
60
|
+
}
|
|
61
|
+
releaseDriver(token) {
|
|
62
|
+
if (this.driver === token) {
|
|
63
|
+
this.driver = null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
hasDriver() {
|
|
67
|
+
return this.driver !== null;
|
|
68
|
+
}
|
|
34
69
|
setTimeSettings(settings) {
|
|
35
70
|
this.enabled = settings.enabled;
|
|
36
71
|
this.timescale = settings.timescale;
|
|
37
72
|
this.autoAnimate = settings.autoAnimate;
|
|
38
73
|
this.syncWithRealTime = settings.syncWithRealTime;
|
|
39
74
|
if (this.syncWithRealTime) {
|
|
75
|
+
// Wall clock wins outright; the settings' timeOfDay is only ever an
|
|
76
|
+
// echo of what this manager last published. Adopting it here is what
|
|
77
|
+
// used to put one frame of yesterday's sun on screen.
|
|
78
|
+
this.lastAppliedTimeOfDay = settings.timeOfDay;
|
|
40
79
|
this.syncToRealTime();
|
|
80
|
+
this.recalculateState();
|
|
81
|
+
return;
|
|
41
82
|
}
|
|
42
|
-
|
|
83
|
+
// A running clock is ahead of whatever the host last stored, so a plain
|
|
84
|
+
// `timeInHours !== settings.timeOfDay` test is true on every settings
|
|
85
|
+
// object and would rewind playback on any unrelated change (a speed
|
|
86
|
+
// button, a project load, a re-render). Only a timeOfDay that differs
|
|
87
|
+
// from the last one we agreed on is a deliberate seek.
|
|
88
|
+
const seeked = this.lastAppliedTimeOfDay === null
|
|
89
|
+
|| Math.abs(settings.timeOfDay - this.lastAppliedTimeOfDay) > 0.001;
|
|
90
|
+
if (seeked) {
|
|
91
|
+
this.lastAppliedTimeOfDay = settings.timeOfDay;
|
|
43
92
|
this.timeInHours = settings.timeOfDay;
|
|
93
|
+
this.delta = 0;
|
|
94
|
+
this.syncAccumulator = 0;
|
|
44
95
|
this.recalculateState();
|
|
45
96
|
}
|
|
46
97
|
}
|
|
@@ -49,20 +100,29 @@ export class SkySystemManager {
|
|
|
49
100
|
}
|
|
50
101
|
setTime(hours, minutes = 0) {
|
|
51
102
|
this.timeInHours = hours + minutes / 60;
|
|
103
|
+
this.lastAppliedTimeOfDay = this.timeInHours;
|
|
104
|
+
// An explicit seek is a cut, not a movement: zero delta tells every
|
|
105
|
+
// consumer's smoothing lerp to snap rather than glide from the old sun.
|
|
106
|
+
this.delta = 0;
|
|
52
107
|
this.recalculateState();
|
|
53
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* One frame of clock. Safe to call every frame whenever time is enabled —
|
|
111
|
+
* it advances `timeInHours` only in an advancing mode, but `elapsedTime` and
|
|
112
|
+
* `delta` are what every consumer's smoothing and twinkle read, so they must
|
|
113
|
+
* keep running in manual mode too.
|
|
114
|
+
*/
|
|
54
115
|
tick(delta) {
|
|
55
116
|
if (!this.enabled)
|
|
56
117
|
return;
|
|
57
|
-
|
|
58
|
-
// this.delta = delta;
|
|
118
|
+
this.delta = delta;
|
|
59
119
|
this.elapsedTime += delta;
|
|
60
120
|
if (this.syncWithRealTime) {
|
|
61
121
|
this.syncToRealTime();
|
|
62
122
|
}
|
|
63
123
|
else if (this.autoAnimate && this.timescale > 0) {
|
|
64
|
-
// console.log(delta)
|
|
65
124
|
this.advanceTime(delta);
|
|
125
|
+
this.publishTime(delta);
|
|
66
126
|
}
|
|
67
127
|
this.recalculateState();
|
|
68
128
|
}
|
|
@@ -78,6 +138,7 @@ export class SkySystemManager {
|
|
|
78
138
|
delta: this.delta,
|
|
79
139
|
elapsedTime: this.elapsedTime,
|
|
80
140
|
autoAnimate: this.autoAnimate,
|
|
141
|
+
advancing: this.isAdvancing(),
|
|
81
142
|
};
|
|
82
143
|
}
|
|
83
144
|
getStateByTime(timeOfDay) {
|
|
@@ -97,6 +158,7 @@ export class SkySystemManager {
|
|
|
97
158
|
delta: 0,
|
|
98
159
|
elapsedTime: this.elapsedTime,
|
|
99
160
|
autoAnimate: this.autoAnimate,
|
|
161
|
+
advancing: this.isAdvancing(),
|
|
100
162
|
};
|
|
101
163
|
}
|
|
102
164
|
getTime() {
|
|
@@ -119,16 +181,45 @@ export class SkySystemManager {
|
|
|
119
181
|
isAutoAnimating() {
|
|
120
182
|
return this.autoAnimate;
|
|
121
183
|
}
|
|
184
|
+
isAdvancing() {
|
|
185
|
+
return this.enabled && (this.syncWithRealTime || (this.autoAnimate && this.timescale > 0));
|
|
186
|
+
}
|
|
122
187
|
advanceTime(delta) {
|
|
123
188
|
const hoursPerSecond = this.timescale / 60;
|
|
124
189
|
this.timeInHours += hoursPerSecond * delta;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
190
|
+
// Modulo rather than a single +/- 24: at a high timescale one long frame
|
|
191
|
+
// (a backgrounded tab handing back a multi-second delta) can carry the
|
|
192
|
+
// clock past 24 by more than a day, and a single subtraction leaves it
|
|
193
|
+
// out of range for every consumer that indexes by hour.
|
|
194
|
+
if (this.timeInHours >= 24 || this.timeInHours < 0) {
|
|
195
|
+
this.timeInHours = ((this.timeInHours % 24) + 24) % 24;
|
|
130
196
|
}
|
|
131
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Push the animating time back to the host a few times a second.
|
|
200
|
+
*
|
|
201
|
+
* Without this the manager was the only thing that knew what time it was:
|
|
202
|
+
* the slider sat frozen at the last value the user set, and that stale value
|
|
203
|
+
* was what got saved with the project. Throttled in REAL seconds, not
|
|
204
|
+
* simulated ones, so the dispatch rate does not scale with timescale.
|
|
205
|
+
*
|
|
206
|
+
* `lastAppliedTimeOfDay` is set to exactly what is published, so the value
|
|
207
|
+
* coming back through the host's state is recognised as our own echo by
|
|
208
|
+
* `setTimeSettings` and does not rewind the clock.
|
|
209
|
+
*/
|
|
210
|
+
publishTime(delta) {
|
|
211
|
+
if (!this.onTimeSync)
|
|
212
|
+
return;
|
|
213
|
+
this.syncAccumulator += delta;
|
|
214
|
+
if (this.syncAccumulator < SYNC_INTERVAL_SECONDS)
|
|
215
|
+
return;
|
|
216
|
+
this.syncAccumulator = 0;
|
|
217
|
+
const rounded = Math.round(this.timeInHours * 100) / 100;
|
|
218
|
+
if (rounded === this.lastAppliedTimeOfDay)
|
|
219
|
+
return;
|
|
220
|
+
this.lastAppliedTimeOfDay = rounded;
|
|
221
|
+
this.onTimeSync(rounded);
|
|
222
|
+
}
|
|
132
223
|
syncToRealTime() {
|
|
133
224
|
const now = new Date();
|
|
134
225
|
const currentMinute = now.getHours() * 60 + now.getMinutes();
|
|
@@ -136,7 +227,9 @@ export class SkySystemManager {
|
|
|
136
227
|
now.getHours() + now.getMinutes() / 60 + now.getSeconds() / 3600;
|
|
137
228
|
if (currentMinute !== this.lastSyncedMinute) {
|
|
138
229
|
this.lastSyncedMinute = currentMinute;
|
|
139
|
-
|
|
230
|
+
const rounded = Math.round(this.timeInHours * 100) / 100;
|
|
231
|
+
this.lastAppliedTimeOfDay = rounded;
|
|
232
|
+
this.onTimeSync?.(rounded);
|
|
140
233
|
}
|
|
141
234
|
}
|
|
142
235
|
recalculateState() {
|
|
@@ -148,20 +241,41 @@ export class SkySystemManager {
|
|
|
148
241
|
this.sunAzimuth = state.sunAzimuth;
|
|
149
242
|
this.updateSunDirection();
|
|
150
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Sun elevation and azimuth for a given hour.
|
|
246
|
+
*
|
|
247
|
+
* Both curves are continuous across the whole 24 h, which they were not:
|
|
248
|
+
*
|
|
249
|
+
* - Azimuth used to be `180 + 180 * normalizedTime` with `normalizedTime`
|
|
250
|
+
* restarting at 0 at both SUNRISE and SUNSET, so the sun teleported to
|
|
251
|
+
* the opposite horizon twice a cycle. The night arc now CONTINUES the
|
|
252
|
+
* day arc (360 -> 540, which is 180 again mod 360), so the sun keeps
|
|
253
|
+
* travelling the same way round and meets the next dawn exactly where
|
|
254
|
+
* the day arc starts.
|
|
255
|
+
*
|
|
256
|
+
* - Night elevation used to be the day curve halved, and the day curve is
|
|
257
|
+
* positive in the middle — so the sun rose again after sunset and stood
|
|
258
|
+
* +18 degrees up at 02:00. Anything reading the direction rather than
|
|
259
|
+
* `isDaytime` (the clouds and the ocean, through useCloudSun) was lit as
|
|
260
|
+
* if it were afternoon in the middle of the night. The night arc is now
|
|
261
|
+
* its own curve, below the horizon for all of it, meeting the day curve
|
|
262
|
+
* at -5 degrees at both ends.
|
|
263
|
+
*/
|
|
151
264
|
calculateTimeState(timeInHours) {
|
|
152
265
|
const isDaytime = timeInHours >= SUNRISE && timeInHours <= SUNSET;
|
|
153
266
|
let normalizedTime;
|
|
267
|
+
let sunElevation;
|
|
268
|
+
let sunAzimuth;
|
|
154
269
|
if (isDaytime) {
|
|
155
270
|
normalizedTime = (timeInHours - SUNRISE) / (SUNSET - SUNRISE);
|
|
271
|
+
sunElevation = Math.cos(Math.PI * (normalizedTime - 0.5)) * MAX_ELEVATION - HORIZON_OFFSET;
|
|
272
|
+
sunAzimuth = 180 + 180 * normalizedTime;
|
|
156
273
|
}
|
|
157
274
|
else {
|
|
158
275
|
const nightHour = timeInHours >= SUNSET ? timeInHours : timeInHours + 24;
|
|
159
276
|
normalizedTime = (nightHour - SUNSET) / (24 - SUNSET + SUNRISE);
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const sunAzimuth = 180 + 180 * normalizedTime;
|
|
163
|
-
if (!isDaytime) {
|
|
164
|
-
sunElevation *= 0.5;
|
|
277
|
+
sunElevation = -HORIZON_OFFSET - Math.cos(Math.PI * (normalizedTime - 0.5)) * MAX_NIGHT_DEPTH;
|
|
278
|
+
sunAzimuth = 360 + 180 * normalizedTime;
|
|
165
279
|
}
|
|
166
280
|
let timeOfDay = 'Nighttime';
|
|
167
281
|
if (isDaytime) {
|
|
@@ -174,8 +288,12 @@ export class SkySystemManager {
|
|
|
174
288
|
}
|
|
175
289
|
return { normalizedTime, isDaytime, timeOfDay, sunElevation, sunAzimuth };
|
|
176
290
|
}
|
|
291
|
+
/** Scene azimuth in [-180, 180). Normalised into range rather than left to
|
|
292
|
+
* JS's signed `%`, so the night arc's 360-540 degrees maps onto the same
|
|
293
|
+
* directions the day arc's 180-360 does instead of running off the end. */
|
|
177
294
|
transformAzimuth(rawAzimuth) {
|
|
178
|
-
|
|
295
|
+
const wrapped = (((270 - rawAzimuth) % 360) + 360) % 360;
|
|
296
|
+
return wrapped - 180;
|
|
179
297
|
}
|
|
180
298
|
updateSunDirection() {
|
|
181
299
|
const el = THREE.MathUtils.degToRad(this.sunElevation);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SkySystemV.d.ts","sourceRoot":"","sources":["../../src/three/SkySystemV.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAClH,MAAM,WAAW,iBAAiB;IAC9B,YAAY,EAAE,YAAY,CAAC;IAC3B,MAAM,EAAE,KAAK,CAAC,iBAAiB,GAAG,KAAK,CAAC,kBAAkB,CAAC;IAC3D,KAAK,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,qBAAqB,CAAC,EAAE,WAAW,CAAC;IACpC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACxE;AAGD,qBAAa,UAAU;IACnB,OAAO,CAAC,MAAM,CAAC;IACf,OAAO,CAAC,MAAM,CAAC;IACf,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,IAAI,CAAQ;IACpB,OAAO,CAAC,iBAAiB,CAAQ;gBACrB,MAAM,EAAE,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,iBAAiB;IAwC9D,OAAO,CAAC,QAAQ;IAQhB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,GAAE,MAAc,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"SkySystemV.d.ts","sourceRoot":"","sources":["../../src/three/SkySystemV.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAClH,MAAM,WAAW,iBAAiB;IAC9B,YAAY,EAAE,YAAY,CAAC;IAC3B,MAAM,EAAE,KAAK,CAAC,iBAAiB,GAAG,KAAK,CAAC,kBAAkB,CAAC;IAC3D,KAAK,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,qBAAqB,CAAC,EAAE,WAAW,CAAC;IACpC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACxE;AAGD,qBAAa,UAAU;IACnB,OAAO,CAAC,MAAM,CAAC;IACf,OAAO,CAAC,MAAM,CAAC;IACf,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,IAAI,CAAQ;IACpB,OAAO,CAAC,iBAAiB,CAAQ;gBACrB,MAAM,EAAE,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,iBAAiB;IAwC9D,OAAO,CAAC,QAAQ;IAQhB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,GAAE,MAAc,GAAG,IAAI;IAoBrD,YAAY,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAa9C,OAAO,CAAC,eAAe,GAAE,OAAc,GAAG,IAAI;CAMjD"}
|
package/dist/three/SkySystemV.js
CHANGED
|
@@ -67,15 +67,17 @@ export class SkySystemV {
|
|
|
67
67
|
const { timeSettings, onTimeOfDayChange } = this.config;
|
|
68
68
|
if (!timeSettings.enabled)
|
|
69
69
|
return;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
70
|
+
// Ticked whenever time is enabled, not only while it animates: the
|
|
71
|
+
// manager's `delta` and `elapsedTime` come out of here and drive every
|
|
72
|
+
// consumer's colour smoothing. Advancing the hour is the manager's own
|
|
73
|
+
// decision. Kept in step with the R3F SkySystem deliberately.
|
|
74
|
+
skySystemManager.tick(delta);
|
|
75
|
+
const current = skySystemManager.getTimeOfDay();
|
|
76
|
+
if (this.previousTimeOfDay !== null && this.previousTimeOfDay !== current) {
|
|
77
|
+
const state = skySystemManager.getState();
|
|
78
|
+
onTimeOfDayChange?.(current, state.elapsedTime);
|
|
78
79
|
}
|
|
80
|
+
this.previousTimeOfDay = current;
|
|
79
81
|
this.skybox?.update(_elapsed, delta);
|
|
80
82
|
this.lensflare?.update(_elapsed, delta);
|
|
81
83
|
this.sunLight?.update(_elapsed, delta);
|
|
@@ -90,7 +90,7 @@ export const updateCurrentLayoutSettings = (currentSettings, newSettings) => {
|
|
|
90
90
|
? currentSettings.imageUrls
|
|
91
91
|
: createNumberOfDefaultImages(galleryState.numImages ?? 20), galleryState);
|
|
92
92
|
const layoutSettings = getLayoutSettings(currentSettings.layout, galleryState);
|
|
93
|
-
return { frames, layoutSettings };
|
|
93
|
+
return { frames, layoutSettings: galleryState };
|
|
94
94
|
};
|
|
95
95
|
// =============================================================================
|
|
96
96
|
// DEFAULT STATE
|
|
@@ -3,6 +3,7 @@ import { MaterialInstance } from '../services/MaterialRegistry';
|
|
|
3
3
|
export declare const MATERIAL_INSTANCE_KEY = "_3driseMaterialInstance";
|
|
4
4
|
export declare const EDGES_GROUP_KEY = "__isEdgesGroup";
|
|
5
5
|
export declare const GEREATIVE_EFFECTS_KEY = "__isGenerativeEffectsGroup";
|
|
6
|
+
export declare const IMAGE_KEY = "__isImage";
|
|
6
7
|
/**
|
|
7
8
|
* Set by a renderer that builds and owns its object's material itself — today the
|
|
8
9
|
* points renderers, while shader effects are active. Two writers on one
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"materialApplicationUtils.d.ts","sourceRoot":"","sources":["../../src/utils/materialApplicationUtils.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAChD,OAA4B,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAKrF,eAAO,MAAM,qBAAqB,4BAA4B,CAAC;AAE/D,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAChD,eAAO,MAAM,qBAAqB,+BAA+B,CAAC;AAClE;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,sBAAsB,CAAC;AAC3D;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,GAAG,IAAI,CAOxH;AAKD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,gBAAgB,CAI/G;AACD;;;GAGG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAQlH;AACD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI,CAsCrF;AACD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE1F;AACD;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEpD;AACD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGtD;AACD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAElE;AACD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAElE;AACD;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,GAAG,CAAC,MAAM,CAGtC,CAAC;AACH;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG;IAC3D,OAAO,EAAE,KAAK,CAAC;QACX,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IACH,MAAM,EAAE,KAAK,CAAC;QACV,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IACH,QAAQ,EAAE,KAAK,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,GAAG,CAAC;KACd,CAAC,CAAC;IACH,QAAQ,EAAE,KAAK,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,OAAO,CAAC;KAClB,CAAC,CAAC;IACH,KAAK,EAAE,KAAK,CAAC;QACT,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,GAAG,CAAC;KACd,CAAC,CAAC;CACN,CA6BJ"}
|
|
1
|
+
{"version":3,"file":"materialApplicationUtils.d.ts","sourceRoot":"","sources":["../../src/utils/materialApplicationUtils.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAChD,OAA4B,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAKrF,eAAO,MAAM,qBAAqB,4BAA4B,CAAC;AAE/D,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAChD,eAAO,MAAM,qBAAqB,+BAA+B,CAAC;AAClE,eAAO,MAAM,SAAS,cAAc,CAAC;AACrC;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,sBAAsB,CAAC;AAC3D;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,GAAG,IAAI,CAOxH;AAKD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,gBAAgB,CAI/G;AACD;;;GAGG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAQlH;AACD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI,CAsCrF;AACD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE1F;AACD;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEpD;AACD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGtD;AACD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAElE;AACD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAElE;AACD;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,GAAG,CAAC,MAAM,CAGtC,CAAC;AACH;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG;IAC3D,OAAO,EAAE,KAAK,CAAC;QACX,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IACH,MAAM,EAAE,KAAK,CAAC;QACV,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IACH,QAAQ,EAAE,KAAK,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,GAAG,CAAC;KACd,CAAC,CAAC;IACH,QAAQ,EAAE,KAAK,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,OAAO,CAAC;KAClB,CAAC,CAAC;IACH,KAAK,EAAE,KAAK,CAAC;QACT,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,GAAG,CAAC;KACd,CAAC,CAAC;CACN,CA6BJ"}
|
|
@@ -18,6 +18,7 @@ export const MATERIAL_INSTANCE_KEY = '_3driseMaterialInstance';
|
|
|
18
18
|
// Key placed on edges group userData to prevent material bleed-through
|
|
19
19
|
export const EDGES_GROUP_KEY = '__isEdgesGroup';
|
|
20
20
|
export const GEREATIVE_EFFECTS_KEY = '__isGenerativeEffectsGroup';
|
|
21
|
+
export const IMAGE_KEY = '__isImage';
|
|
21
22
|
/**
|
|
22
23
|
* Set by a renderer that builds and owns its object's material itself — today the
|
|
23
24
|
* points renderers, while shader effects are active. Two writers on one
|
|
@@ -33,7 +34,7 @@ export const SELF_OWNED_MATERIAL_KEY = '__ownsItsMaterial';
|
|
|
33
34
|
* apply its own material via useMaterialApplication).
|
|
34
35
|
*/
|
|
35
36
|
export function traverseSkippingEdgesAndEffects(obj, isRoot, callback) {
|
|
36
|
-
if (!isRoot && (obj.userData[EDGES_GROUP_KEY] || obj.userData[GEREATIVE_EFFECTS_KEY] || obj.userData[SELF_OWNED_MATERIAL_KEY]))
|
|
37
|
+
if (!isRoot && (obj.userData[EDGES_GROUP_KEY] || obj.userData[GEREATIVE_EFFECTS_KEY] || obj.userData[SELF_OWNED_MATERIAL_KEY] || obj.userData[IMAGE_KEY]))
|
|
37
38
|
return;
|
|
38
39
|
callback(obj);
|
|
39
40
|
for (const child of obj.children) {
|