@skewedaspect/sage 0.3.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.
Files changed (86) hide show
  1. package/LICENSE +21 -0
  2. package/Readme.md +53 -0
  3. package/dist/classes/bindings/toggle.d.ts +122 -0
  4. package/dist/classes/bindings/trigger.d.ts +79 -0
  5. package/dist/classes/bindings/value.d.ts +104 -0
  6. package/dist/classes/entity.d.ts +83 -0
  7. package/dist/classes/eventBus.d.ts +94 -0
  8. package/dist/classes/gameEngine.d.ts +57 -0
  9. package/dist/classes/input/gamepad.d.ts +94 -0
  10. package/dist/classes/input/keyboard.d.ts +66 -0
  11. package/dist/classes/input/mouse.d.ts +80 -0
  12. package/dist/classes/input/readers/gamepad.d.ts +77 -0
  13. package/dist/classes/input/readers/keyboard.d.ts +60 -0
  14. package/dist/classes/input/readers/mouse.d.ts +45 -0
  15. package/dist/classes/loggers/consoleBackend.d.ts +29 -0
  16. package/dist/classes/loggers/nullBackend.d.ts +14 -0
  17. package/dist/engines/scene.d.ts +11 -0
  18. package/dist/interfaces/action.d.ts +20 -0
  19. package/dist/interfaces/binding.d.ts +144 -0
  20. package/dist/interfaces/entity.d.ts +9 -0
  21. package/dist/interfaces/game.d.ts +26 -0
  22. package/dist/interfaces/input.d.ts +181 -0
  23. package/dist/interfaces/logger.d.ts +88 -0
  24. package/dist/managers/binding.d.ts +185 -0
  25. package/dist/managers/entity.d.ts +70 -0
  26. package/dist/managers/game.d.ts +20 -0
  27. package/dist/managers/input.d.ts +56 -0
  28. package/dist/managers/level.d.ts +55 -0
  29. package/dist/sage.d.ts +20 -0
  30. package/dist/sage.es.js +2208 -0
  31. package/dist/sage.es.js.map +1 -0
  32. package/dist/sage.umd.js +2 -0
  33. package/dist/sage.umd.js.map +1 -0
  34. package/dist/utils/capabilities.d.ts +2 -0
  35. package/dist/utils/graphics.d.ts +10 -0
  36. package/dist/utils/logger.d.ts +66 -0
  37. package/dist/utils/physics.d.ts +2 -0
  38. package/dist/utils/version.d.ts +5 -0
  39. package/docs/architecture.md +129 -0
  40. package/docs/behaviors.md +706 -0
  41. package/docs/binding_system.md +820 -0
  42. package/docs/design/input.md +86 -0
  43. package/docs/entity_system.md +538 -0
  44. package/docs/eventbus.md +225 -0
  45. package/docs/getting_started.md +264 -0
  46. package/docs/images/sage_logo.png +0 -0
  47. package/docs/images/sage_logo_shape.png +0 -0
  48. package/docs/overview.md +38 -0
  49. package/docs/physics_system.md +686 -0
  50. package/docs/scene_system.md +513 -0
  51. package/package.json +69 -0
  52. package/src/classes/bindings/toggle.ts +261 -0
  53. package/src/classes/bindings/trigger.ts +211 -0
  54. package/src/classes/bindings/value.ts +227 -0
  55. package/src/classes/entity.ts +256 -0
  56. package/src/classes/eventBus.ts +259 -0
  57. package/src/classes/gameEngine.ts +125 -0
  58. package/src/classes/input/gamepad.ts +388 -0
  59. package/src/classes/input/keyboard.ts +189 -0
  60. package/src/classes/input/mouse.ts +276 -0
  61. package/src/classes/input/readers/gamepad.ts +179 -0
  62. package/src/classes/input/readers/keyboard.ts +123 -0
  63. package/src/classes/input/readers/mouse.ts +133 -0
  64. package/src/classes/loggers/consoleBackend.ts +135 -0
  65. package/src/classes/loggers/nullBackend.ts +51 -0
  66. package/src/engines/scene.ts +112 -0
  67. package/src/images/sage_logo.svg +172 -0
  68. package/src/images/sage_logo_shape.svg +146 -0
  69. package/src/interfaces/action.ts +30 -0
  70. package/src/interfaces/binding.ts +191 -0
  71. package/src/interfaces/entity.ts +21 -0
  72. package/src/interfaces/game.ts +44 -0
  73. package/src/interfaces/input.ts +221 -0
  74. package/src/interfaces/logger.ts +118 -0
  75. package/src/managers/binding.ts +729 -0
  76. package/src/managers/entity.ts +252 -0
  77. package/src/managers/game.ts +111 -0
  78. package/src/managers/input.ts +233 -0
  79. package/src/managers/level.ts +261 -0
  80. package/src/sage.ts +119 -0
  81. package/src/types/global.d.ts +11 -0
  82. package/src/utils/capabilities.ts +16 -0
  83. package/src/utils/graphics.ts +148 -0
  84. package/src/utils/logger.ts +225 -0
  85. package/src/utils/physics.ts +16 -0
  86. package/src/utils/version.ts +11 -0
