@luma.gl/engine 9.0.0-beta.1 → 9.0.0-beta.10

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.
Files changed (154) hide show
  1. package/dist/animation/key-frames.js +54 -54
  2. package/dist/animation/timeline.d.ts.map +1 -1
  3. package/dist/animation/timeline.js +95 -100
  4. package/dist/animation-loop/animation-loop-template.d.ts +1 -1
  5. package/dist/animation-loop/animation-loop-template.d.ts.map +1 -1
  6. package/dist/animation-loop/animation-loop-template.js +19 -5
  7. package/dist/animation-loop/animation-loop.d.ts +2 -2
  8. package/dist/animation-loop/animation-loop.d.ts.map +1 -1
  9. package/dist/animation-loop/animation-loop.js +433 -356
  10. package/dist/animation-loop/animation-props.d.ts +2 -2
  11. package/dist/animation-loop/animation-props.d.ts.map +1 -1
  12. package/dist/animation-loop/animation-props.js +0 -1
  13. package/dist/animation-loop/make-animation-loop.d.ts +2 -2
  14. package/dist/animation-loop/make-animation-loop.d.ts.map +1 -1
  15. package/dist/animation-loop/make-animation-loop.js +28 -24
  16. package/dist/computation.d.ts +95 -0
  17. package/dist/computation.d.ts.map +1 -0
  18. package/dist/computation.js +248 -0
  19. package/dist/debug/copy-texture-to-image.d.ts.map +1 -1
  20. package/dist/debug/copy-texture-to-image.js +39 -42
  21. package/dist/debug/debug-framebuffer.d.ts.map +1 -1
  22. package/dist/debug/debug-framebuffer.js +43 -40
  23. package/dist/debug/debug-shader-layout.js +24 -25
  24. package/dist/debug/pixel-data-utils.d.ts.map +1 -1
  25. package/dist/debug/pixel-data-utils.js +34 -36
  26. package/dist/dist.dev.js +2538 -3027
  27. package/dist/dist.min.js +102 -0
  28. package/dist/geometries/cone-geometry.d.ts +1 -1
  29. package/dist/geometries/cone-geometry.d.ts.map +1 -1
  30. package/dist/geometries/cone-geometry.js +11 -17
  31. package/dist/geometries/cube-geometry.d.ts +1 -1
  32. package/dist/geometries/cube-geometry.d.ts.map +1 -1
  33. package/dist/geometries/cube-geometry.js +190 -61
  34. package/dist/geometries/cylinder-geometry.d.ts +1 -1
  35. package/dist/geometries/cylinder-geometry.d.ts.map +1 -1
  36. package/dist/geometries/cylinder-geometry.js +9 -14
  37. package/dist/geometries/ico-sphere-geometry.d.ts +1 -1
  38. package/dist/geometries/ico-sphere-geometry.d.ts.map +1 -1
  39. package/dist/geometries/ico-sphere-geometry.js +141 -160
  40. package/dist/geometries/plane-geometry.d.ts +1 -1
  41. package/dist/geometries/plane-geometry.d.ts.map +1 -1
  42. package/dist/geometries/plane-geometry.js +92 -110
  43. package/dist/geometries/sphere-geometry.d.ts +1 -1
  44. package/dist/geometries/sphere-geometry.d.ts.map +1 -1
  45. package/dist/geometries/sphere-geometry.js +76 -95
  46. package/dist/geometries/truncated-cone-geometry.d.ts +1 -1
  47. package/dist/geometries/truncated-cone-geometry.d.ts.map +1 -1
  48. package/dist/geometries/truncated-cone-geometry.js +99 -117
  49. package/dist/geometry/geometry-table.d.ts.map +1 -1
  50. package/dist/geometry/geometry-table.js +3 -1
  51. package/dist/geometry/geometry-utils.js +35 -32
  52. package/dist/geometry/geometry.d.ts.map +1 -1
  53. package/dist/geometry/geometry.js +80 -71
  54. package/dist/geometry/gpu-geometry.d.ts +1 -1
  55. package/dist/geometry/gpu-geometry.d.ts.map +1 -1
  56. package/dist/geometry/gpu-geometry.js +79 -99
  57. package/dist/geometry/gpu-table.js +41 -1
  58. package/dist/index.cjs +725 -409
  59. package/dist/index.cjs.map +7 -0
  60. package/dist/index.d.ts +43 -40
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +6 -1
  63. package/dist/lib/clip-space.d.ts +2 -2
  64. package/dist/lib/clip-space.d.ts.map +1 -1
  65. package/dist/lib/clip-space.js +28 -34
  66. package/dist/lib/pipeline-factory.d.ts +13 -14
  67. package/dist/lib/pipeline-factory.d.ts.map +1 -1
  68. package/dist/lib/pipeline-factory.js +86 -85
  69. package/dist/lib/shader-factory.d.ts +17 -0
  70. package/dist/lib/shader-factory.d.ts.map +1 -0
  71. package/dist/lib/shader-factory.js +46 -0
  72. package/dist/model/model.d.ts +59 -46
  73. package/dist/model/model.d.ts.map +1 -1
  74. package/dist/model/model.js +635 -411
  75. package/dist/scenegraph/group-node.d.ts +1 -1
  76. package/dist/scenegraph/group-node.d.ts.map +1 -1
  77. package/dist/scenegraph/group-node.js +73 -83
  78. package/dist/scenegraph/model-node.d.ts +3 -3
  79. package/dist/scenegraph/model-node.d.ts.map +1 -1
  80. package/dist/scenegraph/model-node.js +31 -24
  81. package/dist/scenegraph/scenegraph-node.d.ts.map +1 -1
  82. package/dist/scenegraph/scenegraph-node.js +136 -124
  83. package/dist/shader-inputs.d.ts.map +1 -1
  84. package/dist/shader-inputs.js +99 -58
  85. package/dist/transform/buffer-transform.d.ts +1 -1
  86. package/dist/transform/buffer-transform.d.ts.map +1 -1
  87. package/dist/transform/buffer-transform.js +65 -57
  88. package/dist/transform/texture-transform.d.ts +1 -1
  89. package/dist/transform/texture-transform.d.ts.map +1 -1
  90. package/dist/transform/texture-transform.js +109 -114
  91. package/dist.min.js +3 -271
  92. package/package.json +10 -9
  93. package/src/animation/timeline.ts +20 -20
  94. package/src/animation-loop/animation-loop-template.ts +10 -8
  95. package/src/animation-loop/animation-loop.ts +20 -10
  96. package/src/animation-loop/animation-props.ts +1 -1
  97. package/src/animation-loop/make-animation-loop.ts +17 -8
  98. package/src/computation.ts +346 -0
  99. package/src/debug/copy-texture-to-image.ts +9 -11
  100. package/src/debug/debug-framebuffer.ts +16 -3
  101. package/src/debug/debug-shader-layout.ts +1 -1
  102. package/src/debug/pixel-data-utils.ts +3 -6
  103. package/src/geometries/cube-geometry.ts +17 -13
  104. package/src/geometries/ico-sphere-geometry.ts +1 -1
  105. package/src/geometries/plane-geometry.ts +1 -1
  106. package/src/geometries/sphere-geometry.ts +1 -1
  107. package/src/geometries/truncated-cone-geometry.ts +2 -1
  108. package/src/geometry/geometry-table.ts +9 -6
  109. package/src/geometry/geometry-utils.ts +16 -0
  110. package/src/geometry/geometry.ts +9 -6
  111. package/src/geometry/gpu-geometry.ts +18 -11
  112. package/src/index.ts +4 -1
  113. package/src/lib/clip-space.ts +16 -19
  114. package/src/lib/pipeline-factory.ts +71 -64
  115. package/src/lib/shader-factory.ts +57 -0
  116. package/src/model/model.ts +255 -146
  117. package/src/scenegraph/group-node.ts +14 -10
  118. package/src/scenegraph/model-node.ts +2 -2
  119. package/src/scenegraph/scenegraph-node.ts +2 -2
  120. package/src/shader-inputs.ts +19 -12
  121. package/src/transform/buffer-transform.ts +16 -8
  122. package/src/transform/texture-transform.ts +14 -15
  123. package/dist/animation/key-frames.js.map +0 -1
  124. package/dist/animation/timeline.js.map +0 -1
  125. package/dist/animation-loop/animation-loop-template.js.map +0 -1
  126. package/dist/animation-loop/animation-loop.js.map +0 -1
  127. package/dist/animation-loop/animation-props.js.map +0 -1
  128. package/dist/animation-loop/make-animation-loop.js.map +0 -1
  129. package/dist/debug/copy-texture-to-image.js.map +0 -1
  130. package/dist/debug/debug-framebuffer.js.map +0 -1
  131. package/dist/debug/debug-shader-layout.js.map +0 -1
  132. package/dist/debug/pixel-data-utils.js.map +0 -1
  133. package/dist/geometries/cone-geometry.js.map +0 -1
  134. package/dist/geometries/cube-geometry.js.map +0 -1
  135. package/dist/geometries/cylinder-geometry.js.map +0 -1
  136. package/dist/geometries/ico-sphere-geometry.js.map +0 -1
  137. package/dist/geometries/plane-geometry.js.map +0 -1
  138. package/dist/geometries/sphere-geometry.js.map +0 -1
  139. package/dist/geometries/truncated-cone-geometry.js.map +0 -1
  140. package/dist/geometry/geometry-table.js.map +0 -1
  141. package/dist/geometry/geometry-utils.js.map +0 -1
  142. package/dist/geometry/geometry.js.map +0 -1
  143. package/dist/geometry/gpu-geometry.js.map +0 -1
  144. package/dist/geometry/gpu-table.js.map +0 -1
  145. package/dist/index.js.map +0 -1
  146. package/dist/lib/clip-space.js.map +0 -1
  147. package/dist/lib/pipeline-factory.js.map +0 -1
  148. package/dist/model/model.js.map +0 -1
  149. package/dist/scenegraph/group-node.js.map +0 -1
  150. package/dist/scenegraph/model-node.js.map +0 -1
  151. package/dist/scenegraph/scenegraph-node.js.map +0 -1
  152. package/dist/shader-inputs.js.map +0 -1
  153. package/dist/transform/buffer-transform.js.map +0 -1
  154. package/dist/transform/texture-transform.js.map +0 -1
