@nayan-ui/engine 0.0.0-stage → 1.0.0-beta.4

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 (198) hide show
  1. package/LICENSE +21 -0
  2. package/NayanEngine.podspec +28 -0
  3. package/README.md +308 -2
  4. package/lib/module/assets/AssetBytesCache.js +76 -0
  5. package/lib/module/assets/AssetCache.js +19 -0
  6. package/lib/module/assets/AssetLoader.js +52 -0
  7. package/lib/module/assets/CacheGeneration.js +7 -0
  8. package/lib/module/assets/IndexedDbCache.web.js +149 -0
  9. package/lib/module/assets/ProceduralCache.js +249 -0
  10. package/lib/module/assets/font.js +139 -0
  11. package/lib/module/assets/loft.js +142 -0
  12. package/lib/module/assets/model.js +96 -0
  13. package/lib/module/assets/modelling.js +254 -0
  14. package/lib/module/assets/shape.js +926 -0
  15. package/lib/module/assets/source.js +58 -0
  16. package/lib/module/assets/source.web.js +22 -0
  17. package/lib/module/assets/storage.js +4 -0
  18. package/lib/module/assets/storage.shared.js +38 -0
  19. package/lib/module/assets/storage.web.js +38 -0
  20. package/lib/module/assets/surface.js +47 -0
  21. package/lib/module/assets/texture.js +69 -0
  22. package/lib/module/drawing/DrawingCanvas.js +38 -0
  23. package/lib/module/drawing/DrawingCanvas.web.js +206 -0
  24. package/lib/module/drawing/DrawingDocument.js +256 -0
  25. package/lib/module/drawing/types.js +4 -0
  26. package/lib/module/globals.d.ts +12 -0
  27. package/lib/module/index.js +39 -0
  28. package/lib/module/input/Joystick.js +84 -0
  29. package/lib/module/input/Joystick.web.js +93 -0
  30. package/lib/module/input/keyboard.js +250 -0
  31. package/lib/module/media/audio-worklet.js +34 -0
  32. package/lib/module/media/audio.js +97 -0
  33. package/lib/module/media/haptics.js +82 -0
  34. package/lib/module/media/output.web.js +66 -0
  35. package/lib/module/native/BrowserEngine.js +568 -0
  36. package/lib/module/native/EngineBridge.js +4 -0
  37. package/lib/module/native/NativeNayanEngine.js +7 -0
  38. package/lib/module/native/NativeNayanEngine.web.js +62 -0
  39. package/lib/module/native/prepare.js +7 -0
  40. package/lib/module/native/prepare.web.js +17 -0
  41. package/lib/module/native/spec/NativeNayanEngine.js +17 -0
  42. package/lib/module/package.json +1 -0
  43. package/lib/module/render/GameView.js +317 -0
  44. package/lib/module/render/camera.js +34 -0
  45. package/lib/module/render/host.js +39 -0
  46. package/lib/module/render/host.web.js +104 -0
  47. package/lib/module/render/light.js +24 -0
  48. package/lib/module/render/meshes.js +159 -0
  49. package/lib/module/render/quality.js +119 -0
  50. package/lib/module/render/responder.js +4 -0
  51. package/lib/module/render/responder.web.js +97 -0
  52. package/lib/module/render/touch.js +185 -0
  53. package/lib/module/render/useWorld.js +72 -0
  54. package/lib/module/types.js +30 -0
  55. package/lib/module/world/Scoreboard.js +161 -0
  56. package/lib/module/world/TransformBatch.js +128 -0
  57. package/lib/module/world/World.js +704 -0
  58. package/lib/module/world/color.js +80 -0
  59. package/lib/module/world/desc.js +187 -0
  60. package/lib/module/world/effects.js +463 -0
  61. package/lib/module/world/quat.js +86 -0
  62. package/lib/module/world/types.js +4 -0
  63. package/lib/typescript/package.json +1 -0
  64. package/lib/typescript/src/assets/AssetBytesCache.d.ts +7 -0
  65. package/lib/typescript/src/assets/AssetCache.d.ts +9 -0
  66. package/lib/typescript/src/assets/AssetLoader.d.ts +11 -0
  67. package/lib/typescript/src/assets/CacheGeneration.d.ts +3 -0
  68. package/lib/typescript/src/assets/IndexedDbCache.web.d.ts +20 -0
  69. package/lib/typescript/src/assets/ProceduralCache.d.ts +45 -0
  70. package/lib/typescript/src/assets/font.d.ts +53 -0
  71. package/lib/typescript/src/assets/loft.d.ts +31 -0
  72. package/lib/typescript/src/assets/model.d.ts +58 -0
  73. package/lib/typescript/src/assets/modelling.d.ts +157 -0
  74. package/lib/typescript/src/assets/shape.d.ts +203 -0
  75. package/lib/typescript/src/assets/source.d.ts +10 -0
  76. package/lib/typescript/src/assets/source.web.d.ts +8 -0
  77. package/lib/typescript/src/assets/storage.d.ts +2 -0
  78. package/lib/typescript/src/assets/storage.shared.d.ts +7 -0
  79. package/lib/typescript/src/assets/storage.web.d.ts +7 -0
  80. package/lib/typescript/src/assets/surface.d.ts +17 -0
  81. package/lib/typescript/src/assets/texture.d.ts +30 -0
  82. package/lib/typescript/src/drawing/DrawingCanvas.d.ts +5 -0
  83. package/lib/typescript/src/drawing/DrawingCanvas.web.d.ts +4 -0
  84. package/lib/typescript/src/drawing/DrawingDocument.d.ts +210 -0
  85. package/lib/typescript/src/drawing/types.d.ts +12 -0
  86. package/lib/typescript/src/index.d.ts +45 -0
  87. package/lib/typescript/src/input/Joystick.d.ts +20 -0
  88. package/lib/typescript/src/input/Joystick.web.d.ts +13 -0
  89. package/lib/typescript/src/input/keyboard.d.ts +17 -0
  90. package/lib/typescript/src/media/audio.d.ts +46 -0
  91. package/lib/typescript/src/media/haptics.d.ts +22 -0
  92. package/lib/typescript/src/media/output.web.d.ts +4 -0
  93. package/lib/typescript/src/native/BrowserEngine.d.ts +8 -0
  94. package/lib/typescript/src/native/EngineBridge.d.ts +26 -0
  95. package/lib/typescript/src/native/NativeNayanEngine.d.ts +5 -0
  96. package/lib/typescript/src/native/NativeNayanEngine.web.d.ts +10 -0
  97. package/lib/typescript/src/native/prepare.d.ts +3 -0
  98. package/lib/typescript/src/native/prepare.web.d.ts +2 -0
  99. package/lib/typescript/src/native/spec/NativeNayanEngine.d.ts +211 -0
  100. package/lib/typescript/src/render/GameView.d.ts +107 -0
  101. package/lib/typescript/src/render/camera.d.ts +6 -0
  102. package/lib/typescript/src/render/host.d.ts +29 -0
  103. package/lib/typescript/src/render/host.web.d.ts +16 -0
  104. package/lib/typescript/src/render/light.d.ts +12 -0
  105. package/lib/typescript/src/render/meshes.d.ts +45 -0
  106. package/lib/typescript/src/render/quality.d.ts +48 -0
  107. package/lib/typescript/src/render/responder.d.ts +2 -0
  108. package/lib/typescript/src/render/responder.web.d.ts +40 -0
  109. package/lib/typescript/src/render/touch.d.ts +26 -0
  110. package/lib/typescript/src/render/useWorld.d.ts +20 -0
  111. package/lib/typescript/src/types.d.ts +81 -0
  112. package/lib/typescript/src/world/Scoreboard.d.ts +75 -0
  113. package/lib/typescript/src/world/TransformBatch.d.ts +39 -0
  114. package/lib/typescript/src/world/World.d.ts +207 -0
  115. package/lib/typescript/src/world/color.d.ts +18 -0
  116. package/lib/typescript/src/world/desc.d.ts +17 -0
  117. package/lib/typescript/src/world/effects.d.ts +157 -0
  118. package/lib/typescript/src/world/quat.d.ts +31 -0
  119. package/lib/typescript/src/world/types.d.ts +238 -0
  120. package/lib/web/audio-worklet.js +29 -0
  121. package/lib/web/engine_core_bg.wasm +0 -0
  122. package/lib/web/index.js +8240 -0
  123. package/lib/web/types/src/assets/AssetBytesCache.d.ts +6 -0
  124. package/lib/web/types/src/assets/AssetCache.d.ts +8 -0
  125. package/lib/web/types/src/assets/AssetLoader.d.ts +10 -0
  126. package/lib/web/types/src/assets/CacheGeneration.d.ts +2 -0
  127. package/lib/web/types/src/assets/IndexedDbCache.d.ts +19 -0
  128. package/lib/web/types/src/assets/ProceduralCache.d.ts +44 -0
  129. package/lib/web/types/src/assets/font.d.ts +52 -0
  130. package/lib/web/types/src/assets/loft.d.ts +30 -0
  131. package/lib/web/types/src/assets/model.d.ts +57 -0
  132. package/lib/web/types/src/assets/modelling.d.ts +156 -0
  133. package/lib/web/types/src/assets/shape.d.ts +202 -0
  134. package/lib/web/types/src/assets/source.d.ts +7 -0
  135. package/lib/web/types/src/assets/storage.d.ts +6 -0
  136. package/lib/web/types/src/assets/storage.shared.d.ts +6 -0
  137. package/lib/web/types/src/assets/surface.d.ts +16 -0
  138. package/lib/web/types/src/assets/texture.d.ts +29 -0
  139. package/lib/web/types/src/drawing/DrawingCanvas.d.ts +3 -0
  140. package/lib/web/types/src/drawing/DrawingDocument.d.ts +209 -0
  141. package/lib/web/types/src/drawing/types.d.ts +11 -0
  142. package/lib/web/types/src/index.d.ts +44 -0
  143. package/lib/web/types/src/input/Joystick.d.ts +12 -0
  144. package/lib/web/types/src/input/keyboard.d.ts +16 -0
  145. package/lib/web/types/src/media/audio.d.ts +45 -0
  146. package/lib/web/types/src/media/haptics.d.ts +21 -0
  147. package/lib/web/types/src/media/output.d.ts +3 -0
  148. package/lib/web/types/src/native/BrowserEngine.d.ts +7 -0
  149. package/lib/web/types/src/native/EngineBridge.d.ts +25 -0
  150. package/lib/web/types/src/native/NativeNayanEngine.d.ts +9 -0
  151. package/lib/web/types/src/native/prepare.d.ts +1 -0
  152. package/lib/web/types/src/native/spec/NativeNayanEngine.d.ts +210 -0
  153. package/lib/web/types/src/render/GameView.d.ts +106 -0
  154. package/lib/web/types/src/render/camera.d.ts +5 -0
  155. package/lib/web/types/src/render/host.d.ts +15 -0
  156. package/lib/web/types/src/render/light.d.ts +11 -0
  157. package/lib/web/types/src/render/meshes.d.ts +44 -0
  158. package/lib/web/types/src/render/quality.d.ts +47 -0
  159. package/lib/web/types/src/render/responder.d.ts +39 -0
  160. package/lib/web/types/src/render/touch.d.ts +25 -0
  161. package/lib/web/types/src/render/useWorld.d.ts +19 -0
  162. package/lib/web/types/src/types.d.ts +80 -0
  163. package/lib/web/types/src/world/Scoreboard.d.ts +74 -0
  164. package/lib/web/types/src/world/TransformBatch.d.ts +38 -0
  165. package/lib/web/types/src/world/World.d.ts +206 -0
  166. package/lib/web/types/src/world/color.d.ts +17 -0
  167. package/lib/web/types/src/world/desc.d.ts +16 -0
  168. package/lib/web/types/src/world/effects.d.ts +156 -0
  169. package/lib/web/types/src/world/quat.d.ts +30 -0
  170. package/lib/web/types/src/world/types.d.ts +237 -0
  171. package/native/android/CMakeLists.txt +34 -0
  172. package/native/android/NayanEngineSpec.h +15 -0
  173. package/native/android/build.gradle +26 -0
  174. package/native/android/codegen/CMakeLists.txt +3 -0
  175. package/native/android/proguard-rules.pro +2 -0
  176. package/native/android/src/main/java/com/nayanui/engine/EngineSurfaceView.java +295 -0
  177. package/native/android/src/main/java/com/nayanui/engine/EngineSurfaceViewManager.java +45 -0
  178. package/native/android/src/main/java/com/nayanui/engine/NayanEnginePackage.java +24 -0
  179. package/native/core/build/EngineCore.xcframework/Info.plist +43 -0
  180. package/native/core/build/EngineCore.xcframework/ios-arm64/libengine_core.a +0 -0
  181. package/native/core/build/EngineCore.xcframework/ios-arm64-simulator/libengine_core.a +0 -0
  182. package/native/core/build/android/arm64-v8a/libengine_core.a +0 -0
  183. package/native/core/build/android/armeabi-v7a/libengine_core.a +0 -0
  184. package/native/core/build/android/x86/libengine_core.a +0 -0
  185. package/native/core/build/android/x86_64/libengine_core.a +0 -0
  186. package/native/core/include/drawing-licenses.txt +58 -0
  187. package/native/core/include/engine_core.h +331 -0
  188. package/native/cpp/NayanEngineModule.cpp +998 -0
  189. package/native/cpp/NayanEngineModule.h +174 -0
  190. package/native/cpp/generated/NayanEngineSpecJSI.cpp +2 -0
  191. package/native/cpp/generated/NayanEngineSpecJSI.h +1026 -0
  192. package/native/ios/NayanEngineProvider.h +5 -0
  193. package/native/ios/NayanEngineProvider.mm +21 -0
  194. package/native/ios/NayanEngineSurface.mm +252 -0
  195. package/native/scripts/check-package.mjs +39 -0
  196. package/package.json +162 -4
  197. package/react-native.config.js +18 -0
  198. package/src/native/spec/NativeNayanEngine.ts +265 -0
