@geastack/native-webgl-angle 0.1.6

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.
@@ -0,0 +1,116 @@
1
+ #pragma once
2
+ #ifndef NOMINMAX
3
+ #define NOMINMAX
4
+ #endif
5
+ #include <windows.h>
6
+ #include <xaudio2.h>
7
+ #include <mfapi.h>
8
+ #include <mfidl.h>
9
+ #include <mfreadwrite.h>
10
+ #include <wrl/client.h>
11
+ #include <array>
12
+ #include <string>
13
+ #include <vector>
14
+
15
+ // The WebAudio graph remains in audio_host.mm. This adapter only delivers its
16
+ // planar float output to XAudio2 and decodes packaged audio through Media Foundation.
17
+ class GeaUwpAudioOutput final : public IXAudio2VoiceCallback {
18
+ // Two 10 ms blocks bound queued audio to 20 ms at 48 kHz. The previous
19
+ // three 1024-frame blocks delayed gain changes and new sounds by up to 64 ms.
20
+ static constexpr size_t frames = 480;
21
+ Microsoft::WRL::ComPtr<IXAudio2> engine;
22
+ IXAudio2MasteringVoice *master = nullptr;
23
+ IXAudio2SourceVoice *voice = nullptr;
24
+ std::array<std::array<float, frames * 2>, 2> buffers{};
25
+ std::array<float, frames> left{}, right{};
26
+ void *context = nullptr;
27
+ void (*render)(void *, size_t, float *, float *) = nullptr;
28
+
29
+ HRESULT submit(float *data) {
30
+ XAUDIO2_BUFFER buffer{};
31
+ buffer.AudioBytes = static_cast<UINT32>(frames * 2 * sizeof(float));
32
+ buffer.pAudioData = reinterpret_cast<const BYTE *>(data);
33
+ buffer.pContext = data;
34
+ return voice->SubmitSourceBuffer(&buffer);
35
+ }
36
+
37
+ public:
38
+ ~GeaUwpAudioOutput() {
39
+ if (voice) voice->DestroyVoice();
40
+ if (master) master->DestroyVoice();
41
+ }
42
+ bool start(void *ctx, void (*pull)(void *, size_t, float *, float *)) {
43
+ context = ctx;
44
+ render = pull;
45
+ if (FAILED(XAudio2Create(engine.GetAddressOf(), 0, XAUDIO2_DEFAULT_PROCESSOR))) return false;
46
+ if (FAILED(engine->CreateMasteringVoice(&master, 2, 48000))) return false;
47
+ WAVEFORMATEX format{};
48
+ format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
49
+ format.nChannels = 2;
50
+ format.nSamplesPerSec = 48000;
51
+ format.wBitsPerSample = 32;
52
+ format.nBlockAlign = 8;
53
+ format.nAvgBytesPerSec = 48000 * 8;
54
+ if (FAILED(engine->CreateSourceVoice(&voice, &format, 0, 1.0f, this))) return false;
55
+ for (auto &buffer : buffers) if (FAILED(submit(buffer.data()))) return false;
56
+ return SUCCEEDED(voice->Start());
57
+ }
58
+
59
+ void STDMETHODCALLTYPE OnBufferEnd(void *data) override {
60
+ render(context, frames, left.data(), right.data());
61
+ auto *output = static_cast<float *>(data);
62
+ for (size_t i = 0; i < frames; ++i) {
63
+ output[i * 2] = left[i];
64
+ output[i * 2 + 1] = right[i];
65
+ }
66
+ if (FAILED(submit(output))) OutputDebugStringA("[gea-audio] XAudio2 buffer submission failed\n");
67
+ }
68
+ void STDMETHODCALLTYPE OnStreamEnd() override {}
69
+ void STDMETHODCALLTYPE OnVoiceProcessingPassEnd() override {}
70
+ void STDMETHODCALLTYPE OnVoiceProcessingPassStart(UINT32) override {}
71
+ void STDMETHODCALLTYPE OnBufferStart(void *) override {}
72
+ void STDMETHODCALLTYPE OnLoopEnd(void *) override {}
73
+ void STDMETHODCALLTYPE OnVoiceError(void *, HRESULT) override {
74
+ OutputDebugStringA("[gea-audio] XAudio2 voice failed\n");
75
+ }
76
+ };
77
+
78
+ inline bool geaUwpDecodeAudio(const std::wstring &path, std::vector<std::vector<float>> &channels, double &rate) {
79
+ using Microsoft::WRL::ComPtr;
80
+ static const HRESULT startup = MFStartup(MF_VERSION);
81
+ if (FAILED(startup)) return false;
82
+ ComPtr<IMFSourceReader> reader;
83
+ if (FAILED(MFCreateSourceReaderFromURL(path.c_str(), nullptr, reader.GetAddressOf()))) return false;
84
+ ComPtr<IMFMediaType> requested;
85
+ if (FAILED(MFCreateMediaType(requested.GetAddressOf()))) return false;
86
+ requested->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
87
+ requested->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_Float);
88
+ if (FAILED(reader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, nullptr, requested.Get()))) return false;
89
+ ComPtr<IMFMediaType> actual;
90
+ if (FAILED(reader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, actual.GetAddressOf()))) return false;
91
+ UINT32 count = 0, sampleRate = 0;
92
+ if (FAILED(actual->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &count)) || count == 0 || count > 2) return false;
93
+ if (FAILED(actual->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &sampleRate)) || sampleRate == 0) return false;
94
+ rate = sampleRate;
95
+ channels.resize(count);
96
+ for (;;) {
97
+ DWORD flags = 0;
98
+ ComPtr<IMFSample> sample;
99
+ if (FAILED(reader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nullptr, &flags, nullptr, sample.GetAddressOf()))) return false;
100
+ if (flags & MF_SOURCE_READERF_ERROR) return false;
101
+ if (sample) {
102
+ ComPtr<IMFMediaBuffer> buffer;
103
+ if (FAILED(sample->ConvertToContiguousBuffer(buffer.GetAddressOf()))) return false;
104
+ BYTE *bytes = nullptr;
105
+ DWORD length = 0;
106
+ if (FAILED(buffer->Lock(&bytes, nullptr, &length))) return false;
107
+ const auto *pcm = reinterpret_cast<const float *>(bytes);
108
+ const size_t frames = length / (sizeof(float) * count);
109
+ for (size_t i = 0; i < frames; ++i)
110
+ for (UINT32 ch = 0; ch < count; ++ch) channels[ch].push_back(pcm[i * count + ch]);
111
+ buffer->Unlock();
112
+ }
113
+ if (flags & MF_SOURCE_READERF_ENDOFSTREAM) break;
114
+ }
115
+ return !channels[0].empty();
116
+ }
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@geastack/native-webgl-angle",
3
+ "version": "0.1.6",
4
+ "description": "Typed native WebGL compatibility layer backed by ANGLE/Metal",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/geastack/native-webgl-angle.git"
9
+ },
10
+ "engines": {
11
+ "node": ">=20.19"
12
+ },
13
+ "type": "module",
14
+ "exports": {
15
+ ".": "./src/nativeWebGL.ts",
16
+ "./nativeWebGL": "./src/nativeWebGL.ts",
17
+ "./nativeWebGLHost": "./src/nativeWebGLHost.ts",
18
+ "./nativeAudioHost": "./src/nativeAudioHost.ts",
19
+ "./nativeWebGLAnimation": "./src/nativeWebGLAnimation.ts",
20
+ "./nativeThreeUtils": "./src/nativeThreeUtils.ts",
21
+ "./nativeWebXRManager": "./src/nativeWebXRManager.ts",
22
+ "./troika-three-text": "./src/troika-three-text.ts",
23
+ "./geatsc-plugin": "./geatsc-plugin.mjs",
24
+ "./native/*": "./native/*",
25
+ "./package.json": "./package.json",
26
+ "./native-text-types": "./src/text/native-text-types.ts",
27
+ "./text/native-text-types": "./src/text/native-text-types.ts",
28
+ "./geatsc-plugin-batched-probe": "./geatsc-plugin-batched-probe.mjs",
29
+ "./test/batched-render-scene": "./test/batched-render-scene.ts",
30
+ "./test/auto-instanced-render-scene": "./test/auto-instanced-render-scene.ts"
31
+ },
32
+ "files": [
33
+ "src",
34
+ "native",
35
+ "geatsc-plugin.mjs",
36
+ "geatsc-plugin-uploads.mjs",
37
+ "geatsc-plugin-uniforms.mjs",
38
+ "geatsc-plugin-instancing.mjs",
39
+ "geatsc-plugin-batched-probe.mjs",
40
+ "test/batched-render-scene.ts",
41
+ "test/auto-instanced-render-scene.ts"
42
+ ],
43
+ "publishConfig": {
44
+ "access": "public",
45
+ "registry": "https://registry.npmjs.org/"
46
+ },
47
+ "scripts": {
48
+ "test:event-dispatch": "node test/native-event-dispatch-contract.test.mjs",
49
+ "test:host-effects": "node test/native-host-effects-contract.test.mjs",
50
+ "test:allocation-contracts": "node test/render-sort-contract.test.mjs && node test/upload-array-contract.test.mjs && node test/draw-dispatch-contract.test.mjs && node test/native-upload-transport.test.mjs && node test/fixed-uniform-contract.test.mjs && node test/uniform-binding-contract.test.mjs && node test/buffer-streaming-contract.test.mjs",
51
+ "check": "tsc -p tsconfig.json --noEmit",
52
+ "test": "node test/native-blending-contract.test.mjs && node test/native-transform-contract.test.mjs && node test/troika-three-text.test.mjs && node test/typed-void-contract.test.mjs && node test/instancing-transport-contract.test.mjs && node test/automatic-instancing-contract.test.mjs && node test/jsdoc-fold-contract.test.mjs",
53
+ "test:package": "node --experimental-vm-modules test/package.test.mjs"
54
+ },
55
+ "dependencies": {
56
+ "@geastack/apple": ">=0.1.0"
57
+ },
58
+ "peerDependencies": {
59
+ "three": "^0.185.0",
60
+ "@geastack/compiler": "^1.0.9"
61
+ },
62
+ "devDependencies": {
63
+ "@geastack/compiler": "^1.0.9",
64
+ "@geastack/core": "^0.1.12",
65
+ "@geastack/engine": "^0.1.0",
66
+ "@types/three": "^0.185.0",
67
+ "three": "^0.185.0",
68
+ "typescript": "latest"
69
+ }
70
+ }
@@ -0,0 +1,4 @@
1
+ declare const __gea_native_webgl_angle_f32_brand: unique symbol
2
+
3
+ type f32 = number & { readonly [__gea_native_webgl_angle_f32_brand]?: never }
4
+ declare function threeGamepadState(channel: number): number
@@ -0,0 +1,296 @@
1
+ // Native audio host facade — a Web-Audio-shaped wrapper over the
2
+ // `gea_three_audio_*` externs (native/audio_host.mm, registered through
3
+ // geatsc-plugin.mjs). It implements exactly the surface a Web Audio sound
4
+ // design uses: gain / biquad
5
+ // lowpass / buffer source / oscillator nodes, param automation (setValueAtTime,
6
+ // linear/exponential ramps, cancel), synth buffers written via getChannelData,
7
+ // and mp3 decode by asset basename (the native asset stub lowers
8
+ // `import url from './x.mp3'` to the file's basename).
9
+ //
10
+ // Everything here is structural: the classes satisfy the app's
11
+ // AudioContextLike/GainLike/... interfaces without importing them. On the web
12
+ // these classes are never constructed (the real AudioContext wins), so the
13
+ // ambient host externs are never referenced at runtime there.
14
+
15
+ // Node kinds (native/audio_host.mm NodeKind).
16
+ const NODE_GAIN = 1
17
+ const NODE_BIQUAD = 2
18
+ const NODE_BUFFER_SOURCE = 3
19
+ const NODE_OSCILLATOR = 4
20
+
21
+ // Param codes (audio_host.mm paramFor).
22
+ const PARAM_GAIN = 0
23
+ const PARAM_FREQUENCY = 1
24
+ const PARAM_Q = 2
25
+ const PARAM_PLAYBACK_RATE = 3
26
+
27
+ // Param methods (audio_host.mm ParamMethod).
28
+ const METHOD_SET_VALUE = 0
29
+ const METHOD_SET_VALUE_AT_TIME = 1
30
+ const METHOD_LINEAR_RAMP = 2
31
+ const METHOD_EXPONENTIAL_RAMP = 3
32
+ const METHOD_CANCEL = 4
33
+
34
+ export class NativeAudioParam {
35
+ readonly __audioNodeId: number
36
+ readonly __paramCode: number
37
+
38
+ constructor(nodeId: number, paramCode: number) {
39
+ this.__audioNodeId = nodeId
40
+ this.__paramCode = paramCode
41
+ }
42
+
43
+ get value(): number {
44
+ return threeAudioParamGet(this.__audioNodeId, this.__paramCode)
45
+ }
46
+
47
+ set value(v: number) {
48
+ threeAudioParamEvent(this.__audioNodeId, this.__paramCode, METHOD_SET_VALUE, v, 0)
49
+ }
50
+
51
+ setValueAtTime(value: number, time: number): void {
52
+ threeAudioParamEvent(this.__audioNodeId, this.__paramCode, METHOD_SET_VALUE_AT_TIME, value, time)
53
+ }
54
+
55
+ linearRampToValueAtTime(value: number, time: number): void {
56
+ threeAudioParamEvent(this.__audioNodeId, this.__paramCode, METHOD_LINEAR_RAMP, value, time)
57
+ }
58
+
59
+ exponentialRampToValueAtTime(value: number, time: number): void {
60
+ threeAudioParamEvent(this.__audioNodeId, this.__paramCode, METHOD_EXPONENTIAL_RAMP, value, time)
61
+ }
62
+
63
+ cancelScheduledValues(time: number): void {
64
+ threeAudioParamEvent(this.__audioNodeId, this.__paramCode, METHOD_CANCEL, 0, time)
65
+ }
66
+ }
67
+
68
+ export class NativeAudioNode {
69
+ readonly __audioNodeId: number
70
+
71
+ constructor(nodeId: number) {
72
+ this.__audioNodeId = nodeId
73
+ }
74
+
75
+ // Param connect targets never occur in the reference graph; node targets are
76
+ // always wrappers from this module, so the id read is direct.
77
+ connect(node: NativeAudioNode): void {
78
+ threeAudioConnect(this.__audioNodeId, node.__audioNodeId)
79
+ }
80
+ }
81
+
82
+ export class NativeAudioDestination extends NativeAudioNode {}
83
+
84
+ export class NativeGainNode extends NativeAudioNode {
85
+ readonly gain: NativeAudioParam
86
+
87
+ constructor(nodeId: number) {
88
+ super(nodeId)
89
+ this.gain = new NativeAudioParam(nodeId, PARAM_GAIN)
90
+ }
91
+ }
92
+
93
+ export class NativeBiquadFilterNode extends NativeAudioNode {
94
+ readonly frequency: NativeAudioParam
95
+ readonly Q: NativeAudioParam
96
+ private __typeName = 'lowpass'
97
+
98
+ constructor(nodeId: number) {
99
+ super(nodeId)
100
+ this.frequency = new NativeAudioParam(nodeId, PARAM_FREQUENCY)
101
+ this.Q = new NativeAudioParam(nodeId, PARAM_Q)
102
+ }
103
+
104
+ get type(): string {
105
+ return this.__typeName
106
+ }
107
+
108
+ set type(value: string) {
109
+ this.__typeName = value
110
+ threeAudioSetNodeType(this.__audioNodeId, value)
111
+ }
112
+ }
113
+
114
+ export class NativeOscillatorNode extends NativeAudioNode {
115
+ readonly frequency: NativeAudioParam
116
+ private __typeName = 'sine'
117
+
118
+ constructor(nodeId: number) {
119
+ super(nodeId)
120
+ this.frequency = new NativeAudioParam(nodeId, PARAM_FREQUENCY)
121
+ }
122
+
123
+ get type(): string {
124
+ return this.__typeName
125
+ }
126
+
127
+ set type(value: string) {
128
+ this.__typeName = value
129
+ threeAudioSetNodeType(this.__audioNodeId, value)
130
+ }
131
+
132
+ start(when = 0): void {
133
+ threeAudioSourceStart(this.__audioNodeId, when, 0)
134
+ }
135
+
136
+ stop(when = 0): void {
137
+ threeAudioSourceStop(this.__audioNodeId, when)
138
+ }
139
+ }
140
+
141
+ export class NativeAudioBuffer {
142
+ __nativeBufferId: number
143
+ readonly __sampleRate: number
144
+ readonly __channels: Float32Array[]
145
+
146
+ constructor(nativeBufferId: number, channels: number, length: number, sampleRate: number) {
147
+ this.__nativeBufferId = nativeBufferId
148
+ this.__sampleRate = sampleRate
149
+ this.__channels = []
150
+ if (nativeBufferId === 0) {
151
+ for (let ch = 0; ch < channels; ch++) this.__channels.push(new Float32Array(length))
152
+ }
153
+ }
154
+
155
+ // Synth buffers (createBuffer) hand out writable local planes; decoded
156
+ // buffers live natively and are never read back by the game.
157
+ getChannelData(channel: number): Float32Array {
158
+ if (channel >= 0 && channel < this.__channels.length) return this.__channels[channel]
159
+ return new Float32Array(0)
160
+ }
161
+
162
+ // Push locally written synth planes to the host on first playback.
163
+ __ensureUploaded(): number {
164
+ if (this.__nativeBufferId === 0 && this.__channels.length > 0) {
165
+ const length = this.__channels[0].length
166
+ const id = threeAudioCreateBuffer(this.__channels.length, length, this.__sampleRate)
167
+ for (let ch = 0; ch < this.__channels.length; ch++) {
168
+ threeAudioBufferChannelData(id, ch, this.__channels[ch])
169
+ }
170
+ this.__nativeBufferId = id
171
+ }
172
+ return this.__nativeBufferId
173
+ }
174
+ }
175
+
176
+ export class NativeBufferSourceNode extends NativeAudioNode {
177
+ buffer: NativeAudioBuffer | null = null
178
+ loop = false
179
+ readonly playbackRate: NativeAudioParam
180
+
181
+ constructor(nodeId: number) {
182
+ super(nodeId)
183
+ this.playbackRate = new NativeAudioParam(nodeId, PARAM_PLAYBACK_RATE)
184
+ }
185
+
186
+ start(when = 0): void {
187
+ const buffer = this.buffer
188
+ if (buffer) {
189
+ const bufferId = buffer.__ensureUploaded()
190
+ if (bufferId > 0) threeAudioSourceSetBuffer(this.__audioNodeId, bufferId)
191
+ }
192
+ threeAudioSourceStart(this.__audioNodeId, when, this.loop ? 1 : 0)
193
+ }
194
+
195
+ stop(when = 0): void {
196
+ threeAudioSourceStop(this.__audioNodeId, when)
197
+ }
198
+ }
199
+
200
+ export class NativeAudioContext {
201
+ readonly destination: NativeAudioDestination = new NativeAudioDestination(0)
202
+ readonly state: string = 'running'
203
+
204
+ get currentTime(): number {
205
+ return threeAudioCurrentTime()
206
+ }
207
+
208
+ get sampleRate(): number {
209
+ return threeAudioSampleRate()
210
+ }
211
+
212
+ createGain(): NativeGainNode {
213
+ return new NativeGainNode(threeAudioCreateNode(NODE_GAIN))
214
+ }
215
+
216
+ createBiquadFilter(): NativeBiquadFilterNode {
217
+ return new NativeBiquadFilterNode(threeAudioCreateNode(NODE_BIQUAD))
218
+ }
219
+
220
+ createBufferSource(): NativeBufferSourceNode {
221
+ return new NativeBufferSourceNode(threeAudioCreateNode(NODE_BUFFER_SOURCE))
222
+ }
223
+
224
+ createOscillator(): NativeOscillatorNode {
225
+ return new NativeOscillatorNode(threeAudioCreateNode(NODE_OSCILLATOR))
226
+ }
227
+
228
+ createBuffer(channels: number, length: number, sampleRate: number): NativeAudioBuffer {
229
+ return new NativeAudioBuffer(0, channels, length, sampleRate)
230
+ }
231
+
232
+ // Web-only surface (kept for structural compatibility with the app's
233
+ // AudioContextLike). Native clip loading goes through
234
+ // nativeAudioBufferForFile below; this promise never settles.
235
+ decodeAudioData(_data: ArrayBuffer): Promise<NativeAudioBuffer> {
236
+ return new Promise<NativeAudioBuffer>(() => {})
237
+ }
238
+
239
+ resume(): void {
240
+ threeAudioResume()
241
+ }
242
+ }
243
+
244
+ let activeNativeAudioContext: NativeAudioContext | null = null
245
+
246
+ // Create (or return) the native audio context. Only call this when the web
247
+ // AudioContext is absent — on the web the host externs referenced here do not
248
+ // exist at runtime.
249
+ export function createNativeAudioContext(): NativeAudioContext | null {
250
+ if (activeNativeAudioContext) return activeNativeAudioContext
251
+ if (!threeAudioSupported()) return null
252
+ activeNativeAudioContext = new NativeAudioContext()
253
+ return activeNativeAudioContext
254
+ }
255
+
256
+ // Decode an audio asset by basename ("coin.mp3" — what the native asset stub
257
+ // lowers an mp3 import to) into a playable buffer. Returns null when no native
258
+ // context is active (web) or the file is missing, letting callers fall through
259
+ // to the fetch + decodeAudioData path.
260
+ export function nativeAudioBufferForFile(name: string): NativeAudioBuffer | null {
261
+ if (!activeNativeAudioContext) return null
262
+ const bufferId = threeAudioLoadBuffer(name)
263
+ if (bufferId <= 0) return null
264
+ return new NativeAudioBuffer(bufferId, 0, 0, threeAudioSampleRate())
265
+ }
266
+
267
+ /** @gea-host-inert Reads scalar arguments and updates native audio state only; retains no script references or callbacks. */
268
+ declare function threeAudioSupported(): boolean
269
+ /** @gea-host-inert Reads scalar arguments and updates native audio state only; retains no script references or callbacks. */
270
+ declare function threeAudioCurrentTime(): number
271
+ /** @gea-host-inert Reads scalar arguments and updates native audio state only; retains no script references or callbacks. */
272
+ declare function threeAudioSampleRate(): number
273
+ /** @gea-host-inert Reads scalar arguments and updates native audio state only; retains no script references or callbacks. */
274
+ declare function threeAudioResume(): void
275
+ /** @gea-host-inert Reads scalar arguments and updates native audio state only; retains no script references or callbacks. */
276
+ declare function threeAudioCreateNode(kind: number): number
277
+ /** @gea-host-inert Reads scalar arguments and updates native audio state only; retains no script references or callbacks. */
278
+ declare function threeAudioSetNodeType(node: number, type: string): void
279
+ /** @gea-host-inert Reads scalar arguments and updates native audio state only; retains no script references or callbacks. */
280
+ declare function threeAudioConnect(src: number, dst: number): void
281
+ /** @gea-host-inert Reads scalar arguments and updates native audio state only; retains no script references or callbacks. */
282
+ declare function threeAudioParamEvent(node: number, param: number, method: number, value: number, time: number): void
283
+ /** @gea-host-inert Reads scalar arguments and updates native audio state only; retains no script references or callbacks. */
284
+ declare function threeAudioParamGet(node: number, param: number): number
285
+ /** @gea-host-inert Reads scalar arguments and updates native audio state only; retains no script references or callbacks. */
286
+ declare function threeAudioLoadBuffer(name: string): number
287
+ /** @gea-host-inert Reads scalar arguments and updates native audio state only; retains no script references or callbacks. */
288
+ declare function threeAudioCreateBuffer(channels: number, length: number, sampleRate: number): number
289
+ /** @gea-host-inert Synchronously copies the typed-array argument into native audio storage; retains no script references or callbacks. */
290
+ declare function threeAudioBufferChannelData(buffer: number, channel: number, data: Float32Array): void
291
+ /** @gea-host-inert Reads scalar arguments and updates native audio state only; retains no script references or callbacks. */
292
+ declare function threeAudioSourceSetBuffer(node: number, buffer: number): void
293
+ /** @gea-host-inert Reads scalar arguments and updates native audio state only; retains no script references or callbacks. */
294
+ declare function threeAudioSourceStart(node: number, when: number, loop: number): void
295
+ /** @gea-host-inert Reads scalar arguments and updates native audio state only; retains no script references or callbacks. */
296
+ declare function threeAudioSourceStop(node: number, when: number): void
@@ -0,0 +1,11 @@
1
+ export * from 'three/src/utils.js'
2
+
3
+ import { createNativeWebGLCanvas, type NativeWebGLCanvas } from './nativeWebGL'
4
+
5
+ /** Three's XHTML factory, narrowed to the canvas surface used by the native renderer. */
6
+ export function createElementNS(_name: string): NativeWebGLCanvas {
7
+ return createNativeWebGLCanvas(300, 150)
8
+ }
9
+
10
+ /** Three's browser fallback, backed by the real native ANGLE canvas. */
11
+ export function createCanvasElement(): NativeWebGLCanvas { return createElementNS('canvas') }