@@ -1,365 +1,442 @@
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
1
4
  import { luma } from '@luma.gl/core';
2
5
  import { requestAnimationFrame, cancelAnimationFrame } from '@luma.gl/core';
3
6
  import { Stats } from '@probe.gl/stats';
4
7
  let statIdCounter = 0;
5
8
  const DEFAULT_ANIMATION_LOOP_PROPS = {
6
- device: null,
7
- onAddHTML: () => '',
8
- onInitialize: async () => {
9
- return null;
10
- },
11
- onRender: () => {},
12
- onFinalize: () => {},
13
- onError: error => console.error(error),
14
- stats: luma.stats.get(`animation-loop-${statIdCounter++}`),
15
- useDevicePixels: true,
16
- autoResizeViewport: false,
17
- autoResizeDrawingBuffer: false
9
+ device: null,
10
+ onAddHTML: () => '',
11
+ onInitialize: async () => {
12
+ return null;
13
+ },
14
+ onRender: () => { },
15
+ onFinalize: () => { },
16
+ onError: error => console.error(error), // eslint-disable-line no-console
17
+ stats: luma.stats.get(`animation-loop-${statIdCounter++}`),
18
+ // view parameters
19
+ useDevicePixels: true,
20
+ autoResizeViewport: false,
21
+ autoResizeDrawingBuffer: false
18
22
  };