@@ -0,0 +1,568 @@
1
+ "use strict";
2
+
3
+ /** Raw C exports are shared with the mobile JSI bridge. All pointers stay private here. */
4
+
5
+ const lengths = {
6
+ matrices: c => c * 64,
7
+ colors: c => c * 16,
8
+ regions: c => c * 16,
9
+ ranges: () => 4096 * 16,
10
+ done: c => (c * 5 + 64) * 4,
11
+ events: () => 4096 * 16,
12
+ scratch: () => 64,
13
+ scoreboard_state: () => 64
14
+ };
15
+ /** Mirrors whose native memory is exactly as long as the mirror, so a renderer can read it in place. */
16
+ const drawable = new Set(['matrices', 'colors', 'regions', 'ranges']);
17
+ const MATRIX_BYTES = 64;
18
+ const VEC4_BYTES = 16;
19
+ /** Doubles per `setTransforms` record (TRANSFORM_STRIDE). */
20
+ const TRANSFORM_DOUBLES = 16;
21
+ /** Scoreboard slot JS may clear after reading (see engine_world_scoreboard_state). */
22
+ const SCORE_FLAGS = 4;
23
+ export function createBrowserEngine(core) {
24
+ const worlds = new Map();
25
+ // Mirrors handed to JS, so render calls given a world's own buffers read its memory in place.
26
+ const mirrors = new WeakMap();
27
+ let nextWorld = 1;
28
+ const slots = [];
29
+ const encoder = new TextEncoder();
30
+ const decoder = new TextDecoder();
31
+
32
+ // Exports are resolved once (lazily, so a core missing an optional export fails only when it is used).
33
+ const resolved = Object.create(null);
34
+ const ex = name => {
35
+ const cached = resolved[name];
36
+ if (cached) return cached;
37
+ const found = core[`engine_${name}`];
38
+ if (typeof found !== 'function') throw new Error(`Engine WASM is missing engine_${name}`);
39
+ resolved[name] = found;
40
+ return found;
41
+ };
42
+ const fn = (name, ...args) => Number(ex(name)(...args));
43
+
44
+ // Views into WASM memory, re-created only after memory.grow replaces (detaches) the buffer.
45
+ let heap = null;
46
+ let heapU8 = new Uint8Array(0);
47
+ let heapF64 = new Float64Array(0);
48
+ const memory = () => {
49
+ const current = core.memory.buffer;
50
+ if (current !== heap) {
51
+ heap = current;
52
+ heapU8 = new Uint8Array(current);
53
+ heapF64 = new Float64Array(current, 0, current.byteLength >> 3);
54
+ }
55
+ return current;
56
+ };
57
+ const u8 = () => {
58
+ memory();
59
+ return heapU8;
60
+ };
61
+ const f64 = () => {
62
+ memory();
63
+ return heapF64;
64
+ };
65
+ const buf = value => {
66
+ if (!(value instanceof ArrayBuffer)) throw new Error('Engine: expected an ArrayBuffer');
67
+ return value;
68
+ };
69
+ const bytes = value => new Uint8Array(buf(value));
70
+ const copy = (pointer, length) => u8().slice(pointer, pointer + length).buffer;
71
+
72
+ // Reused aligned scratch allocations, one per argument position.
73
+ const slot = (index, size) => {
74
+ let s = slots[index];
75
+ if (!s || s.size < size) {
76
+ if (s) {
77
+ slots[index] = undefined;
78
+ fn('web_free', s.pointer, s.size);
79
+ }
80
+ const reserved = Math.max(64, size);
81
+ s = {
82
+ pointer: fn('web_alloc', reserved),
83
+ size: reserved
84
+ };
85
+ slots[index] = s;
86
+ }
87
+ return s.pointer;
88
+ };
89
+ /** Copies `value` into scratch slot `index` (allocating first, since growth detaches views). */
90
+ const put = (index, value) => {
91
+ const pointer = slot(index, value.byteLength);
92
+ u8().set(value, pointer);
93
+ return pointer;
94
+ };
95
+ /** A zero-filled scratch slot for a native function to write into. */
96
+ const zeroed = (index, size) => {
97
+ const pointer = slot(index, size);
98
+ u8().fill(0, pointer, pointer + size);
99
+ return pointer;
100
+ };
101
+ const inputs = values => values.map((value, i) => put(i, value));
102
+ const data = (...values) => inputs(values.map(bytes));
103
+ const w = id => {
104
+ const world = worlds.get(id);
105
+ if (!world) throw new Error('World: unknown or disposed world');
106
+ return world;
107
+ };
108
+ const ptr = id => w(id).pointer;
109
+ const native = (world, name) => Number(ex(`world_${name}`)(world.pointer));
110
+ /** Copies up to `length` bytes of a world output into its mirror (if JS asked for that mirror). */
111
+ const sync = (world, name, length) => {
112
+ const view = world.views.get(name);
113
+ if (!view) return;
114
+ const n = Math.min(length, view.byteLength);
115
+ if (n <= 0) return;
116
+ const pointer = native(world, name);
117
+ view.set(u8().subarray(pointer, pointer + n));
118
+ };
119
+ const buffer = (id, name) => {
120
+ const world = w(id);
121
+ let b = world.buffers.get(name);
122
+ if (!b) {
123
+ b = new ArrayBuffer(lengths[name](world.capacity));
124
+ world.buffers.set(name, b);
125
+ world.views.set(name, new Uint8Array(b));
126
+ if (name === 'scoreboard_state') world.score = new Float64Array(b);
127
+ mirrors.set(b, {
128
+ world,
129
+ name
130
+ });
131
+ // done/events have valid lengths smaller than their reserved storage.
132
+ sync(world, name, name === 'done' ? fn('world_done_len', world.pointer) * 4 : name === 'events' ? fn('world_event_len', world.pointer) * 4 : b.byteLength);
133
+ }
134
+ return b;
135
+ };
136
+ const scoreIn = world => {
137
+ if (!world.score) return;
138
+ const pointer = native(world, 'scoreboard_state');
139
+ f64()[(pointer >> 3) + SCORE_FLAGS] = world.score[SCORE_FLAGS];
140
+ };
141
+ /**
142
+ * A render input: a live world's own output buffer is read in place (the mirror holds the same
143
+ * data, copied after the last update), anything else is copied into scratch slot `index`.
144
+ */
145
+ const source = (index, value) => {
146
+ const b = buf(value);
147
+ const mirror = mirrors.get(b);
148
+ if (mirror && drawable.has(mirror.name) && worlds.get(mirror.world.id) === mirror.world) return native(mirror.world, mirror.name);
149
+ return put(index, new Uint8Array(b));
150
+ };
151
+ const scalar = (name, id, ...args) => Number(ex(name)(ptr(id), ...args));
152
+ const hit = (id, name, ...args) => {
153
+ const world = w(id);
154
+ const result = Number(ex(name)(world.pointer, ...args));
155
+ sync(world, 'scratch', 64);
156
+ return result === 0xffffffff || result === -1 ? -1 : result;
157
+ };
158
+ const query = (id, name, ...args) => {
159
+ const world = w(id);
160
+ const result = Number(ex(name)(world.pointer, ...args));
161
+ sync(world, 'scratch', 64);
162
+ return result !== 0;
163
+ };
164
+ const string = (name, args = []) => {
165
+ const length = fn(name, ...args, 0, 0);
166
+ const p = zeroed(0, length + 1);
167
+ fn(name, ...args, p, length + 1);
168
+ return decoder.decode(u8().subarray(p, p + length));
169
+ };
170
+ const cString = pointer => {
171
+ const b = u8();
172
+ let end = pointer;
173
+ while (end < b.length && b[end] !== 0) end++;
174
+ return decoder.decode(b.subarray(pointer, end));
175
+ };
176
+ const geometry = handle => {
177
+ try {
178
+ const error = cString(fn('geometry_error', handle));
179
+ if (error) throw new Error(`Engine geometry: ${error}`);
180
+ const pointer = fn('geometry_data', handle);
181
+ const size = fn('geometry_size', handle);
182
+ if (size < 8) throw new Error('Engine geometry: invalid result');
183
+ const header = new Uint32Array(memory(), pointer, 2);
184
+ const vertexBytes = header[0] * 44;
185
+ const indexBytes = header[1] * 4;
186
+ if (size !== 8 + vertexBytes + indexBytes) throw new Error('Engine geometry: invalid buffers');
187
+ return {
188
+ vertices: copy(pointer + 8, vertexBytes),
189
+ indices: copy(pointer + 8 + vertexBytes, indexBytes)
190
+ };
191
+ } finally {
192
+ fn('geometry_free', handle);
193
+ }
194
+ };
195
+ const meshCopy = (mesh, kind) => {
196
+ const count = fn(`render_mesh_${kind === 'vertices' ? 'vertex' : 'index'}_count`, mesh);
197
+ const size = count * (kind === 'vertices' ? 44 : 4);
198
+ const p = zeroed(0, size);
199
+ fn(`render_mesh_${kind}`, mesh, p, size / 4);
200
+ return copy(p, size);
201
+ };
202
+ const dimensions = (name, id, count, float) => {
203
+ const p = zeroed(0, count * 4);
204
+ fn(name, id, p);
205
+ return Array.from(float ? new Float32Array(memory(), p, count) : new Uint32Array(memory(), p, count));
206
+ };
207
+ /** A promise for work that runs right here (its errors reject it). */
208
+ const settle = work => new Promise(resolve => resolve(work()));
209
+ const engine = {
210
+ createWorld(capacity) {
211
+ if (!Number.isInteger(capacity) || capacity < 1 || capacity > 1_000_000) throw new Error('World: capacity must be an integer in 1..1000000');
212
+ const id = nextWorld++;
213
+ worlds.set(id, {
214
+ id,
215
+ pointer: fn('world_new', capacity),
216
+ capacity,
217
+ buffers: new Map(),
218
+ views: new Map(),
219
+ score: null
220
+ });
221
+ return id;
222
+ },
223
+ destroyWorld(id) {
224
+ fn('world_free', ptr(id));
225
+ worlds.delete(id);
226
+ },
227
+ spawnDesc(id, desc) {
228
+ const b = bytes(desc);
229
+ const p = put(0, b);
230
+ return hit(id, 'world_spawn_desc', p, b.byteLength / 8);
231
+ },
232
+ setDesc(id, entity, desc) {
233
+ const b = bytes(desc);
234
+ const p = put(0, b);
235
+ return scalar('world_set_desc', id, entity, p, b.byteLength / 8) !== 0;
236
+ },
237
+ setTransforms(id, transforms, count) {
238
+ const b = buf(transforms);
239
+ // The batch's storage only grows: copy just the queued records.
240
+ const used = count > 0 ? Math.min(b.byteLength, Math.trunc(count) * TRANSFORM_DOUBLES * 8) : b.byteLength;
241
+ const p = put(0, new Uint8Array(b, 0, used));
242
+ return scalar('world_set_transforms', id, p, used / 8, count);
243
+ },
244
+ despawn(id, entity) {
245
+ return scalar('world_despawn', id, entity) !== 0;
246
+ },
247
+ impulse(id, entity, x, y, z) {
248
+ scalar('world_apply_impulse', id, entity, x, y, z);
249
+ },
250
+ setGravity(id, x, y, z) {
251
+ scalar('world_set_gravity', id, x, y, z);
252
+ },
253
+ setBounds(id, a, b, c, d) {
254
+ scalar('world_set_bounds', id, a, b, c, d);
255
+ },
256
+ setListener(id, entity) {
257
+ scalar('world_set_listener', id, entity);
258
+ },
259
+ raycast(id, ...args) {
260
+ return hit(id, 'world_raycast', ...args);
261
+ },
262
+ pick(id, ...args) {
263
+ return hit(id, 'world_pick', ...args);
264
+ },
265
+ pickScreen(id, x, y) {
266
+ return hit(id, 'world_pick_screen', x, y);
267
+ },
268
+ setView(id, matrix, width, height) {
269
+ const p = put(0, bytes(matrix));
270
+ scalar('world_set_view', id, p, width, height);
271
+ },
272
+ screenRay(id, x, y) {
273
+ return query(id, 'world_screen_ray', x, y);
274
+ },
275
+ screenToWorld(id, ...args) {
276
+ return query(id, 'world_screen_to_world', ...args);
277
+ },
278
+ toScreen(id, x, y, z) {
279
+ return query(id, 'world_to_screen', x, y, z);
280
+ },
281
+ readPosition(id, entity, rendered) {
282
+ return query(id, 'world_read_position', entity, +rendered);
283
+ },
284
+ readVelocity(id, entity) {
285
+ return query(id, 'world_read_velocity', entity);
286
+ },
287
+ animate(id, value) {
288
+ const b = bytes(value);
289
+ const p = put(0, b);
290
+ return scalar('world_animate', id, p, b.byteLength / 8);
291
+ },
292
+ stopAnimation(id, entity) {
293
+ return scalar('world_stop_animation', id, entity) !== 0;
294
+ },
295
+ burst(id, value) {
296
+ const b = bytes(value);
297
+ const p = put(0, b);
298
+ return scalar('world_burst', id, p, b.byteLength / 8);
299
+ },
300
+ update(id, dt) {
301
+ const world = w(id);
302
+ scoreIn(world);
303
+ ex('world_update')(world.pointer, dt);
304
+ // Only the first `count` instances are valid after an update (ranges never reach past them),
305
+ // so the per-instance mirrors copy just those rather than the whole capacity.
306
+ const count = Number(ex('world_count')(world.pointer));
307
+ sync(world, 'matrices', count * MATRIX_BYTES);
308
+ sync(world, 'colors', count * VEC4_BYTES);
309
+ sync(world, 'regions', count * VEC4_BYTES);
310
+ sync(world, 'ranges', Infinity);
311
+ sync(world, 'scoreboard_state', Infinity);
312
+ },
313
+ after(id, seconds) {
314
+ return scalar('world_after', id, seconds);
315
+ },
316
+ cancelTimers(id) {
317
+ return scalar('world_cancel_timers', id);
318
+ },
319
+ scoreboard(id, op, a, b) {
320
+ const world = w(id);
321
+ scoreIn(world);
322
+ const result = Number(ex('world_scoreboard')(world.pointer, op, a, b));
323
+ sync(world, 'scoreboard_state', Infinity);
324
+ return result;
325
+ },
326
+ getScoreboard: id => buffer(id, 'scoreboard_state'),
327
+ count: id => scalar('world_count', id),
328
+ getMatrices: id => buffer(id, 'matrices'),
329
+ getColors: id => buffer(id, 'colors'),
330
+ getRegions: id => buffer(id, 'regions'),
331
+ getRanges: id => buffer(id, 'ranges'),
332
+ getDone: id => buffer(id, 'done'),
333
+ doneLength(id) {
334
+ const world = w(id);
335
+ const length = fn('world_done_len', world.pointer);
336
+ sync(world, 'done', length * 4);
337
+ return length;
338
+ },
339
+ getEvents: id => buffer(id, 'events'),
340
+ eventLength(id) {
341
+ const world = w(id);
342
+ const length = fn('world_event_len', world.pointer);
343
+ sync(world, 'events', length * 4);
344
+ return length;
345
+ },
346
+ getScratch: id => buffer(id, 'scratch'),
347
+ modelRig: mesh => string('model_rig', [mesh]),
348
+ playClip(id, entity, name, loop, speed, fade, time) {
349
+ const b = encoder.encode(name);
350
+ const p = put(0, b);
351
+ return scalar('world_play_clip', id, entity, p, b.length, +loop, speed, fade, time) !== 0;
352
+ },
353
+ stopClip(id, entity, reset) {
354
+ return scalar('world_stop_clip', id, entity, +reset) !== 0;
355
+ },
356
+ skinPoints(id, entity, vertices, output, after) {
357
+ const count = bytes(vertices).byteLength / 4;
358
+ if (bytes(output).byteLength < count * 12) throw new Error('skinPoints: output needs three floats per vertex');
359
+ const [v, o] = data(vertices, output);
360
+ const ok = scalar('world_skin_points', id, entity, v, count, o, after) !== 0;
361
+ if (ok) bytes(output).set(u8().subarray(o, o + output.byteLength));
362
+ return ok;
363
+ },
364
+ cacheRead(key) {
365
+ const text = encoder.encode(key);
366
+ const p = put(0, text);
367
+ const record = fn('cache_read', p, text.length);
368
+ if (!record) return new ArrayBuffer(0);
369
+ try {
370
+ return copy(fn('cache_data', record), fn('cache_size', record));
371
+ } finally {
372
+ fn('cache_free', record);
373
+ }
374
+ },
375
+ cacheWrite(key, value) {
376
+ const text = encoder.encode(key);
377
+ const [p, data] = inputs([text, bytes(value)]);
378
+ return fn('cache_write', p, text.length, data, bytes(value).length) !== 0;
379
+ },
380
+ cacheRemove(key) {
381
+ const text = encoder.encode(key);
382
+ const p = put(0, text);
383
+ return fn('cache_remove', p, text.length) !== 0;
384
+ },
385
+ cacheClear: () => fn('cache_clear') !== 0,
386
+ shapePrepare(vertices, indices, options) {
387
+ const text = encoder.encode(options);
388
+ const [v, i, o] = inputs([bytes(vertices), bytes(indices), text]);
389
+ return fn('shape_prepare', v, bytes(vertices).length / 4, i, bytes(indices).length / 4, o, text.length);
390
+ },
391
+ solidPrepare(recipe, options) {
392
+ const r = encoder.encode(recipe),
393
+ o = encoder.encode(options);
394
+ const [a, b] = inputs([r, o]);
395
+ return fn('solid_prepare', a, r.length, b, o.length);
396
+ },
397
+ modelLoad(value, center, fit) {
398
+ const [p] = data(value);
399
+ return fn('model_load', p, bytes(value).length, +center, fit);
400
+ },
401
+ modelVertices: mesh => copy(fn('model_vertices', mesh), fn('model_vertex_count', mesh) * 44),
402
+ modelIndices: mesh => copy(fn('model_indices', mesh), fn('model_index_count', mesh) * 4),
403
+ modelSize: mesh => {
404
+ const [x, y, z] = dimensions('model_size', mesh, 3, true);
405
+ return [x, y, z];
406
+ },
407
+ // The browser core is single-threaded: the "background" loads settle at once with the same work.
408
+ modelLoadAsync(value, center, fit) {
409
+ return settle(() => engine.modelLoad(value, center, fit));
410
+ },
411
+ textureLoadAsync(value) {
412
+ return settle(() => engine.textureLoad(value));
413
+ },
414
+ fontLoadAsync(value, depth, chars, weight) {
415
+ return settle(() => engine.fontLoad(value, depth, chars, weight));
416
+ },
417
+ audioLoadAsync(value) {
418
+ return settle(() => engine.audioLoad(value));
419
+ },
420
+ // An older core without it uploads through JS instead (see registerCoreMesh).
421
+ renderModel: (mesh, texture, shine) => typeof core.engine_render_model === 'function' && fn('render_model', mesh, texture, shine) !== 0,
422
+ modelTexture: mesh => fn('model_texture', mesh),
423
+ meshAlias: mesh => fn('mesh_alias', mesh),
424
+ meshCreate(vertices, indices) {
425
+ const [v, i] = data(vertices, indices);
426
+ return fn('mesh_create', v, bytes(vertices).length / 44, i, bytes(indices).length / 4);
427
+ },
428
+ pathExtrude(points, counts, depth, evenOdd) {
429
+ if (counts.some(c => !Number.isInteger(c) || c < 0) || counts.reduce((a, b) => a + b, 0) * 8 > bytes(points).length) throw new Error('pathExtrude: invalid contour counts');
430
+ const [p, c] = inputs([bytes(points), new Uint8Array(new Uint32Array(counts).buffer)]);
431
+ const count = fn('path_extrude', p, c, counts.length, depth, +evenOdd);
432
+ const out = zeroed(0, count * 4);
433
+ fn('path_extrude_copy', out, count);
434
+ return copy(out, count * 4);
435
+ },
436
+ geometrySmoothNormals(vertices, indices) {
437
+ const [v, i] = data(vertices, indices);
438
+ fn('geometry_smooth_normals', v, bytes(vertices).length / 44, i, bytes(indices).length / 4);
439
+ bytes(vertices).set(u8().subarray(v, v + vertices.byteLength));
440
+ },
441
+ geometrySolid(recipe) {
442
+ const b = encoder.encode(recipe);
443
+ const p = put(0, b);
444
+ return geometry(fn('geometry_solid', p, b.length));
445
+ },
446
+ geometryOptimize(vertices, indices, ratio, error, lock) {
447
+ const [v, i] = data(vertices, indices);
448
+ return geometry(fn('geometry_optimize', v, bytes(vertices).length / 4, i, bytes(indices).length / 4, ratio, error, +lock));
449
+ },
450
+ geometryFaceOutward(vertices, indices) {
451
+ const [v, i] = data(vertices, indices);
452
+ fn('geometry_face_outward', v, bytes(vertices).length / 44, i, bytes(indices).length / 4);
453
+ bytes(indices).set(u8().subarray(i, i + indices.byteLength));
454
+ },
455
+ geometryPlace(vertices, indices, placement, first, outVertices, vertexOffset, outIndices, indexOffset) {
456
+ const vbytes = bytes(vertices),
457
+ ibytes = bytes(indices);
458
+ if (placement.length < 13 || vertexOffset < 0 || indexOffset < 0 || vertexOffset * 4 + vbytes.length > bytes(outVertices).length || indexOffset * 4 + ibytes.length > bytes(outIndices).length) throw new Error('geometryPlace: invalid output bounds');
459
+ const [v, i, p, ov, oi] = inputs([vbytes, ibytes, new Uint8Array(new Float32Array(placement).buffer), bytes(outVertices), bytes(outIndices)]);
460
+ fn('geometry_place', v, vbytes.length / 44, i, ibytes.length / 4, p, first, ov + vertexOffset * 4, oi + indexOffset * 4);
461
+ bytes(outVertices).set(u8().subarray(ov, ov + outVertices.byteLength));
462
+ bytes(outIndices).set(u8().subarray(oi, oi + outIndices.byteLength));
463
+ },
464
+ textureLoad(value) {
465
+ const [p] = data(value);
466
+ return fn('texture_load', p, bytes(value).length);
467
+ },
468
+ texturePixels(id) {
469
+ const [width, height] = dimensions('texture_size', id, 2, false);
470
+ const pointer = fn('texture_pixels', id);
471
+ if (!pointer) throw new Error('NayanEngine: unknown world, mesh or texture');
472
+ return copy(pointer, width * height * 4);
473
+ },
474
+ textureSize: id => {
475
+ const [width, height] = dimensions('texture_size', id, 2, false);
476
+ return [width, height];
477
+ },
478
+ fontLoad(value, depth, chars, weight) {
479
+ const b = encoder.encode(chars);
480
+ const [p, c] = inputs([bytes(value), b]);
481
+ return fn('font_load', p, bytes(value).length, depth, c, b.length, weight);
482
+ },
483
+ fontGlyphs(id) {
484
+ const count = fn('font_glyph_floats', id);
485
+ const pointer = fn('font_glyphs', id);
486
+ return [...new Float32Array(memory(), pointer, count), fn('font_cap_height', id)];
487
+ },
488
+ loadError: () => string('load_error'),
489
+ generateBuiltinMeshes() {
490
+ fn('generate_builtin_meshes');
491
+ },
492
+ renderWarmUp() {},
493
+ renderMeshVertices: mesh => meshCopy(mesh, 'vertices'),
494
+ renderMeshIndices: mesh => meshCopy(mesh, 'indices'),
495
+ renderMesh(mesh, vertices, indices, texture, shine) {
496
+ const [v, i] = data(vertices, indices);
497
+ fn('render_mesh', mesh, v, bytes(vertices).length / 44, i, bytes(indices).length / 4, texture, shine);
498
+ },
499
+ renderFrame(surface, frame, matrices, colors, regions, ranges, count, viewProj, world) {
500
+ const frameBuffer = buf(frame);
501
+ const capacity = Math.min(buf(matrices).byteLength / MATRIX_BYTES, buf(colors).byteLength / VEC4_BYTES, buf(regions).byteLength / VEC4_BYTES);
502
+ const f = put(0, new Uint8Array(frameBuffer));
503
+ const m = source(1, matrices);
504
+ const c = source(2, colors);
505
+ const r = source(3, regions);
506
+ const buckets = source(4, ranges);
507
+ const out = buf(viewProj);
508
+ const vp = put(5, new Uint8Array(out));
509
+ const result = Number(ex('render_frame')(surface, f, frameBuffer.byteLength / 8, m, c, r, capacity, buckets, ranges.byteLength / 4, count, vp, world >= 0 ? ptr(world) : 0));
510
+ // Rendering may grow memory: u8() re-creates the view if so.
511
+ new Uint8Array(out, 0, 64).set(u8().subarray(vp, vp + 64));
512
+ return result;
513
+ },
514
+ renderOverlay(surface, matrices, colors, regions, ranges, count, width, height) {
515
+ const capacity = Math.min(buf(matrices).byteLength / MATRIX_BYTES, buf(colors).byteLength / VEC4_BYTES, buf(regions).byteLength / VEC4_BYTES);
516
+ const m = source(1, matrices);
517
+ const c = source(2, colors);
518
+ const r = source(3, regions);
519
+ const buckets = source(4, ranges);
520
+ ex('render_overlay')(surface, m, c, r, capacity, buckets, ranges.byteLength / 4, count, width, height);
521
+ },
522
+ renderError: () => string('render_error'),
523
+ renderBackend: () => string('render_backend'),
524
+ drawingCreate: (width, height, ratio) => fn('drawing_create', width, height, ratio),
525
+ drawingDestroy: document => {
526
+ fn('drawing_destroy', document);
527
+ },
528
+ drawingCommand(document, command) {
529
+ const text = encoder.encode(command);
530
+ const input = put(0, text);
531
+ const pointer = fn('drawing_command', document, input, text.length);
532
+ if (!pointer) throw new Error('Drawing command failed');
533
+ try {
534
+ const memory = u8();
535
+ const end = memory.indexOf(0, pointer);
536
+ if (end < 0) throw new Error('Invalid drawing response');
537
+ return decoder.decode(memory.subarray(pointer, end));
538
+ } finally {
539
+ fn('drawing_result_free', pointer);
540
+ }
541
+ },
542
+ audioLoad(value) {
543
+ const [p] = data(value);
544
+ return fn('audio_load_wav', p, bytes(value).length);
545
+ },
546
+ audioPlay(sound, volume, pan, pitch, loop) {
547
+ return fn('audio_play', sound, volume, pan, pitch, +loop);
548
+ },
549
+ audioStop(voice) {
550
+ const f = core.engine_audio_stop;
551
+ f(BigInt(voice));
552
+ },
553
+ audioSetVolume(volume) {
554
+ fn('audio_set_volume', volume);
555
+ },
556
+ audioSetMuted(muted) {
557
+ fn('audio_set_muted', +muted);
558
+ },
559
+ audioIsRunning: () => fn('audio_is_running') !== 0,
560
+ hapticsSupported: () => fn('haptics_supported') !== 0,
561
+ hapticsPlay(taps, throttle) {
562
+ const p = put(0, new Uint8Array(new Float32Array(taps).buffer));
563
+ fn('haptics_play', p, taps.length / 3, +throttle);
564
+ }
565
+ };
566
+ return engine;
567
+ }
568
+ //# sourceMappingURL=BrowserEngine.js.map
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export {};
4
+ //# sourceMappingURL=EngineBridge.js.map
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+
3
+ import NativeModule from "./spec/NativeNayanEngine.js";
4
+
5
+ // The native JSI implementation provides the buffers described by EngineBridge.
6
+ export default NativeModule;
7
+ //# sourceMappingURL=NativeNayanEngine.js.map
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+
3
+ import init, { attach_canvas, can_warm_asset_record, install_asset_storage, read_saved_asset, restore_asset_record } from '../../native/core/build/web/engine_core.js';
4
+ import { IndexedDbCache } from "../assets/IndexedDbCache.web.js";
5
+ import { installAudioOutput } from "../media/output.web.js";
6
+ import { createBrowserEngine } from "./BrowserEngine.js";
7
+ let storage;
8
+ let core;
9
+ let pending;
10
+ let attaching = Promise.resolve();
11
+ export function prepareEngine() {
12
+ return pending ??= (async () => {
13
+ core = await init();
14
+ try {
15
+ storage = await IndexedDbCache.open();
16
+ install_asset_storage(storage);
17
+ await storage.warm(can_warm_asset_record, restore_asset_record);
18
+ } catch {
19
+ /* IndexedDB denial is a cache miss, not a failed engine initialization. */
20
+ }
21
+ const ready = createBrowserEngine(core);
22
+ ready.audioIsRunning = installAudioOutput(core);
23
+ Object.assign(browser, ready);
24
+ })().catch(error => {
25
+ pending = undefined;
26
+ throw error;
27
+ });
28
+ }
29
+ export async function readSavedAsset(key) {
30
+ await prepareEngine();
31
+ const bytes = await read_saved_asset(key);
32
+ return bytes.length ? bytes.buffer : null;
33
+ }
34
+ export function flushAssetStorage() {
35
+ return storage ? storage.flush() : Promise.reject(new Error('IndexedDB unavailable'));
36
+ }
37
+ export function clearedAssetStorage() {
38
+ return storage ? storage.cleared() : Promise.reject(new Error('IndexedDB unavailable'));
39
+ }
40
+ export function attachCanvas(id, canvas) {
41
+ const attach = attaching.catch(() => {}).then(async () => {
42
+ await prepareEngine();
43
+ await attach_canvas(id, canvas);
44
+ });
45
+ attaching = attach;
46
+ return attach;
47
+ }
48
+ export function detachCanvas(id) {
49
+ const detach = core?.engine_render_detach;
50
+ detach?.(id);
51
+ }
52
+
53
+ // SSR-safe: importing binds nothing. Until warm-up every method throws; prepareEngine() then copies
54
+ // the real methods onto this same object, so calls go straight to them (no per-access trap).
55
+ const browser = {};
56
+ const unready = () => {
57
+ throw new Error('Engine: await warmUpEngine() before creating worlds or procedural meshes on the web');
58
+ };
59
+ // createBrowserEngine touches nothing at creation: its keys list every bridge method.
60
+ for (const key of Object.keys(createBrowserEngine({}))) browser[key] = unready;
61
+ export default browser;
62
+ //# sourceMappingURL=NativeNayanEngine.web.js.map
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+
3
+ /** Mobile is initialized by native module registration. */
4
+ export function prepareEngine() {
5
+ return undefined;
6
+ }
7
+ //# sourceMappingURL=prepare.js.map
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+
3
+ import { prepareEngine as prepare } from "./NativeNayanEngine.web.js";
4
+ let requestedPersistence = false;
5
+ export function prepareEngine() {
6
+ if (!requestedPersistence && typeof navigator !== 'undefined') {
7
+ requestedPersistence = true;
8
+ // Browsers own the permission decision. Do not block game startup on a permission prompt.
9
+ try {
10
+ void navigator.storage?.persist?.().catch(() => {});
11
+ } catch {
12
+ /* Storage can be disabled. */
13
+ }
14
+ }
15
+ return prepare();
16
+ }
17
+ //# sourceMappingURL=prepare.web.js.map
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+
3
+ /* eslint-disable @typescript-eslint/no-wrapper-object-types -- Codegen requires Object for JSI buffers; EngineBridge supplies their precise TypeScript types. */
4
+ import { TurboModuleRegistry } from 'react-native';
5
+
6
+ /**
7
+ * Bridge to the Rust core. Use the `World`, `audio` and `haptics` APIs instead of calling this.
8
+ *
9
+ * Worlds are identified by the number `createWorld` returns; entity ids are opaque numbers.
10
+ * Entities are created and changed with one call each: `spawnDesc` / `setDesc` take a
11
+ * Float64Array's ArrayBuffer encoding every option (layout in native/core/include/engine_core.h).
12
+ * Capacity is fixed at creation, so the buffers returned by `get*` alias Rust memory (zero-copy)
13
+ * and stay valid until `destroyWorld`.
14
+ */
15
+
16
+ export default TurboModuleRegistry.get('NativeNayanEngine');
17
+ //# sourceMappingURL=NativeNayanEngine.js.map
@@ -0,0 +1 @@
1
+ {"type":"module"}