@@ -0,0 +1,261 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Level Manager
3
+ //----------------------------------------------------------------------------------------------------------------------
4
+
5
+ import {
6
+ AbstractMesh,
7
+ AssetContainer,
8
+ Engine,
9
+ ISceneLoaderProgressEvent,
10
+ LoadAssetContainerAsync,
11
+ LoadSceneAsync,
12
+ Scene,
13
+ Sound,
14
+ Tags,
15
+ } from '@babylonjs/core';
16
+ import '@babylonjs/loaders'; // Ensure loaders (glTF, .babylon, etc.) are available
17
+
18
+ // Managers
19
+ import { GameEntityManager } from './entity.ts';
20
+
21
+ //----------------------------------------------------------------------------------------------------------------------
22
+
23
+ export interface LevelLoadOptions
24
+ {
25
+ rootUrl : string;
26
+ filename : string;
27
+ useAssetContainer ?: boolean;
28
+ showLoadingUI ?: boolean;
29
+ onProgress ?: (percent : number) => void;
30
+ onSuccess ?: (scene : Scene) => void;
31
+ onError ?: (message : string, exception ?: any) => void;
32
+ }
33
+
34
+ /**
35
+ /**
36
+ * Manages level loading and streaming using BabylonJS SceneLoader and AssetContainer.
37
+ * Also includes tagging for auto-detection (e.g. "Door", "Trigger", "Enemy"),
38
+ * and automatically spawns entities via an entity manager if an 'entityType' is found.
39
+ *
40
+ * Additionally, this version checks for audio metadata (e.g. sound files) and automatically
41
+ * creates BabylonJS Sound objects when found.
42
+ */
43
+ export class LevelManager
44
+ {
45
+ private engine : Engine;
46
+ private currentScene : Scene | null = null;
47
+ private activeContainer : AssetContainer | null = null;
48
+ private entityManager : GameEntityManager;
49
+
50
+ constructor(engine : Engine, entityManager : GameEntityManager)
51
+ {
52
+ this.engine = engine;
53
+ this.entityManager = entityManager;
54
+ }
55
+
56
+ /**
57
+ * Loads a level, either as a full scene or into a container.
58
+ * @param options - Specifies rootUrl, filename, and optional callbacks.
59
+ */
60
+ public async loadLevel(options : LevelLoadOptions) : Promise<void>
61
+ {
62
+ if(options.showLoadingUI)
63
+ {
64
+ this.engine.displayLoadingUI();
65
+ }
66
+
67
+ try
68
+ {
69
+ if(options.useAssetContainer)
70
+ {
71
+ // Load into AssetContainer (additive style)
72
+ const tempScene = this.currentScene ?? new Scene(this.engine);
73
+ const container = await LoadAssetContainerAsync(
74
+ options.filename,
75
+ tempScene,
76
+ {
77
+ rootUrl: options.rootUrl,
78
+ onProgress: (evt) => this.reportProgress(evt, options.onProgress),
79
+ }
80
+ );
81
+
82
+ this.activeContainer = container;
83
+ container.addAllToScene();
84
+
85
+ // Identify the final scene in use after adding to scene
86
+ // (meshes[0]?.getScene() ensures we get the correct scene reference)
87
+ const usedScene = container.meshes.length > 0
88
+ ? container.meshes[0].getScene()
89
+ : tempScene;
90
+
91
+ this.autoTagAndEntityHook(container.meshes, usedScene);
92
+
93
+ if(options.onSuccess && this.currentScene)
94
+ {
95
+ options.onSuccess(this.currentScene);
96
+ }
97
+ }
98
+ else
99
+ {
100
+ // Load as a new Scene (full replacement)
101
+ const scene = await LoadSceneAsync(
102
+ options.filename,
103
+ this.engine,
104
+ {
105
+ rootUrl: options.rootUrl,
106
+ onProgress: (evt) => this.reportProgress(evt, options.onProgress),
107
+ }
108
+ );
109
+
110
+ // Dispose any previous scene
111
+ this.disposeCurrentScene();
112
+
113
+ this.currentScene = scene;
114
+ // Now that we've loaded a fresh scene, do tagging & entity creation
115
+ this.autoTagAndEntityHook(scene.meshes, scene);
116
+
117
+ if(options.onSuccess)
118
+ {
119
+ options.onSuccess(scene);
120
+ }
121
+ }
122
+ }
123
+ catch (error : any)
124
+ {
125
+ console.error('Level load failed:', error);
126
+ if(options.onError)
127
+ {
128
+ options.onError('Level loading failed.', error);
129
+ }
130
+ }
131
+ finally
132
+ {
133
+ if(options.showLoadingUI)
134
+ {
135
+ this.engine.hideLoadingUI();
136
+ }
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Disposes the current scene and container (if any), freeing up memory.
142
+ */
143
+ public disposeCurrentScene() : void
144
+ {
145
+ if(this.activeContainer)
146
+ {
147
+ this.activeContainer.removeAllFromScene();
148
+ this.activeContainer.dispose();
149
+ this.activeContainer = null;
150
+ }
151
+
152
+ if(this.currentScene)
153
+ {
154
+ this.currentScene.dispose();
155
+ this.currentScene = null;
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Returns the current active scene, if one is loaded.
161
+ */
162
+ public getScene() : Scene | null
163
+ {
164
+ return this.currentScene;
165
+ }
166
+
167
+ /**
168
+ * Internal helper: Reports loading progress as a percentage.
169
+ * @param evt ISceneLoaderProgressEvent data
170
+ * @param callback optional progress callback
171
+ */
172
+ private reportProgress(evt : ISceneLoaderProgressEvent, callback ?: (percent : number) => void) : void
173
+ {
174
+ if(!callback) { return; }
175
+
176
+ if(evt.lengthComputable && evt.total > 0)
177
+ {
178
+ const percent = (evt.loaded / evt.total) * 100;
179
+ callback(percent);
180
+ }
181
+ else
182
+ {
183
+ // If total size is unknown, just guess an arbitrary in-progress value
184
+ callback(50);
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Automatically tag loaded meshes and create entities based on metadata.
190
+ * Also, if audio metadata is present, create spatial sounds.
191
+ *
192
+ * @param meshes An array of loaded AbstractMesh objects.
193
+ * @param scene The scene into which these meshes have been loaded.
194
+ */
195
+ private autoTagAndEntityHook(meshes : AbstractMesh[], scene : Scene) : void
196
+ {
197
+ for(const mesh of meshes)
198
+ {
199
+ // Auto-tag common patterns by name
200
+ if(mesh.name)
201
+ {
202
+ if(mesh.name.startsWith('Door'))
203
+ {
204
+ Tags.AddTagsTo(mesh, 'Door');
205
+ }
206
+ if(mesh.name.startsWith('Trigger'))
207
+ {
208
+ Tags.AddTagsTo(mesh, 'Trigger');
209
+ }
210
+ if(mesh.name.startsWith('Enemy'))
211
+ {
212
+ Tags.AddTagsTo(mesh, 'EnemySpawn');
213
+ }
214
+ if(mesh.name.startsWith('SND_'))
215
+ {
216
+ Tags.AddTagsTo(mesh, 'AudioSource');
217
+ }
218
+ }
219
+
220
+ // Ensure mesh.metadata exists
221
+ const metadata = mesh.metadata ?? (mesh.metadata = {});
222
+
223
+ // 1) Check for entityType (from .babylon or glTF extras)
224
+ const entityType = metadata.entityType || metadata?.gltf?.extras?.entityType;
225
+ if(entityType)
226
+ {
227
+ this.entityManager.createEntity(entityType, {
228
+ mesh,
229
+ position: mesh.position.clone(),
230
+ rotation: mesh.rotation.clone(),
231
+ metadata,
232
+ });
233
+ }
234
+
235
+ // 2) Check for audio data: e.g. { soundFile: "AmbienceHum.wav", loop: true, autoplay: true }
236
+ // We also check if glTF extras has audio info.
237
+ const soundFile = metadata.soundFile ?? metadata?.gltf?.extras?.soundFile;
238
+ if(soundFile)
239
+ {
240
+ // Mark it as an audio source if not already
241
+ Tags.AddTagsTo(mesh, 'AudioSource');
242
+
243
+ // We'll read optional loop/autoplay from metadata too
244
+ const loop = metadata.loop ?? metadata?.gltf?.extras?.loop ?? false;
245
+ const autoplay = metadata.autoplay ?? metadata?.gltf?.extras?.autoplay ?? false;
246
+ const volume = metadata.volume ?? metadata?.gltf?.extras?.volume ?? 1.0;
247
+
248
+ const newSound = new Sound(`${ mesh.name }_sound`, soundFile, scene, null, {
249
+ loop: !!loop,
250
+ autoplay: !!autoplay,
251
+ spatialSound: true,
252
+ volume,
253
+ });
254
+
255
+ // If the mesh has a position, set the sound's 3D location
256
+ // (Note: if it's an empty or node, mesh.position will define the center)
257
+ newSound.setPosition(mesh.position);
258
+ }
259
+ }
260
+ }
261
+ }
package/src/sage.ts ADDED
@@ -0,0 +1,119 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // SkewedAspect Game Engine
3
+ //----------------------------------------------------------------------------------------------------------------------
4
+
5
+ // Interfaces
6
+ import { GameCanvas, RenderEngineOptions, SageOptions } from './interfaces/game.ts';
7
+
8
+ // Base Classes
9
+ import { SkewedAspectGameEngine } from './classes/gameEngine.ts';
10
+ import { GameEventBus } from './classes/eventBus.ts';
11
+
12
+ // Engines
13
+ import { SceneEngine } from './engines/scene.ts';
14
+
15
+ // Managers
16
+ import { BindingManager } from './managers/binding.ts';
17
+ import { GameManager } from './managers/game.ts';
18
+ import { GameEntityManager } from './managers/entity.ts';
19
+ import { UserInputManager } from './managers/input.ts';
20
+
21
+ // Utils
22
+ import { createEngine } from './utils/graphics.ts';
23
+ import { createPhysics } from './utils/physics.ts';
24
+ import { LoggingUtility } from './utils/logger.ts';
25
+ import { VERSION } from './utils/version.ts';
26
+
27
+ //----------------------------------------------------------------------------------------------------------------------
28
+
29
+ /**
30
+ * Creates an instance of SkewedAspectGameEngine.
31
+ * @param canvas - The game canvas.
32
+ * @param options - The game engine options including rendering options and log level.
33
+ * @returns A promise that resolves to an instance of SkewedAspectGameEngine.
34
+ */
35
+ export async function createGameEngine(
36
+ canvas : GameCanvas,
37
+ options : SageOptions = {}
38
+ ) : Promise<SkewedAspectGameEngine>
39
+ {
40
+ // Initialize logging
41
+ const logger = new LoggingUtility(options.logLevel || 'debug');
42
+ const engineLogger = logger.getLogger('SAGE');
43
+
44
+ engineLogger.info(`Initializing SAGE Game Engine v${ VERSION }...`);
45
+
46
+ // Initialize BabylonJS stuff
47
+ engineLogger.debug('Creating rendering engine...');
48
+ const engine = await createEngine(canvas, options.renderOptions || {});
49
+
50
+ engineLogger.debug('Creating physics engine...');
51
+ const physics = await createPhysics();
52
+
53
+ // Initialize internals
54
+ engineLogger.debug('Creating event bus...');
55
+ const eventBus = new GameEventBus(logger);
56
+
57
+ // Initialize SAGE Engines
58
+ engineLogger.debug('Creating scene engine...');
59
+ const sceneEngine = new SceneEngine(engine, physics, logger);
60
+
61
+ // Initialize SAGE Managers
62
+ engineLogger.debug('Creating managers...');
63
+ const inputManager = new UserInputManager(eventBus, canvas as HTMLElement, logger);
64
+ const bindingManager = new BindingManager(eventBus, logger);
65
+ const entityManager = new GameEntityManager(eventBus, logger, bindingManager);
66
+ const gameManager = new GameManager(engine, sceneEngine, entityManager, inputManager, logger);
67
+
68
+ engineLogger.info(`SAGE Game Engine v${ VERSION } initialized successfully.`);
69
+
70
+ // Register default input bindings
71
+ engineLogger.debug('Registering default input bindings...');
72
+ if(options.bindings)
73
+ {
74
+ for(const binding of options.bindings)
75
+ {
76
+ bindingManager.registerBinding(binding);
77
+ }
78
+ }
79
+
80
+ return new SkewedAspectGameEngine(
81
+ canvas,
82
+ engine,
83
+ physics,
84
+ eventBus,
85
+ logger,
86
+
87
+ // Engines
88
+ {
89
+ sceneEngine,
90
+ },
91
+
92
+ // Managers
93
+ {
94
+ bindingManager,
95
+ entityManager,
96
+ gameManager,
97
+ inputManager,
98
+ }
99
+ );
100
+ }
101
+
102
+ //----------------------------------------------------------------------------------------------------------------------
103
+ // Exports
104
+ //----------------------------------------------------------------------------------------------------------------------
105
+
106
+ export type { GameCanvas, RenderEngineOptions, SageOptions };
107
+ export type { GameEvent, GameEventCallback } from './classes/eventBus.ts';
108
+ export type { Action, AnalogAction, DigitalAction } from './interfaces/action.ts';
109
+ export type { GameEntityDefinition, GameEntityBehaviorConstructor } from './interfaces/entity.ts';
110
+ export type { LogLevel, LoggerInterface } from './interfaces/logger.ts';
111
+
112
+ // Export base classes
113
+ export { GameEventBus, SkewedAspectGameEngine };
114
+ export { GameEntity } from './classes/entity.ts';
115
+ export { LoggingUtility, ConsoleBackend, NullBackend } from './utils/logger.ts';
116
+ export { GameEntityBehavior } from './classes/entity.ts';
117
+ export { VERSION } from './utils/version.ts';
118
+
119
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,11 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Global type declarations for the SAGE engine
3
+ //----------------------------------------------------------------------------------------------------------------------
4
+
5
+ /**
6
+ * Global application version injected by Vite during build.
7
+ * This is read from package.json.
8
+ */
9
+ declare const __APP_VERSION__ : string;
10
+
11
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,16 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Utilities to test for capability of the current environment
3
+ //----------------------------------------------------------------------------------------------------------------------
4
+
5
+ export function isBrowser() : boolean
6
+ {
7
+ return typeof window !== 'undefined' && typeof window.document !== 'undefined';
8
+ }
9
+
10
+ export function hasWebGPU() : boolean
11
+ {
12
+ return isBrowser()
13
+ && !!window.navigator.gpu;
14
+ }
15
+
16
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,148 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Graphics Utility
3
+ //----------------------------------------------------------------------------------------------------------------------
4
+
5
+ import {
6
+ AbstractEngine,
7
+ Engine,
8
+ type EngineOptions,
9
+ NullEngine,
10
+ type NullEngineOptions,
11
+ WebGPUEngine,
12
+ type WebGPUEngineOptions,
13
+ } from '@babylonjs/core';
14
+
15
+ // Interfaces
16
+ import type { BabylonEngineOptions, GameCanvas, RenderEngineOptions } from '../interfaces/game.ts';
17
+
18
+ // Utilities
19
+ import { hasWebGPU } from './capabilities.ts';
20
+
21
+ //----------------------------------------------------------------------------------------------------------------------
22
+
23
+ /**
24
+ * Builds and initializes a WebGPU engine.
25
+ *
26
+ * @param canvas - The canvas element to render on (HTMLCanvasElement or OffscreenCanvas)
27
+ * @param options - Options specific to the WebGPU engine
28
+ * @returns A promise that resolves to an initialized WebGPUEngine
29
+ */
30
+ async function _buildWebGPUEngine(
31
+ canvas : HTMLCanvasElement | OffscreenCanvas,
32
+ options : WebGPUEngineOptions
33
+ ) : Promise<WebGPUEngine>
34
+ {
35
+ const engine = new WebGPUEngine(canvas, options);
36
+ await engine.initAsync();
37
+ return engine;
38
+ }
39
+
40
+ /**
41
+ * Builds a WebGL engine.
42
+ *
43
+ * @param canvas - The canvas element to render on (GameCanvas)
44
+ * @param options - Options for configuring the engine
45
+ * @returns An initialized WebGL Engine
46
+ */
47
+ function _buildWebGLEngine(canvas : GameCanvas, options : BabylonEngineOptions) : Engine
48
+ {
49
+ return new Engine(canvas, options.antialias, options.options, options.adaptToDeviceRatio);
50
+ }
51
+
52
+ /**
53
+ * Builds a Null engine, which is used when no rendering is required.
54
+ *
55
+ * @param options - Options specific to the Null engine
56
+ * @returns An initialized Null Engine
57
+ */
58
+ function _buildNullEngine(options : NullEngineOptions) : NullEngine
59
+ {
60
+ return new NullEngine(options as NullEngineOptions);
61
+ }
62
+
63
+ //------------------------------------------------------------------------------------------------------------------
64
+
65
+ /**
66
+ * Creates an appropriate engine based on the provided canvas and options.
67
+ *
68
+ * @param canvas - The canvas element to render on, or null for a Null engine
69
+ * @param options - Options for configuring the engine (EngineOptions, WebGPUEngineOptions, or NullEngineOptions)
70
+ * @returns A promise that resolves to an initialized AbstractEngine
71
+ */
72
+ export async function createEngine(
73
+ canvas : GameCanvas | null,
74
+ options : RenderEngineOptions
75
+ ) : Promise<AbstractEngine>
76
+ {
77
+ // Check if we should use a null engine (no canvas provided)
78
+ if(canvas === null)
79
+ {
80
+ console.debug('Using Null Engine');
81
+ return _buildNullEngine(options as NullEngineOptions);
82
+ }
83
+
84
+ // Extract the forceEngine option if available
85
+ const forceEngine = options.forceEngine || 'auto';
86
+
87
+ // Force WebGPU engine if specified and available
88
+ if(forceEngine === 'webgpu')
89
+ {
90
+ if(hasWebGPU())
91
+ {
92
+ try
93
+ {
94
+ console.debug('Using forced WebGPU engine');
95
+ return await _buildWebGPUEngine(canvas, options);
96
+ }
97
+ catch (error)
98
+ {
99
+ console.error('Forced WebGPU initialization failed:', error);
100
+ throw new Error(
101
+ 'Forced WebGPU failed to initialize. If WebGPU is required, check browser compatibility.'
102
+ );
103
+ }
104
+ }
105
+ else
106
+ {
107
+ throw new Error('WebGPU was forced but is not available in this browser.');
108
+ }
109
+ }
110
+
111
+ // Force WebGL engine if specified
112
+ if(forceEngine === 'webgl')
113
+ {
114
+ console.debug('Using forced WebGL engine');
115
+ return _buildWebGLEngine(canvas, options as BabylonEngineOptions);
116
+ }
117
+
118
+ // Auto detection (default behavior)
119
+ if(hasWebGPU())
120
+ {
121
+ try
122
+ {
123
+ console.debug('Using WebGPU');
124
+
125
+ return _buildWebGPUEngine(canvas, options)
126
+ .catch((error) =>
127
+ {
128
+ console.warn('WebGPU initialization failed, falling back to WebGL:', error);
129
+ return _buildWebGLEngine(canvas, options as EngineOptions);
130
+ });
131
+ }
132
+ catch (error)
133
+ {
134
+ console.warn('WebGPU initialization failed, falling back to WebGL:', error);
135
+ }
136
+ }
137
+ else
138
+ {
139
+ console.warn('WebGPU not supported, falling back to WebGL.');
140
+ }
141
+
142
+ console.debug('Using WebGL');
143
+
144
+ return _buildWebGLEngine(canvas, options as EngineOptions);
145
+ }
146
+
147
+ //----------------------------------------------------------------------------------------------------------------------
148
+