23
+ /** Convenient animation loop */
19
24
  export class AnimationLoop {
20
- constructor(props) {
21
- this.device = null;
22
- this.canvas = null;
23
- this.props = void 0;
24
- this.animationProps = null;
25
- this.timeline = null;
26
- this.stats = void 0;
27
- this.cpuTime = void 0;
28
- this.gpuTime = void 0;
29
- this.frameRate = void 0;
30
- this.display = void 0;
31
- this.needsRedraw = 'initialized';
32
- this._initialized = false;
33
- this._running = false;
34
- this._animationFrameId = null;
35
- this._nextFramePromise = null;
36
- this._resolveNextFrame = null;
37
- this._cpuStartTime = 0;
38
- this.props = {
39
- ...DEFAULT_ANIMATION_LOOP_PROPS,
40
- ...props
41
- };
42
- props = this.props;
43
- if (!props.device) {
44
- throw new Error('No device provided');
45
- }
46
- const {
47
- useDevicePixels = true
48
- } = this.props;
49
- this.stats = props.stats || new Stats({
50
- id: 'animation-loop-stats'
51
- });
52
- this.cpuTime = this.stats.get('CPU Time');
53
- this.gpuTime = this.stats.get('GPU Time');
54
- this.frameRate = this.stats.get('Frame Rate');
55
- this.setProps({
56
- autoResizeViewport: props.autoResizeViewport,
57
- autoResizeDrawingBuffer: props.autoResizeDrawingBuffer,
58
- useDevicePixels
59
- });
60
- this.start = this.start.bind(this);
61
- this.stop = this.stop.bind(this);
62
- this._onMousemove = this._onMousemove.bind(this);
63
- this._onMouseleave = this._onMouseleave.bind(this);
64
- }
65
- destroy() {
66
- this.stop();
67
- this._setDisplay(null);
68
- }
69
- delete() {
70
- this.destroy();
71
- }
72
- setNeedsRedraw(reason) {
73
- this.needsRedraw = this.needsRedraw || reason;
74
- return this;
75
- }
76
- setProps(props) {
77
- if ('autoResizeViewport' in props) {
78
- this.props.autoResizeViewport = props.autoResizeViewport || false;
79
- }
80
- if ('autoResizeDrawingBuffer' in props) {
81
- this.props.autoResizeDrawingBuffer = props.autoResizeDrawingBuffer || false;
82
- }
83
- if ('useDevicePixels' in props) {
84
- this.props.useDevicePixels = props.useDevicePixels || false;
85
- }
86
- return this;
87
- }
88
- async start() {
89
- if (this._running) {
90
- return this;
91
- }
92
- this._running = true;
93
- try {
94
- let appContext;
95
- if (!this._initialized) {
96
- this._initialized = true;
97
- await this._initDevice();
98
- this._initialize();
99
- await this.props.onInitialize(this._getAnimationProps());
100
- }
101
- if (!this._running) {
102
- return null;
103
- }
104
- if (appContext !== false) {
105
- this._cancelAnimationFrame();
25
+ device = null;
26
+ canvas = null;
27
+ props;
28
+ animationProps = null;
29
+ timeline = null;
30
+ stats;
31
+ cpuTime;
32
+ gpuTime;
33
+ frameRate;
34
+ display;
35
+ needsRedraw = 'initialized';
36
+ _initialized = false;
37
+ _running = false;
38
+ _animationFrameId = null;
39
+ _nextFramePromise = null;
40
+ _resolveNextFrame = null;
41
+ _cpuStartTime = 0;
42
+ // _gpuTimeQuery: Query | null = null;
43
+ /*
44
+ * @param {HTMLCanvasElement} canvas - if provided, width and height will be passed to context
45
+ */
46
+ constructor(props) {
47
+ this.props = { ...DEFAULT_ANIMATION_LOOP_PROPS, ...props };
48
+ props = this.props;
49
+ if (!props.device) {
50
+ throw new Error('No device provided');
51
+ }
52
+ const { useDevicePixels = true } = this.props;
53
+ // state
54
+ this.stats = props.stats || new Stats({ id: 'animation-loop-stats' });
55
+ this.cpuTime = this.stats.get('CPU Time');
56
+ this.gpuTime = this.stats.get('GPU Time');
57
+ this.frameRate = this.stats.get('Frame Rate');
58
+ this.setProps({
59
+ autoResizeViewport: props.autoResizeViewport,
60
+ autoResizeDrawingBuffer: props.autoResizeDrawingBuffer,
61
+ useDevicePixels
62
+ });
63
+ // Bind methods
64
+ this.start = this.start.bind(this);
65
+ this.stop = this.stop.bind(this);
66
+ this._onMousemove = this._onMousemove.bind(this);
67
+ this._onMouseleave = this._onMouseleave.bind(this);
68
+ }
69
+ destroy() {
70
+ this.stop();
71
+ this._setDisplay(null);
72
+ }
73
+ /** @deprecated Use .destroy() */
74
+ delete() {
75
+ this.destroy();
76
+ }
77
+ /** Flags this animation loop as needing redraw */
78
+ setNeedsRedraw(reason) {
79
+ this.needsRedraw = this.needsRedraw || reason;
80
+ return this;
81
+ }
82
+ /** TODO - move these props to CanvasContext? */
83
+ setProps(props) {
84
+ if ('autoResizeViewport' in props) {
85
+ this.props.autoResizeViewport = props.autoResizeViewport || false;
86
+ }
87
+ if ('autoResizeDrawingBuffer' in props) {
88
+ this.props.autoResizeDrawingBuffer = props.autoResizeDrawingBuffer || false;
89
+ }
90
+ if ('useDevicePixels' in props) {
91
+ this.props.useDevicePixels = props.useDevicePixels || false;
92
+ }
93
+ return this;
94
+ }
95
+ /** Starts a render loop if not already running */
96
+ async start() {
97
+ if (this._running) {
98
+ return this;
99
+ }
100
+ this._running = true;
101
+ try {
102
+ let appContext;
103
+ if (!this._initialized) {
104
+ this._initialized = true;
105
+ // Create the WebGL context
106
+ await this._initDevice();
107
+ this._initialize();
108
+ // Note: onIntialize can return a promise (e.g. in case app needs to load resources)
109
+ await this.props.onInitialize(this._getAnimationProps());
110
+ }
111
+ // check that we haven't been stopped
112
+ if (!this._running) {
113
+ return null;
114
+ }
115
+ // Start the loop
116
+ if (appContext !== false) {
117
+ // cancel any pending renders to ensure only one loop can ever run
118
+ this._cancelAnimationFrame();
119
+ this._requestAnimationFrame();
120
+ }
121
+ return this;
122
+ }
123
+ catch (err) {
124
+ const error = err instanceof Error ? err : new Error('Unknown error');
125
+ this.props.onError(error);
126
+ // this._running = false; // TODO
127
+ throw error;
128
+ }
129
+ }
130
+ /** Stops a render loop if already running, finalizing */
131
+ stop() {
132
+ // console.debug(`Stopping ${this.constructor.name}`);
133
+ if (this._running) {
134
+ // call callback
135
+ // If stop is called immediately, we can end up in a state where props haven't been initialized...
136
+ if (this.animationProps) {
137
+ this.props.onFinalize(this.animationProps);
138
+ }
139
+ this._cancelAnimationFrame();
140
+ this._nextFramePromise = null;
141
+ this._resolveNextFrame = null;
142
+ this._running = false;
143
+ }
144
+ return this;
145
+ }
146
+ /** Explicitly draw a frame */
147
+ redraw() {
148
+ if (this.device?.isLost) {
149
+ return this;
150
+ }
151
+ this._beginFrameTimers();
152
+ this._setupFrame();
153
+ this._updateAnimationProps();
154
+ this._renderFrame(this._getAnimationProps());
155
+ // clear needsRedraw flag
156
+ this._clearNeedsRedraw();
157
+ if (this._resolveNextFrame) {
158
+ this._resolveNextFrame(this);
159
+ this._nextFramePromise = null;
160
+ this._resolveNextFrame = null;
161
+ }
162
+ this._endFrameTimers();
163
+ return this;
164
+ }
165
+ /** Add a timeline, it will be automatically updated by the animation loop. */
166
+ attachTimeline(timeline) {
167
+ this.timeline = timeline;
168
+ return this.timeline;
169
+ }
170
+ /** Remove a timeline */
171
+ detachTimeline() {
172
+ this.timeline = null;
173
+ }
174
+ /** Wait until a render completes */
175
+ waitForRender() {
176
+ this.setNeedsRedraw('waitForRender');
177
+ if (!this._nextFramePromise) {
178
+ this._nextFramePromise = new Promise(resolve => {
179
+ this._resolveNextFrame = resolve;
180
+ });
181
+ }
182
+ return this._nextFramePromise;
183
+ }
184
+ /** TODO - should use device.deviceContext */
185
+ async toDataURL() {
186
+ this.setNeedsRedraw('toDataURL');
187
+ await this.waitForRender();
188
+ if (this.canvas instanceof HTMLCanvasElement) {
189
+ return this.canvas.toDataURL();
190
+ }
191
+ throw new Error('OffscreenCanvas');
192
+ }
193
+ // PRIVATE METHODS
194
+ _initialize() {
195
+ this._startEventHandling();
196
+ // Initialize the callback data
197
+ this._initializeAnimationProps();
198
+ this._updateAnimationProps();
199
+ // Default viewport setup, in case onInitialize wants to render
200
+ this._resizeCanvasDrawingBuffer();
201
+ this._resizeViewport();
202
+ // this._gpuTimeQuery = Query.isSupported(this.gl, ['timers']) ? new Query(this.gl) : null;
203
+ }
204
+ _setDisplay(display) {
205
+ if (this.display) {
206
+ this.display.destroy();
207
+ this.display.animationLoop = null;
208
+ }
209
+ // store animation loop on the display
210
+ if (display) {
211
+ display.animationLoop = this;
212
+ }
213
+ this.display = display;
214
+ }
215
+ _requestAnimationFrame() {
216
+ if (!this._running) {
217
+ return;
218
+ }
219
+ // VR display has a separate animation frame to sync with headset
220
+ // TODO WebVR API discontinued, replaced by WebXR: https://immersive-web.github.io/webxr/
221
+ // See https://developer.mozilla.org/en-US/docs/Web/API/VRDisplay/requestAnimationFrame
222
+ // if (this.display && this.display.requestAnimationFrame) {
223
+ // this._animationFrameId = this.display.requestAnimationFrame(this._animationFrame.bind(this));
224
+ // }
225
+ this._animationFrameId = requestAnimationFrame(this._animationFrame.bind(this));
226
+ }
227
+ _cancelAnimationFrame() {
228
+ if (this._animationFrameId === null) {
229
+ return;
230
+ }
231
+ // VR display has a separate animation frame to sync with headset
232
+ // TODO WebVR API discontinued, replaced by WebXR: https://immersive-web.github.io/webxr/
233
+ // See https://developer.mozilla.org/en-US/docs/Web/API/VRDisplay/requestAnimationFrame
234
+ // if (this.display && this.display.cancelAnimationFrame) {
235
+ // this.display.cancelAnimationFrame(this._animationFrameId);
236
+ // }
237
+ cancelAnimationFrame(this._animationFrameId);
238
+ this._animationFrameId = null;
239
+ }
240
+ _animationFrame() {
241
+ if (!this._running) {
242
+ return;
243
+ }
244
+ this.redraw();
106
245
  this._requestAnimationFrame();
107
- }
108
- return this;
109
- } catch (err) {
110
- const error = err instanceof Error ? err : new Error('Unknown error');
111
- this.props.onError(error);
112
- throw error;
113
- }
114
- }
115
- stop() {
116
- if (this._running) {
117
- if (this.animationProps) {
118
- this.props.onFinalize(this.animationProps);
119
- }
120
- this._cancelAnimationFrame();
121
- this._nextFramePromise = null;
122
- this._resolveNextFrame = null;
123
- this._running = false;
124
- }
125
- return this;
126
- }
127
- redraw() {
128
- var _this$device;
129
- if ((_this$device = this.device) !== null && _this$device !== void 0 && _this$device.isLost) {
130
- return this;
131
- }
132
- this._beginFrameTimers();
133
- this._setupFrame();
134
- this._updateAnimationProps();
135
- this._renderFrame(this._getAnimationProps());
136
- this._clearNeedsRedraw();
137
- if (this._resolveNextFrame) {
138
- this._resolveNextFrame(this);
139
- this._nextFramePromise = null;
140
- this._resolveNextFrame = null;
141
- }
142
- this._endFrameTimers();
143
- return this;
144
- }
145
- attachTimeline(timeline) {
146
- this.timeline = timeline;
147
- return this.timeline;
148
- }
149
- detachTimeline() {
150
- this.timeline = null;
151
- }
152
- waitForRender() {
153
- this.setNeedsRedraw('waitForRender');
154
- if (!this._nextFramePromise) {
155
- this._nextFramePromise = new Promise(resolve => {
156
- this._resolveNextFrame = resolve;
157
- });
158
- }
159
- return this._nextFramePromise;
160
- }
161
- async toDataURL() {
162
- this.setNeedsRedraw('toDataURL');
163
- await this.waitForRender();
164
- if (this.canvas instanceof HTMLCanvasElement) {
165
- return this.canvas.toDataURL();
166
- }
167
- throw new Error('OffscreenCanvas');
168
- }
169
- _initialize() {
170
- this._startEventHandling();
171
- this._initializeAnimationProps();
172
- this._updateAnimationProps();
173
- this._resizeCanvasDrawingBuffer();
174
- this._resizeViewport();
175
- }
176
- _setDisplay(display) {
177
- if (this.display) {
178
- this.display.destroy();
179
- this.display.animationLoop = null;
180
- }
181
- if (display) {
182
- display.animationLoop = this;
183
- }
184
- this.display = display;
185
- }
186
- _requestAnimationFrame() {
187
- if (!this._running) {
188
- return;
189
- }
190
- this._animationFrameId = requestAnimationFrame(this._animationFrame.bind(this));
191
- }
192
- _cancelAnimationFrame() {
193
- if (this._animationFrameId === null) {
194
- return;
195
- }
196
- cancelAnimationFrame(this._animationFrameId);
197
- this._animationFrameId = null;
198
- }
199
- _animationFrame() {
200
- if (!this._running) {
201
- return;
202
- }
203
- this.redraw();
204
- this._requestAnimationFrame();
205
- }
206
- _renderFrame(animationProps) {
207
- if (this.display) {
208
- this.display._renderFrame(animationProps);
209
- return;
210
- }
211
- this.props.onRender(this._getAnimationProps());
212
- this.device.submit();
213
- }
214
- _clearNeedsRedraw() {
215
- this.needsRedraw = false;
216
- }
217
- _setupFrame() {
218
- this._resizeCanvasDrawingBuffer();
219
- this._resizeViewport();
220
- }
221
- _initializeAnimationProps() {
222
- var _this$device2;
223
- if (!this.device) {
224
- throw new Error('loop');
225
- }
226
- this.animationProps = {
227
- animationLoop: this,
228
- device: this.device,
229
- canvas: (_this$device2 = this.device) === null || _this$device2 === void 0 || (_this$device2 = _this$device2.canvasContext) === null || _this$device2 === void 0 ? void 0 : _this$device2.canvas,
230
- timeline: this.timeline,
231
- useDevicePixels: this.props.useDevicePixels,
232
- needsRedraw: false,
233
- width: 1,
234
- height: 1,
235
- aspect: 1,
236
- time: 0,
237
- startTime: Date.now(),
238
- engineTime: 0,
239
- tick: 0,
240
- tock: 0,
241
- _mousePosition: null
242
- };
243
- }
244
- _getAnimationProps() {
245
- if (!this.animationProps) {
246
- throw new Error('animationProps');
247
- }
248
- return this.animationProps;
249
- }
250
- _updateAnimationProps() {
251
- if (!this.animationProps) {
252
- return;
253
- }
254
- const {
255
- width,
256
- height,
257
- aspect
258
- } = this._getSizeAndAspect();
259
- if (width !== this.animationProps.width || height !== this.animationProps.height) {
260
- this.setNeedsRedraw('drawing buffer resized');
261
- }
262
- if (aspect !== this.animationProps.aspect) {
263
- this.setNeedsRedraw('drawing buffer aspect changed');
264
- }
265
- this.animationProps.width = width;
266
- this.animationProps.height = height;
267
- this.animationProps.aspect = aspect;
268
- this.animationProps.needsRedraw = this.needsRedraw;
269
- this.animationProps.engineTime = Date.now() - this.animationProps.startTime;
270
- if (this.timeline) {
271
- this.timeline.update(this.animationProps.engineTime);
272
- }
273
- this.animationProps.tick = Math.floor(this.animationProps.time / 1000 * 60);
274
- this.animationProps.tock++;
275
- this.animationProps.time = this.timeline ? this.timeline.getTime() : this.animationProps.engineTime;
276
- }
277
- async _initDevice() {
278
- var _this$device$canvasCo;
279
- this.device = await this.props.device;
280
- if (!this.device) {
281
- throw new Error('No device provided');
282
- }
283
- this.canvas = ((_this$device$canvasCo = this.device.canvasContext) === null || _this$device$canvasCo === void 0 ? void 0 : _this$device$canvasCo.canvas) || null;
284
- }
285
- _createInfoDiv() {
286
- if (this.canvas && this.props.onAddHTML) {
287
- const wrapperDiv = document.createElement('div');
288
- document.body.appendChild(wrapperDiv);
289
- wrapperDiv.style.position = 'relative';
290
- const div = document.createElement('div');
291
- div.style.position = 'absolute';
292
- div.style.left = '10px';
293
- div.style.bottom = '10px';
294
- div.style.width = '300px';
295
- div.style.background = 'white';
296
- if (this.canvas instanceof HTMLCanvasElement) {
297
- wrapperDiv.appendChild(this.canvas);
298
- }
299
- wrapperDiv.appendChild(div);
300
- const html = this.props.onAddHTML(div);
301
- if (html) {
302
- div.innerHTML = html;
303
- }
304
- }
305
- }
306
- _getSizeAndAspect() {
307
- var _this$device3, _this$device4;
308
- if (!this.device) {
309
- return {
310
- width: 1,
311
- height: 1,
312
- aspect: 1
313
- };
314
- }
315
- const [width, height] = ((_this$device3 = this.device) === null || _this$device3 === void 0 || (_this$device3 = _this$device3.canvasContext) === null || _this$device3 === void 0 ? void 0 : _this$device3.getPixelSize()) || [1, 1];
316
- let aspect = 1;
317
- const canvas = (_this$device4 = this.device) === null || _this$device4 === void 0 || (_this$device4 = _this$device4.canvasContext) === null || _this$device4 === void 0 ? void 0 : _this$device4.canvas;
318
- if (canvas && canvas.clientHeight) {
319
- aspect = canvas.clientWidth / canvas.clientHeight;
320
- } else if (width > 0 && height > 0) {
321
- aspect = width / height;
322
- }
323
- return {
324
- width,
325
- height,
326
- aspect
327
- };
328
- }
329
- _resizeViewport() {
330
- if (this.props.autoResizeViewport && this.device.gl) {
331
- this.device.gl.viewport(0, 0, this.device.gl.drawingBufferWidth, this.device.gl.drawingBufferHeight);
332
- }
333
- }
334
- _resizeCanvasDrawingBuffer() {
335
- if (this.props.autoResizeDrawingBuffer) {
336
- var _this$device5;
337
- (_this$device5 = this.device) === null || _this$device5 === void 0 || (_this$device5 = _this$device5.canvasContext) === null || _this$device5 === void 0 ? void 0 : _this$device5.resize({
338
- useDevicePixels: this.props.useDevicePixels
339
- });
340
- }
341
- }
342
- _beginFrameTimers() {
343
- this.frameRate.timeEnd();
344
- this.frameRate.timeStart();
345
- this.cpuTime.timeStart();
346
- }
347
- _endFrameTimers() {
348
- this.cpuTime.timeEnd();
349
- }
350
- _startEventHandling() {
351
- if (this.canvas) {
352
- this.canvas.addEventListener('mousemove', this._onMousemove.bind(this));
353
- this.canvas.addEventListener('mouseleave', this._onMouseleave.bind(this));
354
- }
355
- }
356
- _onMousemove(event) {
357
- if (event instanceof MouseEvent) {
358
- this._getAnimationProps()._mousePosition = [event.offsetX, event.offsetY];
359
- }
360
- }
361
- _onMouseleave(event) {
362
- this._getAnimationProps()._mousePosition = null;
363
- }
246
+ }
247
+ // Called on each frame, can be overridden to call onRender multiple times
248
+ // to support e.g. stereoscopic rendering
249
+ _renderFrame(animationProps) {
250
+ // Allow e.g. VR display to render multiple frames.
251
+ if (this.display) {
252
+ this.display._renderFrame(animationProps);
253
+ return;
254
+ }
255
+ // call callback
256
+ this.props.onRender(this._getAnimationProps());
257
+ // end callback
258
+ // Submit commands (necessary on WebGPU)
259
+ this.device.submit();
260
+ }
261
+ _clearNeedsRedraw() {
262
+ this.needsRedraw = false;
263
+ }
264
+ _setupFrame() {
265
+ this._resizeCanvasDrawingBuffer();
266
+ this._resizeViewport();
267
+ }
268
+ // Initialize the object that will be passed to app callbacks
269
+ _initializeAnimationProps() {
270
+ if (!this.device) {
271
+ throw new Error('loop');
272
+ }
273
+ this.animationProps = {
274
+ animationLoop: this,
275
+ device: this.device,
276
+ canvas: this.device?.canvasContext?.canvas,
277
+ timeline: this.timeline,
278
+ // Initial values
279
+ useDevicePixels: this.props.useDevicePixels,
280
+ needsRedraw: false,
281
+ // Placeholders
282
+ width: 1,
283
+ height: 1,
284
+ aspect: 1,
285
+ // Animation props
286
+ time: 0,
287
+ startTime: Date.now(),
288
+ engineTime: 0,
289
+ tick: 0,
290
+ tock: 0,
291
+ // Experimental
292
+ _mousePosition: null // Event props
293
+ };
294
+ }
295
+ _getAnimationProps() {
296
+ if (!this.animationProps) {
297
+ throw new Error('animationProps');
298
+ }
299
+ return this.animationProps;
300
+ }
301
+ // Update the context object that will be passed to app callbacks
302
+ _updateAnimationProps() {
303
+ if (!this.animationProps) {
304
+ return;
305
+ }
306
+ // Can this be replaced with canvas context?
307
+ const { width, height, aspect } = this._getSizeAndAspect();
308
+ if (width !== this.animationProps.width || height !== this.animationProps.height) {
309
+ this.setNeedsRedraw('drawing buffer resized');
310
+ }
311
+ if (aspect !== this.animationProps.aspect) {
312
+ this.setNeedsRedraw('drawing buffer aspect changed');
313
+ }
314
+ this.animationProps.width = width;
315
+ this.animationProps.height = height;
316
+ this.animationProps.aspect = aspect;
317
+ this.animationProps.needsRedraw = this.needsRedraw;
318
+ // Update time properties
319
+ this.animationProps.engineTime = Date.now() - this.animationProps.startTime;
320
+ if (this.timeline) {
321
+ this.timeline.update(this.animationProps.engineTime);
322
+ }
323
+ this.animationProps.tick = Math.floor((this.animationProps.time / 1000) * 60);
324
+ this.animationProps.tock++;
325
+ // For back compatibility
326
+ this.animationProps.time = this.timeline
327
+ ? this.timeline.getTime()
328
+ : this.animationProps.engineTime;
329
+ }
330
+ /** Wait for supplied device */
331
+ async _initDevice() {
332
+ this.device = await this.props.device;
333
+ if (!this.device) {
334
+ throw new Error('No device provided');
335
+ }
336
+ this.canvas = this.device.canvasContext?.canvas || null;
337
+ // this._createInfoDiv();
338
+ }
339
+ _createInfoDiv() {
340
+ if (this.canvas && this.props.onAddHTML) {
341
+ const wrapperDiv = document.createElement('div');
342
+ document.body.appendChild(wrapperDiv);
343
+ wrapperDiv.style.position = 'relative';
344
+ const div = document.createElement('div');
345
+ div.style.position = 'absolute';
346
+ div.style.left = '10px';
347
+ div.style.bottom = '10px';
348
+ div.style.width = '300px';
349
+ div.style.background = 'white';
350
+ if (this.canvas instanceof HTMLCanvasElement) {
351
+ wrapperDiv.appendChild(this.canvas);
352
+ }
353
+ wrapperDiv.appendChild(div);
354
+ const html = this.props.onAddHTML(div);
355
+ if (html) {
356
+ div.innerHTML = html;
357
+ }
358
+ }
359
+ }
360
+ _getSizeAndAspect() {
361
+ if (!this.device) {
362
+ return { width: 1, height: 1, aspect: 1 };
363
+ }
364
+ // https://webglfundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
365
+ const [width, height] = this.device?.canvasContext?.getPixelSize() || [1, 1];
366
+ // https://webglfundamentals.org/webgl/lessons/webgl-anti-patterns.html
367
+ let aspect = 1;
368
+ const canvas = this.device?.canvasContext?.canvas;
369
+ // @ts-expect-error
370
+ if (canvas && canvas.clientHeight) {
371
+ // @ts-expect-error
372
+ aspect = canvas.clientWidth / canvas.clientHeight;
373
+ }
374
+ else if (width > 0 && height > 0) {
375
+ aspect = width / height;
376
+ }
377
+ return { width, height, aspect };
378
+ }
379
+ /** Default viewport setup */
380
+ _resizeViewport() {
381
+ // TODO can we use canvas context to code this in a portable way?
382
+ // @ts-expect-error Expose on canvasContext
383
+ if (this.props.autoResizeViewport && this.device.gl) {
384
+ // @ts-expect-error Expose canvasContext
385
+ this.device.gl.viewport(0, 0,
386
+ // @ts-expect-error Expose canvasContext
387
+ this.device.gl.drawingBufferWidth,
388
+ // @ts-expect-error Expose canvasContext
389
+ this.device.gl.drawingBufferHeight);
390
+ }
391
+ }
392
+ /**
393
+ * Resize the render buffer of the canvas to match canvas client size
394
+ * Optionally multiplying with devicePixel ratio
395
+ */
396
+ _resizeCanvasDrawingBuffer() {
397
+ if (this.props.autoResizeDrawingBuffer) {
398
+ this.device?.canvasContext?.resize({ useDevicePixels: this.props.useDevicePixels });
399
+ }
400
+ }
401
+ _beginFrameTimers() {
402
+ this.frameRate.timeEnd();
403
+ this.frameRate.timeStart();
404
+ // Check if timer for last frame has completed.
405
+ // GPU timer results are never available in the same
406
+ // frame they are captured.
407
+ // if (
408
+ // this._gpuTimeQuery &&
409
+ // this._gpuTimeQuery.isResultAvailable() &&
410
+ // !this._gpuTimeQuery.isTimerDisjoint()
411
+ // ) {
412
+ // this.stats.get('GPU Time').addTime(this._gpuTimeQuery.getTimerMilliseconds());
413
+ // }
414
+ // if (this._gpuTimeQuery) {
415
+ // // GPU time query start
416
+ // this._gpuTimeQuery.beginTimeElapsedQuery();
417
+ // }
418
+ this.cpuTime.timeStart();
419
+ }
420
+ _endFrameTimers() {
421
+ this.cpuTime.timeEnd();
422
+ // if (this._gpuTimeQuery) {
423
+ // // GPU time query end. Results will be available on next frame.
424
+ // this._gpuTimeQuery.end();
425
+ // }
426
+ }
427
+ // Event handling
428
+ _startEventHandling() {
429
+ if (this.canvas) {
430
+ this.canvas.addEventListener('mousemove', this._onMousemove.bind(this));
431
+ this.canvas.addEventListener('mouseleave', this._onMouseleave.bind(this));
432
+ }
433
+ }
434
+ _onMousemove(event) {
435
+ if (event instanceof MouseEvent) {
436
+ this._getAnimationProps()._mousePosition = [event.offsetX, event.offsetY];
437
+ }
438
+ }
439
+ _onMouseleave(event) {
440
+ this._getAnimationProps()._mousePosition = null;
441
+ }
364
442
  }
365
- //# sourceMappingURL=animation-loop.js.map